Browse Source

Merge remote-tracking branch 'origin/feature/edge' into feature/3.0-edge

pull/3811/head
Volodymyr Babak 6 years ago
parent
commit
95786f858c
  1. 1
      .gitignore
  2. 1
      application/src/main/data/json/demo/rule_chains/root_rule_chain.json
  3. 68
      application/src/main/data/upgrade/2.6.0/schema_update.cql
  4. 18
      application/src/main/data/upgrade/2.6.0/schema_update.sql
  5. 32
      application/src/main/data/upgrade/2.6.0/schema_update_ttl.sql
  6. 21
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  7. 17
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  8. 2
      application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
  9. 3
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  10. 22
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  11. 6
      application/src/main/java/org/thingsboard/server/controller/AuthController.java
  12. 88
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  13. 5
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  14. 24
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  15. 20
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  16. 63
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  17. 71
      application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java
  18. 4
      application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
  19. 21
      application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
  20. 19
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  21. 5
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  22. 12
      application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java
  23. 11
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  24. 13
      application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java
  25. 12
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  26. 353
      application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
  27. 88
      application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
  28. 117
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java
  29. 1280
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  30. 26
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.java
  31. 38
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java
  32. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java
  33. 13
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java
  34. 72
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java
  35. 16
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java
  36. 97
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java
  37. 69
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java
  38. 40
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java
  39. 17
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java
  40. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java
  41. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java
  42. 37
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java
  43. 61
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java
  44. 54
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java
  45. 549
      application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java
  46. 20
      application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java
  47. 98
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AlarmProcessor.java
  48. 113
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseProcessor.java
  49. 252
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java
  50. 104
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationProcessor.java
  51. 244
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryProcessor.java
  52. 2
      application/src/main/java/org/thingsboard/server/service/install/AbstractSqlTsDatabaseUpgradeService.java
  53. 2
      application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java
  54. 1
      application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java
  55. 9
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  56. 20
      application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java
  57. 2
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  58. 10
      application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java
  59. 5
      application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java
  60. 33
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  61. 11
      application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java
  62. 3
      application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java
  63. 9
      application/src/main/java/org/thingsboard/server/service/rpc/TbRuleEngineDeviceRpcService.java
  64. 2
      application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
  65. 56
      application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java
  66. 12
      application/src/main/resources/thingsboard.yml
  67. 16
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  68. 27
      application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
  69. 57
      application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java
  70. 37
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
  71. 73
      application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
  72. 125
      application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java
  73. 36
      application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
  74. 23
      application/src/test/java/org/thingsboard/server/controller/nosql/EdgeEventControllerNoSqlTest.java
  75. 23
      application/src/test/java/org/thingsboard/server/controller/sql/EdgeEventControllerSqlTest.java
  76. 1090
      application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java
  77. 47
      application/src/test/java/org/thingsboard/server/edge/EdgeNoSqlTestSuite.java
  78. 41
      application/src/test/java/org/thingsboard/server/edge/EdgeSqlTestSuite.java
  79. 268
      application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
  80. 23
      application/src/test/java/org/thingsboard/server/edge/nosql/EdgeNoSqlTest.java
  81. 23
      application/src/test/java/org/thingsboard/server/edge/sql/EdgeSqlTest.java
  82. 3
      application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java
  83. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
  84. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  85. 6
      common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java
  86. 7
      common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java
  87. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
  88. 5
      common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
  89. 7
      common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
  90. 76
      common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java
  91. 50
      common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java
  92. 4
      common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java
  93. 12
      common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java
  94. 8
      common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java
  95. 16
      common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java
  96. 3
      common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java
  97. 4
      common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
  98. 109
      common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java
  99. 11
      common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java
  100. 220
      common/edge-api/src/main/proto/edge.proto

1
.gitignore

@ -33,3 +33,4 @@ pom.xml.versionsBackup
**/.env
.instance_id
rebuild-docker.sh
.run/

1
application/src/main/data/json/demo/rule_chains/root_rule_chain.json

@ -2,6 +2,7 @@
"ruleChain": {
"additionalInfo": null,
"name": "Root Rule Chain",
"type": "CORE",
"firstRuleNodeId": null,
"root": true,
"debugMode": false,

68
application/src/main/data/upgrade/2.6.0/schema_update.cql

@ -14,15 +14,54 @@
-- limitations under the License.
--
DROP MATERIALIZED VIEW IF EXISTS thingsboard.rule_chain_by_tenant_and_search_text;
DROP TABLE IF EXISTS thingsboard.rule_chain;
CREATE TABLE IF NOT EXISTS thingsboard.rule_chain (
id uuid,
tenant_id uuid,
name text,
type text,
search_text text,
first_rule_node_id uuid,
root boolean,
debug_mode boolean,
configuration text,
additional_info text,
PRIMARY KEY (id, tenant_id, type)
);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS
SELECT *
from thingsboard.rule_chain
WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL AND type IS NOT NULL
PRIMARY KEY ( tenant_id, search_text, id, type )
WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_by_type_and_search_text AS
SELECT *
from thingsboard.rule_chain
WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL AND type IS NOT NULL
PRIMARY KEY ( tenant_id, type, search_text, id )
WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC );
CREATE TABLE IF NOT EXISTS thingsboard.edge (
id timeuuid,
tenant_id timeuuid,
customer_id timeuuid,
root_rule_chain_id timeuuid,
type text,
name text,
label text,
search_text text,
routing_key text,
secret text,
edge_license_key text,
cloud_endpoint text,
configuration text,
additional_info text,
PRIMARY KEY (id, tenant_id)
PRIMARY KEY (id, tenant_id, customer_id, type)
);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_name AS
@ -32,6 +71,13 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_name AS
PRIMARY KEY ( tenant_id, name, id, customer_id, type)
WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_routing_key AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND routing_key IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( routing_key, tenant_id, id, customer_id, type)
WITH CLUSTERING ORDER BY ( tenant_id DESC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_search_text AS
SELECT *
from thingsboard.edge
@ -60,4 +106,22 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_customer_by_type_and_
PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
-- VOBA ADD changes for the MATERIALIZED view for DEVICE ASSET ENTITY_VIEW RULE_CHAIN
CREATE TABLE IF NOT EXISTS thingsboard.edge_event (
id timeuuid,
tenant_id timeuuid,
edge_id timeuuid,
edge_event_type text,
edge_event_action text,
edge_event_uid text,
entity_id timeuuid,
body text,
PRIMARY KEY ((tenant_id, edge_id), edge_event_type, edge_event_uid)
);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_event_by_id AS
SELECT *
FROM thingsboard.edge_event
WHERE tenant_id IS NOT NULL AND edge_id IS NOT NULL AND edge_event_type IS NOT NULL
AND id IS NOT NULL AND edge_event_uid IS NOT NULL
PRIMARY KEY ((tenant_id, edge_id), id, edge_event_type, edge_event_uid)
WITH CLUSTERING ORDER BY (id ASC);

18
application/src/main/data/upgrade/2.6.0/schema_update.sql

@ -25,6 +25,22 @@ CREATE TABLE IF NOT EXISTS edge (
label varchar(255),
routing_key varchar(255),
secret varchar(255),
edge_license_key varchar(30),
cloud_endpoint varchar(255),
search_text varchar(255),
tenant_id varchar(31)
tenant_id varchar(31),
CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name),
CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key)
);
CREATE TABLE IF NOT EXISTS edge_event (
id varchar(31) NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY,
edge_id varchar(31),
edge_event_type varchar(255),
edge_event_uid varchar(255),
entity_id varchar(31),
edge_event_action varchar(255),
body varchar(10000000),
tenant_id varchar(31),
ts bigint NOT NULL
);

32
application/src/main/data/upgrade/2.6.0/schema_update_ttl.sql

@ -0,0 +1,32 @@
--
-- Copyright © 2016-2020 The Thingsboard Authors
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
CREATE OR REPLACE PROCEDURE cleanup_edge_events_by_ttl(IN ttl bigint, INOUT deleted bigint)
LANGUAGE plpgsql AS
$$
DECLARE
ttl_ts bigint;
ttl_deleted_count bigint DEFAULT 0;
BEGIN
IF ttl > 0 THEN
ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
EXECUTE format(
'WITH deleted AS (DELETE FROM edge_event WHERE ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', ttl_ts) into ttl_deleted_count;
END IF;
RAISE NOTICE 'Edge events removed by ttl: %', ttl_deleted_count;
deleted := ttl_deleted_count;
END
$$;

21
application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java

@ -32,7 +32,6 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.common.data.DataConstants;
@ -45,6 +44,7 @@ import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.msg.tools.TbRateLimits;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.audit.AuditLogService;
@ -68,7 +68,7 @@ import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
@ -196,10 +196,6 @@ public class ActorSystemContext {
@Getter
private EntityViewService entityViewService;
@Autowired
@Getter
private EdgeService edgeService;
@Autowired
@Getter
private TelemetrySubscriptionService tsSubService;
@ -274,9 +270,16 @@ public class ActorSystemContext {
private TbCoreDeviceRpcService tbCoreDeviceRpcService;
@Lazy
@Autowired
@Getter
private EdgeEventService edgeEventService;
@Autowired(required = false)
@Getter private EdgeService edgeService;
@Lazy
@Autowired(required = false)
@Getter private EdgeEventService edgeEventService;
@Lazy
@Autowired(required = false)
@Getter private EdgeRpcService edgeRpcService;
@Value("${actors.session.max_concurrent_sessions_per_device:1}")
@Getter

17
application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java

@ -32,10 +32,13 @@ import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.msg.MsgType;
@ -48,6 +51,7 @@ import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import java.util.List;
@ -211,7 +215,18 @@ public class TenantActor extends RuleChainManagerActor {
}
private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
if (isRuleEngineForCurrentTenant) {
if (msg.getEntityId().getEntityType() == EntityType.EDGE) {
EdgeId edgeId = new EdgeId(msg.getEntityId().getId());
EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService();
if (msg.getEvent() == ComponentLifecycleEvent.DELETED) {
edgeRpcService.deleteEdge(edgeId);
} else {
Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId);
if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) {
edgeRpcService.updateEdge(edge);
}
}
} else if (isRuleEngineForCurrentTenant) {
TbActorRef target = getEntityActorRef(msg.getEntityId());
if (target != null) {
if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {

2
application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java

@ -69,7 +69,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public";
public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**"};
protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**", "/api/license/**"};
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";

3
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -94,7 +94,8 @@ public class AlarmController extends BaseController {
getCurrentUser().getCustomerId(),
alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
sendNotificationMsgToEdgeService(getTenantId(), savedAlarm.getId(), alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
sendNotificationMsgToEdgeService(getTenantId(), savedAlarm.getId(),
alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
return savedAlarm;
} catch (Exception e) {

22
application/src/main/java/org/thingsboard/server/controller/AssetController.java

@ -97,17 +97,18 @@ public class AssetController extends BaseController {
try {
asset.setTenantId(getCurrentUser().getTenantId());
checkEntity(asset.getId(), asset, Resource.ASSET);
checkEntity(asset.getId(), asset, Resource.ASSET);
Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
sendNotificationMsgToEdgeService(savedAsset.getTenantId(), null,
savedAsset.getId(), EdgeEventType.ASSET, asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
logEntityAction(savedAsset.getId(), savedAsset,
savedAsset.getCustomerId(),
asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
if (asset.getId() != null) {
sendNotificationMsgToEdgeService(savedAsset.getTenantId(), savedAsset.getId(), ActionType.UPDATED);
}
return savedAsset;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ASSET), asset,
@ -130,7 +131,7 @@ public class AssetController extends BaseController {
asset.getCustomerId(),
ActionType.DELETED, null, strAssetId);
sendNotificationMsgToEdgeService(getTenantId(), null, assetId, EdgeEventType.ASSET, ActionType.DELETED);
sendNotificationMsgToEdgeService(getTenantId(), assetId, ActionType.DELETED);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ASSET),
null,
@ -160,6 +161,9 @@ public class AssetController extends BaseController {
savedAsset.getCustomerId(),
ActionType.ASSIGNED_TO_CUSTOMER, null, strAssetId, strCustomerId, customer.getName());
sendNotificationMsgToEdgeService(savedAsset.getTenantId(), savedAsset.getId(),
customerId, ActionType.ASSIGNED_TO_CUSTOMER);
return savedAsset;
} catch (Exception e) {
@ -191,6 +195,9 @@ public class AssetController extends BaseController {
asset.getCustomerId(),
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strAssetId, customer.getId().toString(), customer.getName());
sendNotificationMsgToEdgeService(savedAsset.getTenantId(), savedAsset.getId(),
customer.getId(), ActionType.UNASSIGNED_FROM_CUSTOMER);
return savedAsset;
} catch (Exception e) {
@ -425,7 +432,7 @@ public class AssetController extends BaseController {
savedAsset.getCustomerId(),
ActionType.ASSIGNED_TO_EDGE, null, strAssetId, strEdgeId, edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(), EdgeEventType.ASSET, ActionType.ASSIGNED_TO_EDGE);
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(), ActionType.ASSIGNED_TO_EDGE);
return savedAsset;
} catch (Exception e) {
@ -458,8 +465,7 @@ public class AssetController extends BaseController {
asset.getCustomerId(),
ActionType.UNASSIGNED_FROM_EDGE, null, strAssetId, edge.getId().toString(), edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(),
EdgeEventType.ASSET, ActionType.UNASSIGNED_FROM_EDGE);
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(), ActionType.UNASSIGNED_FROM_EDGE);
return savedAsset;
} catch (Exception e) {

6
application/src/main/java/org/thingsboard/server/controller/AuthController.java

@ -35,6 +35,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
@ -124,6 +125,9 @@ public class AuthController extends BaseController {
}
userCredentials.setPassword(passwordEncoder.encode(newPassword));
userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials);
sendNotificationMsgToEdgeService(getTenantId(), userCredentials.getUserId(), ActionType.CREDENTIALS_UPDATED);
} catch (Exception e) {
throw handleException(e);
}
@ -231,6 +235,8 @@ public class AuthController extends BaseController {
}
}
sendNotificationMsgToEdgeService(user.getTenantId(), user.getId(), ActionType.CREDENTIALS_UPDATED);
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);

88
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
@ -94,7 +95,6 @@ import org.thingsboard.server.dao.device.ClaimDevicesService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -114,6 +114,8 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.edge.rpc.EdgeGrpcService;
import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.security.model.SecurityUser;
@ -205,9 +207,6 @@ public abstract class BaseController {
@Autowired
protected EntityViewService entityViewService;
@Autowired
protected EdgeService edgeService;
@Autowired
protected TelemetrySubscriptionService tsSubService;
@ -226,16 +225,25 @@ public abstract class BaseController {
@Autowired
protected TbDeviceProfileCache deviceProfileCache;
@Autowired
@Autowired(required = false)
protected EdgeService edgeService;
@Autowired(required = false)
protected EdgeNotificationService edgeNotificationService;
@Autowired
protected EdgeEventService edgeEventService;
@Autowired(required = false)
protected SyncEdgeService syncEdgeService;
@Autowired(required = false)
protected EdgeGrpcService edgeGrpcService;
@Value("${server.log_controller_error_stack_trace}")
@Getter
private boolean logControllerErrorStackTrace;
@Value("${edges.rpc.enabled}")
@Getter
private boolean edgesSupportEnabled;
@ExceptionHandler(ThingsboardException.class)
public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
@ -774,8 +782,7 @@ public abstract class BaseController {
String strTenantName = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("assignedToTenantId", strTenantId);
metaData.putValue("assignedToTenantName", strTenantName);
}
if (actionType == ActionType.ASSIGNED_TO_EDGE) {
} else if (actionType == ActionType.ASSIGNED_TO_EDGE) {
String strEdgeId = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("assignedEdgeId", strEdgeId);
} else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) {
@ -852,31 +859,66 @@ public abstract class BaseController {
}
return null;
}
protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityRelation relation, ActionType edgeEventAction) {
protected void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, CustomerId customerId, ActionType action) {
if (!edgesSupportEnabled) {
return;
}
try {
sendNotificationMsgToEdgeService(tenantId, null, null, json.writeValueAsString(relation), EdgeEventType.RELATION, edgeEventAction);
sendNotificationMsgToEdgeService(tenantId, edgeId, null, json.writeValueAsString(customerId), EdgeEventType.EDGE, action);
} catch (Exception e) {
log.warn("Failed to push relation to core: {}", relation, e);
log.warn("Failed to push assign/unassign to/from customer to core: {}", customerId, e);
}
}
protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, ActionType edgeEventAction) {
EdgeEventType edgeEventType = edgeEventService.getEdgeEventTypeByEntityType(entityId.getEntityType());
if (edgeEventType != null) {
sendNotificationMsgToEdgeService(tenantId, null, entityId, null, edgeEventType, edgeEventAction);
protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, CustomerId customerId, ActionType action) {
if (!edgesSupportEnabled) {
return;
}
EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType());
try {
if (type != null) {
sendNotificationMsgToEdgeService(tenantId, null, entityId, json.writeValueAsString(customerId), type, action);
}
} catch (Exception e) {
log.warn("Failed to push assign/unassign to/from customer to core: {}", customerId, e);
}
}
protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityRelation relation, ActionType action) {
if (!edgesSupportEnabled) {
return;
}
try {
if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
!relation.getTo().getEntityType().equals(EntityType.EDGE)) {
sendNotificationMsgToEdgeService(tenantId, null, null, json.writeValueAsString(relation), EdgeEventType.RELATION, action);
}
} catch (Exception e) {
log.warn("Failed to push relation to core: {}", relation, e);
}
}
protected void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventType edgeEventType, ActionType edgeEventAction) {
sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, edgeEventType, edgeEventAction);
protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, ActionType action) {
sendNotificationMsgToEdgeService(tenantId, null, entityId, action);
}
protected void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, ActionType action) {
if (!edgesSupportEnabled) {
return;
}
EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType());
if (type != null) {
sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, type, action);
}
}
private void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String entityBody, EdgeEventType edgeEventType, ActionType edgeEventAction) {
private void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, ActionType action) {
TransportProtos.EdgeNotificationMsgProto.Builder builder = TransportProtos.EdgeNotificationMsgProto.newBuilder();
builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
builder.setEdgeEventType(edgeEventType.name());
builder.setEdgeEventAction(edgeEventAction.name());
builder.setType(type.name());
builder.setAction(action.name());
if (entityId != null) {
builder.setEntityIdMSB(entityId.getId().getMostSignificantBits());
builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits());
@ -886,8 +928,8 @@ public abstract class BaseController {
builder.setEdgeIdMSB(edgeId.getId().getMostSignificantBits());
builder.setEdgeIdLSB(edgeId.getId().getLeastSignificantBits());
}
if (entityBody != null) {
builder.setEntityBody(entityBody);
if (body != null) {
builder.setBody(body);
}
TransportProtos.EdgeNotificationMsgProto msg = builder.build();
tbClusterService.pushMsgToCore(tenantId, entityId != null ? entityId : tenantId,

5
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -108,6 +108,10 @@ public class CustomerController extends BaseController {
savedCustomer.getId(),
customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
if (customer.getId() != null) {
sendNotificationMsgToEdgeService(savedCustomer.getTenantId(), savedCustomer.getId(),ActionType.UPDATED);
}
return savedCustomer;
} catch (Exception e) {
@ -132,6 +136,7 @@ public class CustomerController extends BaseController {
customer.getId(),
ActionType.DELETED, null, strCustomerId);
sendNotificationMsgToEdgeService(getTenantId(), customerId, ActionType.DELETED);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.CUSTOMER),

24
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -31,10 +31,8 @@ import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
@ -116,8 +114,9 @@ public class DashboardController extends BaseController {
null,
dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), null, savedDashboard.getId(),
EdgeEventType.DASHBOARD, savedDashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
if (dashboard.getId() != null) {
sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), ActionType.UPDATED);
}
return savedDashboard;
} catch (Exception e) {
@ -142,7 +141,7 @@ public class DashboardController extends BaseController {
null,
ActionType.DELETED, null, strDashboardId);
sendNotificationMsgToEdgeService(getTenantId(), null, dashboardId, EdgeEventType.DASHBOARD, ActionType.DELETED);
sendNotificationMsgToEdgeService(getTenantId(), dashboardId, ActionType.DELETED);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD),
@ -174,6 +173,7 @@ public class DashboardController extends BaseController {
customerId,
ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName());
sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.ASSIGNED_TO_CUSTOMER);
return savedDashboard;
} catch (Exception e) {
@ -205,6 +205,8 @@ public class DashboardController extends BaseController {
customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName());
sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.UNASSIGNED_FROM_CUSTOMER);
return savedDashboard;
} catch (Exception e) {
@ -260,6 +262,7 @@ public class DashboardController extends BaseController {
logEntityAction(dashboardId, savedDashboard,
customerId,
ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.ASSIGNED_TO_CUSTOMER);
}
for (CustomerId customerId : removedCustomerIds) {
ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
@ -267,7 +270,7 @@ public class DashboardController extends BaseController {
logEntityAction(dashboardId, dashboard,
customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.UNASSIGNED_FROM_CUSTOMER);
}
return savedDashboard;
}
@ -311,6 +314,7 @@ public class DashboardController extends BaseController {
logEntityAction(dashboardId, savedDashboard,
customerId,
ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.ASSIGNED_TO_CUSTOMER);
}
return savedDashboard;
}
@ -354,7 +358,7 @@ public class DashboardController extends BaseController {
logEntityAction(dashboardId, dashboard,
customerId,
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
sendNotificationMsgToEdgeService(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, ActionType.UNASSIGNED_FROM_CUSTOMER);
}
return savedDashboard;
}
@ -501,8 +505,7 @@ public class DashboardController extends BaseController {
null,
ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, strEdgeId, edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(),
EdgeEventType.DASHBOARD, ActionType.ASSIGNED_TO_EDGE);
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(), ActionType.ASSIGNED_TO_EDGE);
return savedDashboard;
} catch (Exception e) {
@ -534,8 +537,7 @@ public class DashboardController extends BaseController {
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edge.getId().toString(), edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(),
EdgeEventType.DASHBOARD, ActionType.UNASSIGNED_FROM_EDGE);
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(), ActionType.UNASSIGNED_FROM_EDGE);
return savedDashboard;
} catch (Exception e) {

20
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -44,7 +44,6 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
@ -126,8 +125,9 @@ public class DeviceController extends BaseController {
tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(),
savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null);
sendNotificationMsgToEdgeService(savedDevice.getTenantId(), null, savedDevice.getId(),
EdgeEventType.DEVICE, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
if (device.getId() != null) {
sendNotificationMsgToEdgeService(savedDevice.getTenantId(), savedDevice.getId(), ActionType.UPDATED);
}
logEntityAction(savedDevice.getId(), savedDevice,
savedDevice.getCustomerId(),
@ -160,7 +160,7 @@ public class DeviceController extends BaseController {
device.getCustomerId(),
ActionType.DELETED, null, strDeviceId);
sendNotificationMsgToEdgeService(getTenantId(), null, deviceId, EdgeEventType.DEVICE, ActionType.DELETED);
sendNotificationMsgToEdgeService(getTenantId(), deviceId, ActionType.DELETED);
deviceStateService.onDeviceDeleted(device);
} catch (Exception e) {
@ -192,6 +192,9 @@ public class DeviceController extends BaseController {
savedDevice.getCustomerId(),
ActionType.ASSIGNED_TO_CUSTOMER, null, strDeviceId, strCustomerId, customer.getName());
sendNotificationMsgToEdgeService(savedDevice.getTenantId(), savedDevice.getId(),
customerId, ActionType.ASSIGNED_TO_CUSTOMER);
return savedDevice;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), null,
@ -220,6 +223,9 @@ public class DeviceController extends BaseController {
device.getCustomerId(),
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDeviceId, customer.getId().toString(), customer.getName());
sendNotificationMsgToEdgeService(savedDevice.getTenantId(), savedDevice.getId(),
customer.getId(), ActionType.UNASSIGNED_FROM_CUSTOMER);
return savedDevice;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), null,
@ -285,7 +291,7 @@ public class DeviceController extends BaseController {
tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId()), null);
sendNotificationMsgToEdgeService(getTenantId(), null, device.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_UPDATED);
sendNotificationMsgToEdgeService(getTenantId(), device.getId(), ActionType.CREDENTIALS_UPDATED);
logEntityAction(device.getId(), device,
device.getCustomerId(),
@ -646,7 +652,7 @@ public class DeviceController extends BaseController {
savedDevice.getCustomerId(),
ActionType.ASSIGNED_TO_EDGE, null, strDeviceId, strEdgeId, edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), EdgeEventType.DEVICE, ActionType.ASSIGNED_TO_EDGE);
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), ActionType.ASSIGNED_TO_EDGE);
return savedDevice;
} catch (Exception e) {
@ -677,7 +683,7 @@ public class DeviceController extends BaseController {
device.getCustomerId(),
ActionType.UNASSIGNED_FROM_EDGE, null, strDeviceId, edge.getId().toString(), edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), EdgeEventType.DEVICE, ActionType.UNASSIGNED_FROM_EDGE);
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), ActionType.UNASSIGNED_FROM_EDGE);
return savedDevice;
} catch (Exception e) {

63
application/src/main/java/org/thingsboard/server/controller/EdgeController.java

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.controller;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
@ -34,6 +33,7 @@ import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeInfo;
import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
@ -41,10 +41,13 @@ import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.EdgeGrpcSession;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@ -54,6 +57,7 @@ import java.util.List;
import java.util.stream.Collectors;
@RestController
@TbCoreComponent
@RequestMapping("/api")
public class EdgeController extends BaseController {
@ -102,6 +106,9 @@ public class EdgeController extends BaseController {
edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId());
}
tbClusterService.onEntityStateChange(savedEdge.getTenantId(), savedEdge.getId(),
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
logEntityAction(savedEdge.getId(), savedEdge, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
return savedEdge;
} catch (Exception e) {
@ -121,6 +128,9 @@ public class EdgeController extends BaseController {
Edge edge = checkEdgeId(edgeId, Operation.DELETE);
edgeService.deleteEdge(getTenantId(), edgeId);
tbClusterService.onEntityStateChange(getTenantId(), edgeId,
ComponentLifecycleEvent.DELETED);
logEntityAction(edgeId, edge,
null,
ActionType.DELETED, null, strEdgeId);
@ -169,10 +179,16 @@ public class EdgeController extends BaseController {
Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, customerId));
tbClusterService.onEntityStateChange(getTenantId(), edgeId,
ComponentLifecycleEvent.UPDATED);
logEntityAction(edgeId, savedEdge,
savedEdge.getCustomerId(),
ActionType.ASSIGNED_TO_CUSTOMER, null, strEdgeId, strCustomerId, customer.getName());
sendNotificationMsgToEdgeService(savedEdge.getTenantId(), savedEdge.getId(),
customerId, ActionType.ASSIGNED_TO_CUSTOMER);
return savedEdge;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.EDGE), null,
@ -197,10 +213,16 @@ public class EdgeController extends BaseController {
Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(getCurrentUser().getTenantId(), edgeId));
tbClusterService.onEntityStateChange(getTenantId(), edgeId,
ComponentLifecycleEvent.UPDATED);
logEntityAction(edgeId, edge,
edge.getCustomerId(),
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEdgeId, customer.getId().toString(), customer.getName());
sendNotificationMsgToEdgeService(savedEdge.getTenantId(), savedEdge.getId(),
customer.getId(), ActionType.UNASSIGNED_FROM_CUSTOMER);
return savedEdge;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.EDGE), null,
@ -310,6 +332,8 @@ public class EdgeController extends BaseController {
Edge updatedEdge = edgeNotificationService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId);
tbClusterService.onEntityStateChange(updatedEdge.getTenantId(), updatedEdge.getId(), ComponentLifecycleEvent.UPDATED);
logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null);
return updatedEdge;
@ -413,4 +437,41 @@ public class EdgeController extends BaseController {
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/sync", method = RequestMethod.POST)
public void syncEdge(@RequestBody EdgeId edgeId) throws ThingsboardException {
try {
edgeId = checkNotNull(edgeId);
if (isEdgesSupportEnabled()) {
EdgeGrpcSession session = edgeGrpcService.getEdgeGrpcSessionById(edgeId);
Edge edge = session.getEdge();
syncEdgeService.sync(edge);
} else {
throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL);
}
} catch (Exception e) {
throw handleException(e);
}
}
@RequestMapping(value = "/license/checkInstance", method = RequestMethod.POST)
@ResponseBody
public Object checkInstance(@RequestBody Object request) throws ThingsboardException {
try {
return edgeService.checkInstance(request);
} catch (Exception e) {
throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION);
}
}
@RequestMapping(value = "/license/activateInstance", params = {"licenseSecret", "releaseDate"}, method = RequestMethod.POST)
@ResponseBody
public Object activateInstance(@RequestParam String licenseSecret,
@RequestParam String releaseDate) throws ThingsboardException {
try {
return edgeService.activateInstance(licenseSecret, releaseDate);
} catch (Exception e) {
throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION);
}
}
}

71
application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java

@ -0,0 +1,71 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
@Slf4j
@RestController
@TbCoreComponent
@RequestMapping("/api")
public class EdgeEventController extends BaseController {
@Autowired
private EdgeEventService edgeEventService;
public static final String EDGE_ID = "edgeId";
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edge/{edgeId}/events", method = RequestMethod.GET)
@ResponseBody
public PageData<EdgeEvent> getEdgeEvents(
@PathVariable(EDGE_ID) String strEdgeId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false));
} catch (Exception e) {
throw handleException(e);
}
}
}

4
application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java

@ -24,9 +24,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.EntityId;
@ -132,8 +130,6 @@ public class EntityRelationController extends BaseController {
try {
relationService.deleteEntityRelations(getTenantId(), entityId);
logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, null);
sendNotificationMsgToEdgeService(getTenantId(), entityId, ActionType.RELATIONS_DELETED);
} catch (Exception e) {
logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, e);
throw handleException(e);

21
application/src/main/java/org/thingsboard/server/controller/EntityViewController.java

@ -161,8 +161,10 @@ public class EntityViewController extends BaseController {
logEntityAction(savedEntityView.getId(), savedEntityView, null,
entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
sendNotificationMsgToEdgeService(getTenantId(), null, savedEntityView.getId(),
EdgeEventType.ENTITY_VIEW, entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
if (entityView.getId() != null) {
sendNotificationMsgToEdgeService(savedEntityView.getTenantId(), savedEntityView.getId(), ActionType.UPDATED);
}
return savedEntityView;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null,
@ -362,7 +364,7 @@ public class EntityViewController extends BaseController {
logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
ActionType.DELETED, null, strEntityViewId);
sendNotificationMsgToEdgeService(getTenantId(), null, entityViewId, EdgeEventType.ENTITY_VIEW, ActionType.DELETED);
sendNotificationMsgToEdgeService(getTenantId(), entityViewId, ActionType.DELETED);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ENTITY_VIEW),
null,
@ -403,6 +405,10 @@ public class EntityViewController extends BaseController {
logEntityAction(entityViewId, savedEntityView,
savedEntityView.getCustomerId(),
ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName());
sendNotificationMsgToEdgeService(savedEntityView.getTenantId(), savedEntityView.getId(),
customerId, ActionType.ASSIGNED_TO_CUSTOMER);
return savedEntityView;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
@ -429,6 +435,9 @@ public class EntityViewController extends BaseController {
entityView.getCustomerId(),
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName());
sendNotificationMsgToEdgeService(savedEntityView.getTenantId(), savedEntityView.getId(),
customer.getId(), ActionType.UNASSIGNED_FROM_CUSTOMER);
return savedEntityView;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
@ -620,8 +629,7 @@ public class EntityViewController extends BaseController {
savedEntityView.getCustomerId(),
ActionType.ASSIGNED_TO_EDGE, null, strEntityViewId, strEdgeId, edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(),
EdgeEventType.ENTITY_VIEW, ActionType.ASSIGNED_TO_EDGE);
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(), ActionType.ASSIGNED_TO_EDGE);
return savedEntityView;
} catch (Exception e) {
@ -651,8 +659,7 @@ public class EntityViewController extends BaseController {
entityView.getCustomerId(),
ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, edge.getId().toString(), edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(),
EdgeEventType.ENTITY_VIEW, ActionType.UNASSIGNED_FROM_EDGE);
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(), ActionType.UNASSIGNED_FROM_EDGE);
return savedEntityView;
} catch (Exception e) {

19
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -152,9 +152,9 @@ public class RuleChainController extends BaseController {
created ? ActionType.ADDED : ActionType.UPDATED, null);
if (RuleChainType.EDGE.equals(savedRuleChain.getType())) {
sendNotificationMsgToEdgeService(savedRuleChain.getTenantId(), null,
savedRuleChain.getId(), EdgeEventType.RULE_CHAIN,
savedRuleChain.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
if (!created) {
sendNotificationMsgToEdgeService(savedRuleChain.getTenantId(), savedRuleChain.getId(), ActionType.UPDATED);
}
}
return savedRuleChain;
@ -254,9 +254,7 @@ public class RuleChainController extends BaseController {
if (RuleChainType.EDGE.equals(ruleChain.getType())) {
sendNotificationMsgToEdgeService(ruleChain.getTenantId(),
null,
ruleChain.getId(), EdgeEventType.RULE_CHAIN,
ActionType.UPDATED);
ruleChain.getId(), ActionType.UPDATED);
}
return savedRuleChainMetaData;
@ -322,8 +320,7 @@ public class RuleChainController extends BaseController {
ActionType.DELETED, null, strRuleChainId);
if (RuleChainType.EDGE.equals(ruleChain.getType())) {
sendNotificationMsgToEdgeService(ruleChain.getTenantId(), null,
ruleChain.getId(), EdgeEventType.RULE_CHAIN, ActionType.DELETED);
sendNotificationMsgToEdgeService(ruleChain.getTenantId(), ruleChain.getId(), ActionType.DELETED);
}
} catch (Exception e) {
@ -485,8 +482,7 @@ public class RuleChainController extends BaseController {
null,
ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, strEdgeId, edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedRuleChain.getId(),
EdgeEventType.RULE_CHAIN, ActionType.ASSIGNED_TO_EDGE);
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedRuleChain.getId(), ActionType.ASSIGNED_TO_EDGE);
return savedRuleChain;
} catch (Exception e) {
@ -518,8 +514,7 @@ public class RuleChainController extends BaseController {
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edge.getId().toString(), edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedRuleChain.getId(),
EdgeEventType.RULE_CHAIN, ActionType.UNASSIGNED_FROM_EDGE);
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedRuleChain.getId(), ActionType.UNASSIGNED_FROM_EDGE);
return savedRuleChain;
} catch (Exception e) {

5
application/src/main/java/org/thingsboard/server/controller/UserController.java

@ -162,6 +162,9 @@ public class UserController extends BaseController {
savedUser.getCustomerId(),
user.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
sendNotificationMsgToEdgeService(getTenantId(), savedUser.getId(),
user.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
return savedUser;
} catch (Exception e) {
@ -237,6 +240,8 @@ public class UserController extends BaseController {
user.getCustomerId(),
ActionType.DELETED, null, strUserId);
sendNotificationMsgToEdgeService(getTenantId(), user.getId(), ActionType.DELETED);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.USER),
null,

12
application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@ -25,6 +26,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
@ -37,6 +39,7 @@ import org.thingsboard.server.service.security.permission.Resource;
import java.util.List;
@Slf4j
@RestController
@TbCoreComponent
@RequestMapping("/api")
@ -67,8 +70,12 @@ public class WidgetTypeController extends BaseController {
}
checkEntity(widgetType.getId(), widgetType, Resource.WIDGET_TYPE);
WidgetType savedWidgetType = widgetTypeService.saveWidgetType(widgetType);
return checkNotNull(widgetTypeService.saveWidgetType(widgetType));
sendNotificationMsgToEdgeService(getTenantId(), savedWidgetType.getId(),
widgetType.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
return checkNotNull(savedWidgetType);
} catch (Exception e) {
throw handleException(e);
}
@ -83,6 +90,9 @@ public class WidgetTypeController extends BaseController {
WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
checkWidgetTypeId(widgetTypeId, Operation.DELETE);
widgetTypeService.deleteWidgetType(getCurrentUser().getTenantId(), widgetTypeId);
sendNotificationMsgToEdgeService(getTenantId(), widgetTypeId, ActionType.DELETED);
} catch (Exception e) {
throw handleException(e);
}

11
application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java

@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
@ -68,7 +69,12 @@ public class WidgetsBundleController extends BaseController {
}
checkEntity(widgetsBundle.getId(), widgetsBundle, Resource.WIDGETS_BUNDLE);
return checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle));
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
sendNotificationMsgToEdgeService(getTenantId(), savedWidgetsBundle.getId(),
widgetsBundle.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
return checkNotNull(savedWidgetsBundle);
} catch (Exception e) {
throw handleException(e);
}
@ -83,6 +89,9 @@ public class WidgetsBundleController extends BaseController {
WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
checkWidgetsBundleId(widgetsBundleId, Operation.DELETE);
widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId);
sendNotificationMsgToEdgeService(getTenantId(), widgetsBundleId, ActionType.DELETED);
} catch (Exception e) {
throw handleException(e);
}

13
application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java

@ -27,6 +27,7 @@ import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
@ -66,7 +67,12 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
if (exception instanceof ThingsboardException) {
handleThingsboardException((ThingsboardException) exception, response);
ThingsboardException thingsboardException = (ThingsboardException) exception;
if (thingsboardException.getErrorCode() == ThingsboardErrorCode.SUBSCRIPTION_VIOLATION) {
handleSubscriptionException((ThingsboardException) exception, response);
} else {
handleThingsboardException((ThingsboardException) exception, response);
}
} else if (exception instanceof TbRateLimitsException) {
handleRateLimitException(response, (TbRateLimitsException) exception);
} else if (exception instanceof AccessDeniedException) {
@ -126,6 +132,11 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS));
}
private void handleSubscriptionException(ThingsboardException subscriptionException, HttpServletResponse response) throws IOException {
response.setStatus(HttpStatus.FORBIDDEN.value());
mapper.writeValue(response.getWriter(),
(new ObjectMapper()).readValue(((HttpClientErrorException) subscriptionException.getCause()).getResponseBodyAsByteArray(), Object.class));
}
private void handleAccessDeniedException(HttpServletResponse response) throws IOException {
response.setStatus(HttpStatus.FORBIDDEN.value());

12
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -167,7 +167,17 @@ public class ThingsboardInstallService {
databaseTsUpgradeService.upgradeDatabase("2.5.0");
}
case "2.5.1":
log.info("Upgrading ThingsBoard from version 2.5.1 to 3.0.0 ...");
log.info("Upgrading ThingsBoard from version 2.5.1 to 2.5.5 ...");
case "2.5.5":
log.info("Upgrading ThingsBoard from version 2.5.5 to 2.6.0 ...");
if (databaseTsUpgradeService != null) {
databaseTsUpgradeService.upgradeDatabase("2.5.5");
}
databaseEntitiesUpgradeService.upgradeDatabase("2.5.5");
dataUpdateService.updateData("2.5.5");
case "2.6.0":
log.info("Upgrading ThingsBoard from version 2.6.0 to 3.0.0 ...");
case "3.0.1":
log.info("Upgrading ThingsBoard from version 3.0.1 to 3.1.0 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.0.1");

353
application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java

@ -15,39 +15,42 @@
*/
package org.thingsboard.server.service.edge;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.IdBased;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
@ -56,14 +59,12 @@ import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@Service
@TbCoreComponent
@ -76,13 +77,13 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
private EdgeService edgeService;
@Autowired
private RuleChainService ruleChainService;
private AlarmService alarmService;
@Autowired
private AlarmService alarmService;
private UserService userService;
@Autowired
private RelationService relationService;
private RuleChainService ruleChainService;
@Autowired
private EdgeEventService edgeEventService;
@ -106,7 +107,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
@Override
public PageData<EdgeEvent> findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
return edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink);
return edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, true);
}
@Override
@ -119,22 +120,22 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
private void saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType edgeEventType,
ActionType edgeEventAction,
EdgeEventType type,
ActionType action,
EntityId entityId,
JsonNode entityBody) {
log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], edgeEventType [{}], edgeEventAction[{}], entityId [{}], entityBody [{}]",
tenantId, edgeId, edgeEventType, edgeEventAction, entityId, entityBody);
JsonNode body) {
log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
EdgeEvent edgeEvent = new EdgeEvent();
edgeEvent.setEdgeId(edgeId);
edgeEvent.setTenantId(tenantId);
edgeEvent.setEdgeEventType(edgeEventType);
edgeEvent.setEdgeEventAction(edgeEventAction.name());
edgeEvent.setType(type);
edgeEvent.setAction(action.name());
if (entityId != null) {
edgeEvent.setEntityId(entityId.getId());
}
edgeEvent.setEntityBody(entityBody);
edgeEvent.setBody(body);
edgeEventService.saveAsync(edgeEvent);
}
@ -142,16 +143,25 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) {
try {
TenantId tenantId = new TenantId(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB()));
EdgeEventType edgeEventType = EdgeEventType.valueOf(edgeNotificationMsg.getEdgeEventType());
switch (edgeEventType) {
// TODO: voba - handle edge updates
// case EDGE:
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
switch (type) {
case EDGE:
processEdge(tenantId, edgeNotificationMsg);
break;
case USER:
case ASSET:
case DEVICE:
case ENTITY_VIEW:
case DASHBOARD:
case RULE_CHAIN:
processEntities(tenantId, edgeNotificationMsg);
processEntity(tenantId, edgeNotificationMsg);
break;
case CUSTOMER:
processCustomer(tenantId, edgeNotificationMsg);
break;
case WIDGETS_BUNDLE:
case WIDGET_TYPE:
processWidgetBundleOrWidgetType(tenantId, edgeNotificationMsg);
break;
case ALARM:
processAlarm(tenantId, edgeNotificationMsg);
@ -160,7 +170,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
processRelation(tenantId, edgeNotificationMsg);
break;
default:
log.debug("Edge event type [{}] is not designed to be pushed to edge", edgeEventType);
log.debug("Edge event type [{}] is not designed to be pushed to edge", type);
}
} catch (Exception e) {
callback.onFailure(e);
@ -170,67 +180,219 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
}
}
private void processEntities(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
ActionType edgeEventActionType = ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction());
EdgeEventType edgeEventType = EdgeEventType.valueOf(edgeNotificationMsg.getEdgeEventType());
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(edgeEventType, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
switch (edgeEventActionType) {
// TODO: voba - ADDED is not required for CE version ?
// case ADDED:
private void processEdge(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
try {
ActionType actionType = ActionType.valueOf(edgeNotificationMsg.getAction());
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
ListenableFuture<Edge> edgeFuture;
switch (actionType) {
case ASSIGNED_TO_CUSTOMER:
CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
@Override
public void onSuccess(@Nullable Edge edge) {
if (edge != null && !customerId.isNullUid()) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, ActionType.ADDED, customerId, null);
PageData<User> pageData = userService.findCustomerUsers(tenantId, customerId, new PageLink(Integer.MAX_VALUE));
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size());
for (User user : pageData.getData()) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, ActionType.ADDED, user.getId(), null);
}
}
}
}
@Override
public void onFailure(Throwable t) {
log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
}
}, dbCallbackExecutorService);
break;
case UNASSIGNED_FROM_CUSTOMER:
CustomerId customerIdToDelete = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
@Override
public void onSuccess(@Nullable Edge edge) {
if (edge != null && !customerIdToDelete.isNullUid()) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, ActionType.DELETED, customerIdToDelete, null);
}
}
@Override
public void onFailure(Throwable t) {
log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
}
}, dbCallbackExecutorService);
break;
}
} catch (Exception e) {
log.error("Exception during processing edge event", e);
}
}
private void processWidgetBundleOrWidgetType(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
ActionType actionType = ActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
switch (actionType) {
case ADDED:
case UPDATED:
ListenableFuture<List<EdgeId>> edgeIdsFuture = findRelatedEdgeIdsByEntityId(tenantId, entityId);
Futures.transform(edgeIdsFuture, edgeIds -> {
if (edgeIds != null && !edgeIds.isEmpty()) {
for (EdgeId edgeId : edgeIds) {
try {
saveEdgeEvent(tenantId, edgeId, edgeEventType, edgeEventActionType, entityId, null);
if (edgeEventType.equals(EdgeEventType.RULE_CHAIN)) {
RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, new RuleChainId(entityId.getId()));
saveEdgeEvent(tenantId, edgeId, EdgeEventType.RULE_CHAIN_METADATA, edgeEventActionType, ruleChainMetaData.getRuleChainId(), null);
case DELETED:
PageData<Edge> edgesByTenantId = edgeService.findEdgesByTenantId(tenantId, new PageLink(Integer.MAX_VALUE));
if (edgesByTenantId != null && edgesByTenantId.getData() != null && !edgesByTenantId.getData().isEmpty()) {
for (Edge edge : edgesByTenantId.getData()) {
saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);
}
}
break;
}
}
private void processCustomer(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
ActionType actionType = ActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
PageData<Edge> edgesByTenantId = edgeService.findEdgesByTenantId(tenantId, new PageLink(Integer.MAX_VALUE));
if (edgesByTenantId != null && edgesByTenantId.getData() != null && !edgesByTenantId.getData().isEmpty()) {
for (Edge edge : edgesByTenantId.getData()) {
switch (actionType) {
case UPDATED:
if (!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(entityId)) {
saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);
}
break;
case DELETED:
saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);
break;
}
}
}
}
private void processEntity(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
ActionType actionType = ActionType.valueOf(edgeNotificationMsg.getAction());
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type,
new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
ListenableFuture<List<EdgeId>> edgeIdsFuture;
switch (actionType) {
case ADDED: // used only for USER entity
case UPDATED:
case CREDENTIALS_UPDATED:
edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
@Override
public void onSuccess(@Nullable List<EdgeId> edgeIds) {
if (edgeIds != null && !edgeIds.isEmpty()) {
for (EdgeId edgeId : edgeIds) {
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
}
}
}
@Override
public void onFailure(Throwable throwable) {
log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);
}
}, dbCallbackExecutorService);
break;
case ASSIGNED_TO_CUSTOMER:
case UNASSIGNED_FROM_CUSTOMER:
edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
@Override
public void onSuccess(@Nullable List<EdgeId> edgeIds) {
if (edgeIds != null && !edgeIds.isEmpty()) {
for (EdgeId edgeId : edgeIds) {
try {
CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
ListenableFuture<Edge> future = edgeService.findEdgeByIdAsync(tenantId, edgeId);
Futures.addCallback(future, new FutureCallback<Edge>() {
@Override
public void onSuccess(@Nullable Edge edge) {
if (edge != null && edge.getCustomerId() != null &&
!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) {
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
}
}
@Override
public void onFailure(Throwable throwable) {
log.error("Failed to find edge by id [{}]", edgeNotificationMsg, throwable);
}
}, dbCallbackExecutorService);
} catch (Exception e) {
log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e);
}
} catch (Exception e) {
log.error("[{}] Failed to push event to edge, edgeId [{}], edgeEventType [{}], edgeEventActionType [{}], entityId [{}]",
tenantId, edgeId, edgeEventType, edgeEventActionType, entityId, e);
}
}
}
return null;
@Override
public void onFailure(Throwable throwable) {
log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);
}
}, dbCallbackExecutorService);
break;
case DELETED:
PageData<Edge> edgesByTenantId = edgeService.findEdgesByTenantId(tenantId, new PageLink(Integer.MAX_VALUE));
if (edgesByTenantId != null && edgesByTenantId.getData() != null && !edgesByTenantId.getData().isEmpty()) {
for (Edge edge : edgesByTenantId.getData()) {
saveEdgeEvent(tenantId, edge.getId(), edgeEventType, edgeEventActionType, entityId, null);
saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);
}
}
break;
case ASSIGNED_TO_EDGE:
case UNASSIGNED_FROM_EDGE:
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
saveEdgeEvent(tenantId, edgeId, edgeEventType, edgeEventActionType, entityId, null);
break;
case RELATIONS_DELETED:
// TODO: voba - add support for relations deleted
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
if (type.equals(EdgeEventType.RULE_CHAIN)) {
updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);
}
break;
}
}
private void updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) {
PageData<RuleChain> pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, new TimePageLink(Integer.MAX_VALUE));
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
for (RuleChain ruleChain : pageData.getData()) {
if (!ruleChain.getId().equals(processingRuleChainId)) {
List<RuleChainConnectionInfo> connectionInfos =
ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections();
if (connectionInfos != null && !connectionInfos.isEmpty()) {
for (RuleChainConnectionInfo connectionInfo : connectionInfos) {
if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.RULE_CHAIN_METADATA,
ActionType.UPDATED,
ruleChain.getId(),
null);
}
}
}
}
}
}
}
private void processAlarm(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
Futures.transform(alarmFuture, alarm -> {
if (alarm != null) {
EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType());
if (edgeEventType != null) {
ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator());
EdgeEventType type = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType());
if (type != null) {
ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator());
Futures.transform(relatedEdgeIdsByEntityIdFuture, relatedEdgeIdsByEntityId -> {
if (relatedEdgeIdsByEntityId != null) {
for (EdgeId edgeId : relatedEdgeIdsByEntityId) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.ALARM,
ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction()),
ActionType.valueOf(edgeNotificationMsg.getAction()),
alarmId,
null);
}
@ -243,67 +405,38 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
}, dbCallbackExecutorService);
}
private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
EntityRelation entityRelation = mapper.convertValue(edgeNotificationMsg.getEntityBody(), EntityRelation.class);
List<ListenableFuture<List<EdgeId>>> futures = new ArrayList<>();
futures.add(findRelatedEdgeIdsByEntityId(tenantId, entityRelation.getTo()));
futures.add(findRelatedEdgeIdsByEntityId(tenantId, entityRelation.getFrom()));
ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures);
Futures.transform(combinedFuture, listOfListsEdgeIds -> {
Set<EdgeId> uniqueEdgeIds = new HashSet<>();
if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) {
for (List<EdgeId> listOfListsEdgeId : listOfListsEdgeIds) {
if (listOfListsEdgeId != null) {
uniqueEdgeIds.addAll(listOfListsEdgeId);
private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
EntityRelation relation = mapper.readValue(edgeNotificationMsg.getBody(), EntityRelation.class);
if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
!relation.getTo().getEntityType().equals(EntityType.EDGE)) {
List<ListenableFuture<List<EdgeId>>> futures = new ArrayList<>();
futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getTo()));
futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getFrom()));
ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures);
Futures.transform(combinedFuture, listOfListsEdgeIds -> {
Set<EdgeId> uniqueEdgeIds = new HashSet<>();
if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) {
for (List<EdgeId> listOfListsEdgeId : listOfListsEdgeIds) {
if (listOfListsEdgeId != null) {
uniqueEdgeIds.addAll(listOfListsEdgeId);
}
}
}
}
if (!uniqueEdgeIds.isEmpty()) {
for (EdgeId edgeId : uniqueEdgeIds) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.RELATION,
ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction()),
null,
mapper.valueToTree(entityRelation));
}
}
return null;
}, dbCallbackExecutorService);
}
private ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId) {
switch (entityId.getEntityType()) {
case DEVICE:
case ASSET:
case ENTITY_VIEW:
ListenableFuture<List<EntityRelation>> originatorEdgeRelationsFuture = relationService.findByToAndTypeAsync(tenantId, entityId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE);
return Futures.transform(originatorEdgeRelationsFuture, originatorEdgeRelations -> {
if (originatorEdgeRelations != null && originatorEdgeRelations.size() > 0) {
return Collections.singletonList(new EdgeId(originatorEdgeRelations.get(0).getFrom().getId()));
} else {
return Collections.emptyList();
if (!uniqueEdgeIds.isEmpty()) {
for (EdgeId edgeId : uniqueEdgeIds) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.RELATION,
ActionType.valueOf(edgeNotificationMsg.getAction()),
null,
mapper.valueToTree(relation));
}
}, dbCallbackExecutorService);
case DASHBOARD:
return convertToEdgeIds(edgeService.findEdgesByTenantIdAndDashboardId(tenantId, new DashboardId(entityId.getId())));
case RULE_CHAIN:
return convertToEdgeIds(edgeService.findEdgesByTenantIdAndRuleChainId(tenantId, new RuleChainId(entityId.getId())));
default:
return Futures.immediateFuture(Collections.emptyList());
}
return null;
}, dbCallbackExecutorService);
}
}
private ListenableFuture<List<EdgeId>> convertToEdgeIds(ListenableFuture<List<Edge>> future) {
return Futures.transform(future, edges -> {
if (edges != null && !edges.isEmpty()) {
return edges.stream().map(IdBased::getId).collect(Collectors.toList());
} else {
return Collections.emptyList();
}
}, dbCallbackExecutorService);
}
private EdgeEventType getEdgeQueueTypeByEntityType(EntityType entityType) {
switch (entityType) {
case DEVICE:
@ -313,7 +446,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
case ENTITY_VIEW:
return EdgeEventType.ENTITY_VIEW;
default:
log.info("Unsupported entity type: [{}]", entityType);
log.debug("Unsupported entity type: [{}]", entityType);
return null;
}
}

88
application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java

@ -33,22 +33,35 @@ import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings;
import org.thingsboard.server.service.edge.rpc.constructor.AlarmUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.AssetUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.UserUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor;
import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
import org.thingsboard.server.service.edge.rpc.processor.AlarmProcessor;
import org.thingsboard.server.service.edge.rpc.processor.DeviceProcessor;
import org.thingsboard.server.service.edge.rpc.processor.RelationProcessor;
import org.thingsboard.server.service.edge.rpc.processor.TelemetryProcessor;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.state.DeviceStateService;
@Component
@TbCoreComponent
@Data
public class EdgeContextComponent {
@ -56,6 +69,9 @@ public class EdgeContextComponent {
@Autowired
private EdgeService edgeService;
@Autowired
private PartitionService partitionService;
@Lazy
@Autowired
private EdgeNotificationService edgeNotificationService;
@ -108,6 +124,14 @@ public class EdgeContextComponent {
@Autowired
private ActorService actorService;
@Lazy
@Autowired
private WidgetsBundleService widgetsBundleService;
@Lazy
@Autowired
private WidgetTypeService widgetTypeService;
@Lazy
@Autowired
private DeviceStateService deviceStateService;
@ -122,40 +146,72 @@ public class EdgeContextComponent {
@Lazy
@Autowired
private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor;
private RuleChainMsgConstructor ruleChainMsgConstructor;
@Lazy
@Autowired
private AlarmMsgConstructor alarmMsgConstructor;
@Lazy
@Autowired
private DeviceMsgConstructor deviceMsgConstructor;
@Lazy
@Autowired
private AssetMsgConstructor assetMsgConstructor;
@Lazy
@Autowired
private EntityViewMsgConstructor entityViewMsgConstructor;
@Lazy
@Autowired
private AlarmUpdateMsgConstructor alarmUpdateMsgConstructor;
private DashboardMsgConstructor dashboardMsgConstructor;
@Lazy
@Autowired
private DeviceUpdateMsgConstructor deviceUpdateMsgConstructor;
private CustomerMsgConstructor customerMsgConstructor;
@Lazy
@Autowired
private AssetUpdateMsgConstructor assetUpdateMsgConstructor;
private UserMsgConstructor userMsgConstructor;
@Lazy
@Autowired
private EntityViewUpdateMsgConstructor entityViewUpdateMsgConstructor;
private RelationMsgConstructor relationMsgConstructor;
@Lazy
@Autowired
private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor;
private WidgetsBundleMsgConstructor widgetsBundleMsgConstructor;
@Lazy
@Autowired
private UserUpdateMsgConstructor userUpdateMsgConstructor;
private WidgetTypeMsgConstructor widgetTypeMsgConstructor;
@Lazy
@Autowired
private RelationUpdateMsgConstructor relationUpdateMsgConstructor;
private AdminSettingsMsgConstructor adminSettingsMsgConstructor;
@Lazy
@Autowired
private EntityDataMsgConstructor entityDataMsgConstructor;
@Lazy
@Autowired
private AlarmProcessor alarmProcessor;
@Lazy
@Autowired
private DeviceProcessor deviceProcessor;
@Lazy
@Autowired
private RelationProcessor relationProcessor;
@Lazy
@Autowired
private TelemetryProcessor telemetryProcessor;
@Lazy
@Autowired
private EdgeEventStorageSettings edgeEventStorageSettings;

117
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcService.java

@ -17,38 +17,47 @@ package org.thingsboard.server.service.edge.rpc;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.Resources;
import com.google.common.util.concurrent.FutureCallback;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.netty.NettyServerBuilder;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc;
import org.thingsboard.server.gen.edge.RequestMsg;
import org.thingsboard.server.gen.edge.ResponseMsg;
import org.thingsboard.server.service.edge.EdgeContextComponent;
import org.thingsboard.server.service.state.DefaultDeviceStateService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
@ConditionalOnProperty(prefix = "edges.rpc", value = "enabled", havingValue = "true")
public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase {
public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase implements EdgeRpcService {
private final Map<EdgeId, EdgeGrpcSession> sessions = new ConcurrentHashMap<>();
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final ObjectMapper mapper = new ObjectMapper();
@Value("${edges.rpc.port}")
private int rpcPort;
@ -58,10 +67,17 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase {
private String certFileResource;
@Value("${edges.rpc.ssl.private_key}")
private String privateKeyResource;
@Value("${edges.state.persistToTelemetry:false}")
private boolean persistToTelemetry;
@Value("${edges.rpc.client_max_keep_alive_time_sec}")
private int clientMaxKeepAliveTimeSec;
@Autowired
private EdgeContextComponent ctx;
@Autowired
private TelemetrySubscriptionService tsSubService;
private Server server;
private ExecutorService executor;
@ -69,7 +85,9 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase {
@PostConstruct
public void init() {
log.info("Initializing Edge RPC service!");
ServerBuilder builder = ServerBuilder.forPort(rpcPort).addService(this);
NettyServerBuilder builder = NettyServerBuilder.forPort(rpcPort)
.permitKeepAliveTime(clientMaxKeepAliveTimeSec, TimeUnit.SECONDS)
.addService(this);
if (sslEnabled) {
try {
File certFile = new File(Resources.getResource(certFileResource).toURI());
@ -102,22 +120,60 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase {
@Override
public StreamObserver<RequestMsg> handleMsgs(StreamObserver<ResponseMsg> outputStream) {
return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, objectMapper).getInputStream();
return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper).getInputStream();
}
@Override
public void updateEdge(Edge edge) {
EdgeGrpcSession session = sessions.get(edge.getId());
if (session != null && session.isConnected()) {
session.onConfigurationUpdate(edge);
}
}
@Override
public void deleteEdge(EdgeId edgeId) {
EdgeGrpcSession session = sessions.get(edgeId);
if (session != null && session.isConnected()) {
session.close();
sessions.remove(edgeId);
}
}
private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) {
sessions.put(edgeId, edgeGrpcSession);
save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true);
save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, System.currentTimeMillis());
}
public EdgeGrpcSession getEdgeGrpcSessionById(EdgeId edgeId) {
EdgeGrpcSession session = sessions.get(edgeId);
if (session != null && session.isConnected()) {
return session;
} else {
throw new RuntimeException("Edge is not connected");
}
}
private void processHandleMessages() {
executor.submit(() -> {
while (!Thread.interrupted()) {
try {
for (EdgeGrpcSession session : sessions.values()) {
session.processHandleMessages();
if (sessions.size() > 0) {
for (EdgeGrpcSession session : sessions.values()) {
session.processHandleMessages();
}
} else {
log.trace("No sessions available, sleep for the next run");
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {}
}
} catch (Exception e) {
log.warn("Failed to process messages handling!", e);
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {}
}
}
});
@ -125,6 +181,51 @@ public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase {
private void onEdgeDisconnect(EdgeId edgeId) {
sessions.remove(edgeId);
save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false);
save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, System.currentTimeMillis());
}
private void save(EdgeId edgeId, String key, long value) {
if (persistToTelemetry) {
tsSubService.saveAndNotify(
TenantId.SYS_TENANT_ID, edgeId,
Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))),
new AttributeSaveCallback(edgeId, key, value));
} else {
tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value));
}
}
private void save(EdgeId edgeId, String key, boolean value) {
if (persistToTelemetry) {
tsSubService.saveAndNotify(
TenantId.SYS_TENANT_ID, edgeId,
Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))),
new AttributeSaveCallback(edgeId, key, value));
} else {
tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value));
}
}
private static class AttributeSaveCallback implements FutureCallback<Void> {
private final EdgeId edgeId;
private final String key;
private final Object value;
AttributeSaveCallback(EdgeId edgeId, String key, Object value) {
this.edgeId = edgeId;
this.key = key;
this.value = value;
}
@Override
public void onSuccess(@javax.annotation.Nullable Void result) {
log.trace("[{}] Successfully updated attribute [{}] with value [{}]", edgeId, key, value);
}
@Override
public void onFailure(Throwable t) {
log.warn("[{}] Failed to update attribute [{}] with value [{}]", edgeId, key, value, t);
}
}
}

1280
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

File diff suppressed because it is too large

26
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeRpcService.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EdgeId;
public interface EdgeRpcService {
void updateEdge(Edge edge);
void deleteEdge(EdgeId edgeId);
}

38
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AdminSettingsMsgConstructor.java

@ -0,0 +1,38 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.constructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg;
@Slf4j
@Component
public class AdminSettingsMsgConstructor {
public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings) {
AdminSettingsUpdateMsg.Builder builder = AdminSettingsUpdateMsg.newBuilder()
.setKey(adminSettings.getKey())
.setJsonValue(JacksonUtil.toString(adminSettings.getJsonValue()));
if (adminSettings.getId() != null) {
builder.setIsSystem(true);
}
return builder.build();
}
}

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmUpdateMsgConstructor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AlarmMsgConstructor.java

@ -34,7 +34,7 @@ import org.thingsboard.server.gen.edge.UpdateMsgType;
@Component
@Slf4j
public class AlarmUpdateMsgConstructor {
public class AlarmMsgConstructor {
@Autowired
private DeviceService deviceService;

13
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetMsgConstructor.java

@ -19,14 +19,16 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
@Component
@Slf4j
public class AssetUpdateMsgConstructor {
public class AssetMsgConstructor {
public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset) {
public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset, CustomerId customerId) {
AssetUpdateMsg.Builder builder = AssetUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(asset.getId().getId().getMostSignificantBits())
@ -36,6 +38,13 @@ public class AssetUpdateMsgConstructor {
if (asset.getLabel() != null) {
builder.setLabel(asset.getLabel());
}
if (customerId != null) {
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
}
if (asset.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(asset.getAdditionalInfo()));
}
return builder.build();
}

72
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/CustomerMsgConstructor.java

@ -0,0 +1,72 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.constructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
@Component
@Slf4j
public class CustomerMsgConstructor {
public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) {
CustomerUpdateMsg.Builder builder = CustomerUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(customer.getId().getId().getMostSignificantBits())
.setIdLSB(customer.getId().getId().getLeastSignificantBits())
.setTitle(customer.getTitle());
if (customer.getCountry() != null) {
builder.setCountry(customer.getCountry());
}
if (customer.getState() != null) {
builder.setState(customer.getState());
}
if (customer.getCity() != null) {
builder.setCity(customer.getCity());
}
if (customer.getAddress() != null) {
builder.setAddress(customer.getAddress());
}
if (customer.getAddress2() != null) {
builder.setAddress2(customer.getAddress2());
}
if (customer.getZip() != null) {
builder.setZip(customer.getZip());
}
if (customer.getPhone() != null) {
builder.setPhone(customer.getPhone());
}
if (customer.getEmail() != null) {
builder.setEmail(customer.getEmail());
}
if (customer.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(customer.getAdditionalInfo()));
}
return builder.build();
}
public CustomerUpdateMsg constructCustomerDeleteMsg(CustomerId customerId) {
return CustomerUpdateMsg.newBuilder()
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(customerId.getId().getMostSignificantBits())
.setIdLSB(customerId.getId().getLeastSignificantBits()).build();
}
}

16
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardMsgConstructor.java

@ -16,30 +16,30 @@
package org.thingsboard.server.service.edge.rpc.constructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
@Component
@Slf4j
public class DashboardUpdateMsgConstructor {
public class DashboardMsgConstructor {
@Autowired
private DashboardService dashboardService;
public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) {
dashboard = dashboardService.findDashboardById(dashboard.getTenantId(), dashboard.getId());
public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard, CustomerId customerId) {
DashboardUpdateMsg.Builder builder = DashboardUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(dashboard.getId().getId().getMostSignificantBits())
.setIdLSB(dashboard.getId().getId().getLeastSignificantBits())
.setTitle(dashboard.getTitle())
.setConfiguration(JacksonUtil.toString(dashboard.getConfiguration()));
if (customerId != null) {
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
}
return builder.build();
}

97
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceMsgConstructor.java

@ -0,0 +1,97 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.constructor;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.RpcRequestMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
@Component
@Slf4j
public class DeviceMsgConstructor {
protected static final ObjectMapper mapper = new ObjectMapper();
public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, CustomerId customerId) {
DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(device.getId().getId().getMostSignificantBits())
.setIdLSB(device.getId().getId().getLeastSignificantBits())
.setName(device.getName())
.setType(device.getType());
if (device.getLabel() != null) {
builder.setLabel(device.getLabel());
}
if (customerId != null) {
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
}
if (device.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo()));
}
return builder.build();
}
public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) {
DeviceCredentialsUpdateMsg.Builder builder = DeviceCredentialsUpdateMsg.newBuilder()
.setDeviceIdMSB(deviceCredentials.getDeviceId().getId().getMostSignificantBits())
.setDeviceIdLSB(deviceCredentials.getDeviceId().getId().getLeastSignificantBits());
if (deviceCredentials.getCredentialsType() != null) {
builder.setCredentialsType(deviceCredentials.getCredentialsType().name())
.setCredentialsId(deviceCredentials.getCredentialsId());
}
if (deviceCredentials.getCredentialsValue() != null) {
builder.setCredentialsValue(deviceCredentials.getCredentialsValue());
}
return builder.build();
}
public DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId) {
return DeviceUpdateMsg.newBuilder()
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(deviceId.getId().getMostSignificantBits())
.setIdLSB(deviceId.getId().getLeastSignificantBits()).build();
}
public DeviceRpcCallMsg constructDeviceRpcCallMsg(JsonNode body) {
RuleEngineDeviceRpcRequest request = mapper.convertValue(body, RuleEngineDeviceRpcRequest.class);
RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder();
requestBuilder.setMethod(request.getMethod());
requestBuilder.setParams(request.getBody());
DeviceRpcCallMsg.Builder builder = DeviceRpcCallMsg.newBuilder()
.setDeviceIdMSB(request.getDeviceId().getId().getMostSignificantBits())
.setDeviceIdLSB(request.getDeviceId().getId().getLeastSignificantBits())
.setRequestIdMSB(request.getRequestUUID().getMostSignificantBits())
.setRequestIdLSB(request.getRequestUUID().getLeastSignificantBits())
.setExpirationTime(request.getExpirationTime())
.setOriginServiceId(request.getOriginServiceId())
.setOneway(request.isOneway())
.setRequestMsg(requestBuilder.build());
return builder.build();
}
}

69
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java

@ -1,69 +0,0 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.constructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
@Component
@Slf4j
public class DeviceUpdateMsgConstructor {
@Autowired
private DeviceCredentialsService deviceCredentialsService;
public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device) {
DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(device.getId().getId().getMostSignificantBits())
.setIdLSB(device.getId().getId().getLeastSignificantBits())
.setName(device.getName())
.setType(device.getType());
if (device.getLabel() != null) {
builder.setLabel(device.getLabel());
}
if (msgType.equals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE) ||
msgType.equals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE) ||
msgType.equals(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE)) {
DeviceCredentials deviceCredentials
= deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId());
if (deviceCredentials != null) {
if (deviceCredentials.getCredentialsType() != null) {
builder.setCredentialsType(deviceCredentials.getCredentialsType().name())
.setCredentialsId(deviceCredentials.getCredentialsId());
}
if (deviceCredentials.getCredentialsValue() != null) {
builder.setCredentialsValue(deviceCredentials.getCredentialsValue());
}
}
}
return builder.build();
}
public DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId) {
return DeviceUpdateMsg.newBuilder()
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(deviceId.getId().getMostSignificantBits())
.setIdLSB(deviceId.getId().getLeastSignificantBits()).build();
}
}

40
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java

@ -15,13 +15,20 @@
*/
package org.thingsboard.server.service.edge.rpc.constructor;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.gen.edge.AttributeDeleteMsg;
import org.thingsboard.server.gen.edge.EntityDataProto;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.List;
@Component
@Slf4j
@ -35,20 +42,45 @@ public class EntityDataMsgConstructor {
switch (actionType) {
case TIMESERIES_UPDATED:
try {
builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(entityData));
JsonObject data = entityData.getAsJsonObject();
long ts;
if (data.get("ts") != null && !data.get("ts").isJsonNull()) {
ts = data.getAsJsonPrimitive("ts").getAsLong();
} else {
ts = System.currentTimeMillis();
}
builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data.getAsJsonObject("data"), ts));
} catch (Exception e) {
log.warn("Can't convert to telemetry proto, entityData [{}]", entityData, e);
}
break;
case ATTRIBUTES_UPDATED:
try {
builder.setPostAttributesMsg(JsonConverter.convertToAttributesProto(entityData));
JsonObject data = entityData.getAsJsonObject();
TransportProtos.PostAttributeMsg postAttributeMsg = JsonConverter.convertToAttributesProto(data.getAsJsonObject("kv"));
if (data.has("isPostAttributes") && data.getAsJsonPrimitive("isPostAttributes").getAsBoolean()) {
builder.setPostAttributesMsg(postAttributeMsg);
} else {
builder.setAttributesUpdatedMsg(postAttributeMsg);
}
builder.setPostAttributeScope(data.getAsJsonPrimitive("scope").getAsString());
} catch (Exception e) {
log.warn("Can't convert to attributes proto, entityData [{}]", entityData, e);
}
break;
// TODO: voba - add support for attribute delete
// case ATTRIBUTES_DELETED:
case ATTRIBUTES_DELETED:
try {
AttributeDeleteMsg.Builder attributeDeleteMsg = AttributeDeleteMsg.newBuilder();
attributeDeleteMsg.setScope(entityData.getAsJsonObject().getAsJsonPrimitive("scope").getAsString());
JsonArray jsonArray = entityData.getAsJsonObject().getAsJsonArray("keys");
List<String> keys = new Gson().fromJson(jsonArray.toString(), List.class);
attributeDeleteMsg.addAllAttributeNames(keys);
attributeDeleteMsg.build();
builder.setAttributeDeleteMsg(attributeDeleteMsg);
} catch (Exception e) {
log.warn("Can't convert to AttributeDeleteMsg proto, entityData [{}]", entityData, e);
}
break;
}
return builder.build();
}

17
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewMsgConstructor.java

@ -18,16 +18,18 @@ package org.thingsboard.server.service.edge.rpc.constructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.EdgeEntityType;
import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
@Component
@Slf4j
public class EntityViewUpdateMsgConstructor {
public class EntityViewMsgConstructor {
public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) {
public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView, CustomerId customerId) {
EdgeEntityType entityType;
switch (entityView.getEntityId().getEntityType()) {
case DEVICE:
@ -45,9 +47,16 @@ public class EntityViewUpdateMsgConstructor {
.setIdLSB(entityView.getId().getId().getLeastSignificantBits())
.setName(entityView.getName())
.setType(entityView.getType())
.setIdMSB(entityView.getEntityId().getId().getMostSignificantBits())
.setIdLSB(entityView.getEntityId().getId().getLeastSignificantBits())
.setEntityIdMSB(entityView.getEntityId().getId().getMostSignificantBits())
.setEntityIdLSB(entityView.getEntityId().getId().getLeastSignificantBits())
.setEntityType(entityType);
if (customerId != null) {
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
}
if (entityView.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(entityView.getAdditionalInfo()));
}
return builder.build();
}

2
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationMsgConstructor.java

@ -24,7 +24,7 @@ import org.thingsboard.server.gen.edge.UpdateMsgType;
@Component
@Slf4j
public class RelationUpdateMsgConstructor {
public class RelationMsgConstructor {
public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) {
RelationUpdateMsg.Builder builder = RelationUpdateMsg.newBuilder()

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainMsgConstructor.java

@ -38,7 +38,7 @@ import java.util.List;
@Component
@Slf4j
public class RuleChainUpdateMsgConstructor {
public class RuleChainMsgConstructor {
private static final ObjectMapper objectMapper = new ObjectMapper();
@ -68,6 +68,8 @@ public class RuleChainUpdateMsgConstructor {
.addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections()));
if (ruleChainMetaData.getFirstNodeIndex() != null) {
builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex());
} else {
builder.setFirstNodeIndex(-1);
}
builder.setMsgType(msgType);
return builder.build();

37
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserMsgConstructor.java

@ -16,31 +16,34 @@
package org.thingsboard.server.service.edge.rpc.constructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.UserUpdateMsg;
import java.util.UUID;
@Component
@Slf4j
public class UserUpdateMsgConstructor {
@Autowired
private UserService userService;
public class UserMsgConstructor {
public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) {
public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user, CustomerId customerId) {
UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(user.getId().getId().getMostSignificantBits())
.setIdLSB(user.getId().getId().getLeastSignificantBits())
.setEmail(user.getEmail())
.setAuthority(user.getAuthority().name())
.setEnabled(false);
.setAuthority(user.getAuthority().name());
if (customerId != null) {
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits());
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits());
}
if (user.getFirstName() != null) {
builder.setFirstName(user.getFirstName());
}
@ -53,13 +56,6 @@ public class UserUpdateMsgConstructor {
if (user.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo()));
}
if (msgType.equals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE) ||
msgType.equals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE)) {
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
if (userCredentials != null) {
builder.setEnabled(userCredentials.isEnabled()).setPassword(userCredentials.getPassword());
}
}
return builder.build();
}
@ -69,4 +65,13 @@ public class UserUpdateMsgConstructor {
.setIdMSB(userId.getId().getMostSignificantBits())
.setIdLSB(userId.getId().getLeastSignificantBits()).build();
}
public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials) {
UserCredentialsUpdateMsg.Builder builder = UserCredentialsUpdateMsg.newBuilder()
.setUserIdMSB(userCredentials.getUserId().getId().getMostSignificantBits())
.setUserIdLSB(userCredentials.getUserId().getId().getLeastSignificantBits())
.setEnabled(userCredentials.isEnabled())
.setPassword(userCredentials.getPassword());
return builder.build();
}
}

61
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetTypeMsgConstructor.java

@ -0,0 +1,61 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.constructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg;
@Component
@Slf4j
public class WidgetTypeMsgConstructor {
public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetType widgetType) {
WidgetTypeUpdateMsg.Builder builder = WidgetTypeUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(widgetType.getId().getId().getMostSignificantBits())
.setIdLSB(widgetType.getId().getId().getLeastSignificantBits());
if (widgetType.getBundleAlias() != null) {
builder.setBundleAlias(widgetType.getBundleAlias());
}
if (widgetType.getAlias() != null) {
builder.setAlias(widgetType.getAlias());
}
if (widgetType.getName() != null) {
builder.setName(widgetType.getName());
}
if (widgetType.getDescriptor() != null) {
builder.setDescriptorJson(JacksonUtil.toString(widgetType.getDescriptor()));
}
if (widgetType.getTenantId().equals(TenantId.SYS_TENANT_ID)) {
builder.setIsSystem(true);
}
return builder.build();
}
public WidgetTypeUpdateMsg constructWidgetTypeDeleteMsg(WidgetTypeId widgetTypeId) {
return WidgetTypeUpdateMsg.newBuilder()
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(widgetTypeId.getId().getMostSignificantBits())
.setIdLSB(widgetTypeId.getId().getLeastSignificantBits())
.build();
}
}

54
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/WidgetsBundleMsgConstructor.java

@ -0,0 +1,54 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.constructor;
import com.google.protobuf.ByteString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg;
@Component
@Slf4j
public class WidgetsBundleMsgConstructor {
public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle) {
WidgetsBundleUpdateMsg.Builder builder = WidgetsBundleUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits())
.setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits())
.setTitle(widgetsBundle.getTitle())
.setAlias(widgetsBundle.getAlias());
if (widgetsBundle.getImage() != null) {
builder.setImage(ByteString.copyFrom(widgetsBundle.getImage()));
}
if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) {
builder.setIsSystem(true);
}
return builder.build();
}
public WidgetsBundleUpdateMsg constructWidgetsBundleDeleteMsg(WidgetsBundleId widgetsBundleId) {
return WidgetsBundleUpdateMsg.newBuilder()
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(widgetsBundleId.getId().getMostSignificantBits())
.setIdLSB(widgetsBundleId.getId().getLeastSignificantBits())
.build();
}
}

549
application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java

@ -15,22 +15,44 @@
*/
package org.thingsboard.server.service.edge.rpc.init;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import io.grpc.stub.StreamObserver;
import com.google.common.util.concurrent.SettableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.AdminSettingsId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
@ -39,45 +61,49 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationsSearchParameters;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.gen.edge.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.EntityUpdateMsg;
import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.ResponseMsg;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.gen.edge.AttributesRequestMsg;
import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.RelationRequestMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
import org.thingsboard.server.gen.edge.UserUpdateMsg;
import org.thingsboard.server.service.edge.EdgeContextComponent;
import org.thingsboard.server.service.edge.rpc.constructor.AssetUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DashboardUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.DeviceUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RelationUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.UserUpdateMsgConstructor;
import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service
@Slf4j
public class DefaultSyncEdgeService implements SyncEdgeService {
private static final ObjectMapper mapper = new ObjectMapper();
@Autowired
private EdgeEventService edgeEventService;
@Autowired
private AttributesService attributesService;
@Autowired
private RuleChainService ruleChainService;
@ -100,272 +126,417 @@ public class DefaultSyncEdgeService implements SyncEdgeService {
private UserService userService;
@Autowired
private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor;
@Autowired
private DeviceUpdateMsgConstructor deviceUpdateMsgConstructor;
@Autowired
private AssetUpdateMsgConstructor assetUpdateMsgConstructor;
@Autowired
private EntityViewUpdateMsgConstructor entityViewUpdateMsgConstructor;
private WidgetsBundleService widgetsBundleService;
@Autowired
private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor;
private WidgetTypeService widgetTypeService;
@Autowired
private UserUpdateMsgConstructor userUpdateMsgConstructor;
private AdminSettingsService adminSettingsService;
@Autowired
private RelationUpdateMsgConstructor relationUpdateMsgConstructor;
private DbCallbackExecutorService dbCallbackExecutorService;
@Override
public void sync(EdgeContextComponent ctx, Edge edge, StreamObserver<ResponseMsg> outputStream) {
Set<EntityId> pushedEntityIds = new HashSet<>();
syncUsers(ctx, edge, pushedEntityIds, outputStream);
List<ListenableFuture<Void>> futures = new ArrayList<>();
futures.add(syncRuleChains(ctx, edge, pushedEntityIds, outputStream));
futures.add(syncDevices(ctx, edge, pushedEntityIds, outputStream));
futures.add(syncAssets(ctx, edge, pushedEntityIds, outputStream));
futures.add(syncEntityViews(ctx, edge, pushedEntityIds, outputStream));
futures.add(syncDashboards(ctx, edge, pushedEntityIds, outputStream));
ListenableFuture<List<Void>> joinFuture = Futures.allAsList(futures);
Futures.transform(joinFuture, result -> {
syncRelations(ctx, edge, pushedEntityIds, outputStream);
return null;
}, MoreExecutors.directExecutor());
public void sync(Edge edge) {
try {
syncWidgetsBundleAndWidgetTypes(edge);
syncAdminSettings(edge);
syncRuleChains(edge);
syncUsers(edge);
syncDevices(edge);
syncAssets(edge);
syncEntityViews(edge);
syncDashboards(edge);
} catch (Exception e) {
log.error("Exception during sync process", e);
}
}
private ListenableFuture<Void> syncRuleChains(EdgeContextComponent ctx, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
private void syncRuleChains(Edge edge) {
try {
PageData<RuleChain> pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
PageData<RuleChain> pageData =
ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] rule chains(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (RuleChain ruleChain : pageData.getData()) {
RuleChainUpdateMsg ruleChainUpdateMsg =
ruleChainUpdateMsgConstructor.constructRuleChainUpdatedMsg(
edge.getRootRuleChainId(),
UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE,
ruleChain);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setRuleChainUpdateMsg(ruleChainUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
pushedEntityIds.add(ruleChain.getId());
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.RULE_CHAIN, ActionType.ADDED, ruleChain.getId(), null);
}
}
} catch (Exception e) {
log.error("Exception during loading edge rule chain(s) on sync!", e);
}
return Futures.immediateFuture(null);
}
private ListenableFuture<Void> syncDevices(EdgeContextComponent ctx, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
private void syncDevices(Edge edge) {
try {
PageData<Device> pageData = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
PageData<Device> pageData =
deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (Device device : pageData.getData()) {
DeviceUpdateMsg deviceUpdateMsg =
deviceUpdateMsgConstructor.constructDeviceUpdatedMsg(
UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE,
device);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setDeviceUpdateMsg(deviceUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
pushedEntityIds.add(device.getId());
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.DEVICE, ActionType.ADDED, device.getId(), null);
}
}
} catch (Exception e) {
log.error("Exception during loading edge device(s) on sync!", e);
}
return Futures.immediateFuture(null);
}
private ListenableFuture<Void> syncAssets(EdgeContextComponent ctx, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
private void syncAssets(Edge edge) {
try {
PageData<Asset> pageData = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] asset(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (Asset asset : pageData.getData()) {
AssetUpdateMsg assetUpdateMsg =
assetUpdateMsgConstructor.constructAssetUpdatedMsg(
UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE,
asset);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setAssetUpdateMsg(assetUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
pushedEntityIds.add(asset.getId());
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ASSET, ActionType.ADDED, asset.getId(), null);
}
}
} catch (Exception e) {
log.error("Exception during loading edge asset(s) on sync!", e);
}
return Futures.immediateFuture(null);
}
private ListenableFuture<Void> syncEntityViews(EdgeContextComponent ctx, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
private void syncEntityViews(Edge edge) {
try {
PageData<EntityView> pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] entity view(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (EntityView entityView : pageData.getData()) {
EntityViewUpdateMsg entityViewUpdateMsg =
entityViewUpdateMsgConstructor.constructEntityViewUpdatedMsg(
UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE,
entityView);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setEntityViewUpdateMsg(entityViewUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
pushedEntityIds.add(entityView.getId());
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ENTITY_VIEW, ActionType.ADDED, entityView.getId(), null);
}
}
} catch (Exception e) {
log.error("Exception during loading edge entity view(s) on sync!", e);
}
return Futures.immediateFuture(null);
}
private ListenableFuture<Void> syncDashboards(EdgeContextComponent ctx, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
private void syncDashboards(Edge edge) {
try {
PageData<DashboardInfo> pageData = dashboardService.findDashboardsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] dashboard(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (DashboardInfo dashboardInfo : pageData.getData()) {
Dashboard dashboard = dashboardService.findDashboardById(edge.getTenantId(), dashboardInfo.getId());
DashboardUpdateMsg dashboardUpdateMsg =
dashboardUpdateMsgConstructor.constructDashboardUpdatedMsg(
UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE,
dashboard);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setDashboardUpdateMsg(dashboardUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
pushedEntityIds.add(dashboard.getId());
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.DASHBOARD, ActionType.ADDED, dashboardInfo.getId(), null);
}
}
} catch (Exception e) {
log.error("Exception during loading edge dashboard(s) on sync!", e);
}
return Futures.immediateFuture(null);
}
private void syncUsers(EdgeContextComponent ctx, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
private void syncUsers(Edge edge) {
try {
PageData<User> pageData = userService.findTenantAdmins(edge.getTenantId(), new PageLink(Integer.MAX_VALUE));
pushUsersToEdge(pageData, edge, pushedEntityIds, outputStream);
pushUsersToEdge(pageData, edge);
if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, ActionType.ADDED, edge.getCustomerId(), null);
pageData = userService.findCustomerUsers(edge.getTenantId(), edge.getCustomerId(), new PageLink(Integer.MAX_VALUE));
pushUsersToEdge(pageData, edge, pushedEntityIds, outputStream);
pushUsersToEdge(pageData, edge);
}
} catch (Exception e) {
log.error("Exception during loading edge user(s) on sync!", e);
}
}
private void pushUsersToEdge(PageData<User> pageData, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
private void syncWidgetsBundleAndWidgetTypes(Edge edge) {
List<WidgetsBundle> widgetsBundlesToPush = new ArrayList<>();
List<WidgetType> widgetTypesToPush = new ArrayList<>();
widgetsBundlesToPush.addAll(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(edge.getTenantId()));
widgetsBundlesToPush.addAll(widgetsBundleService.findSystemWidgetsBundles(edge.getTenantId()));
try {
for (WidgetsBundle widgetsBundle: widgetsBundlesToPush) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.WIDGETS_BUNDLE, ActionType.ADDED, widgetsBundle.getId(), null);
widgetTypesToPush.addAll(widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(widgetsBundle.getTenantId(), widgetsBundle.getAlias()));
}
for (WidgetType widgetType: widgetTypesToPush) {
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.WIDGET_TYPE, ActionType.ADDED, widgetType.getId(), null);
}
} catch (Exception e) {
log.error("Exception during loading widgets bundle(s) and widget type(s) on sync!", e);
}
}
private void syncAdminSettings(Edge edge) {
try {
AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail");
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ADMIN_SETTINGS, ActionType.UPDATED, null, mapper.valueToTree(systemMailSettings));
AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue());
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ADMIN_SETTINGS, ActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings));
AdminSettings systemMailTemplates = loadMailTemplates();
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ADMIN_SETTINGS, ActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates));
AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue());
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.ADMIN_SETTINGS, ActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates));
} catch (Exception e) {
log.error("Can't load admin settings", e);
}
}
private AdminSettings loadMailTemplates() throws Exception {
Map<String, Object> mailTemplates = new HashMap<>();
Pattern startPattern = Pattern.compile("<div class=\"content\".*?>");
Pattern endPattern = Pattern.compile("<div class=\"footer\".*?>");
File[] files = new DefaultResourceLoader().getResource("classpath:/templates/").getFile().listFiles();
for (File file: files) {
Map<String, String> mailTemplate = new HashMap<>();
String name = validateName(file.getName());
String stringTemplate = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
Matcher start = startPattern.matcher(stringTemplate);
Matcher end = endPattern.matcher(stringTemplate);
if (start.find() && end.find()) {
String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", "");
String subject = StringUtils.substringBetween(body, "<h2>", "</h2>");
mailTemplate.put("subject", subject);
mailTemplate.put("body", body);
mailTemplates.put(name, mailTemplate);
} else {
log.error("Can't load mail template from file {}", file.getName());
}
}
AdminSettings adminSettings = new AdminSettings();
adminSettings.setId(new AdminSettingsId(Uuids.timeBased()));
adminSettings.setKey("mailTemplates");
adminSettings.setJsonValue(mapper.convertValue(mailTemplates, JsonNode.class));
return adminSettings;
}
private String validateName(String name) throws Exception {
StringBuilder nameBuilder = new StringBuilder();
name = name.replace(".vm", "");
String[] nameParts = name.split("\\.");
if (nameParts.length >= 1) {
nameBuilder.append(nameParts[0]);
for (int i = 1; i < nameParts.length; i++) {
String word = WordUtils.capitalize(nameParts[i]);
nameBuilder.append(word);
}
return nameBuilder.toString();
} else {
throw new Exception("Error during filename validation");
}
}
private AdminSettings convertToTenantAdminSettings(String key, ObjectNode jsonValue) {
AdminSettings tenantMailSettings = new AdminSettings();
jsonValue.put("useSystemMailSettings", true);
tenantMailSettings.setJsonValue(jsonValue);
tenantMailSettings.setKey(key);
return tenantMailSettings;
}
private void pushUsersToEdge(PageData<User> pageData, Edge edge) {
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
log.trace("[{}] [{}] user(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size());
for (User user : pageData.getData()) {
UserUpdateMsg userUpdateMsg =
userUpdateMsgConstructor.constructUserUpdatedMsg(
UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE,
user);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setUserUpdateMsg(userUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
pushedEntityIds.add(user.getId());
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, ActionType.ADDED, user.getId(), null);
}
}
}
private ListenableFuture<Void> syncRelations(EdgeContextComponent ctx, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
if (!pushedEntityIds.isEmpty()) {
List<ListenableFuture<List<EntityRelation>>> futures = new ArrayList<>();
for (EntityId entityId : pushedEntityIds) {
futures.add(syncRelations(edge, entityId, EntitySearchDirection.FROM));
futures.add(syncRelations(edge, entityId, EntitySearchDirection.TO));
}
ListenableFuture<List<List<EntityRelation>>> relationsListFuture = Futures.allAsList(futures);
return Futures.transform(relationsListFuture, relationsList -> {
try {
Set<EntityRelation> uniqueEntityRelations = new HashSet<>();
if (!relationsList.isEmpty()) {
for (List<EntityRelation> entityRelations : relationsList) {
if (!entityRelations.isEmpty()) {
uniqueEntityRelations.addAll(entityRelations);
@Override
public ListenableFuture<Void> processRuleChainMetadataRequestMsg(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg) {
SettableFuture<Void> futureToSet = SettableFuture.create();
if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) {
RuleChainId ruleChainId =
new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB()));
ListenableFuture<EdgeEvent> future = saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.RULE_CHAIN_METADATA, ActionType.ADDED, ruleChainId, null);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't save edge event [{}]", ruleChainMetadataRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
return futureToSet;
}
@Override
public ListenableFuture<Void> processAttributesRequestMsg(Edge edge, AttributesRequestMsg attributesRequestMsg) {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(
EntityType.valueOf(attributesRequestMsg.getEntityType()),
new UUID(attributesRequestMsg.getEntityIdMSB(), attributesRequestMsg.getEntityIdLSB()));
final EdgeEventType type = getEdgeQueueTypeByEntityType(entityId.getEntityType());
if (type != null) {
SettableFuture<Void> futureToSet = SettableFuture.create();
ListenableFuture<List<AttributeKvEntry>> ssAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.SERVER_SCOPE);
Futures.addCallback(ssAttrFuture, new FutureCallback<List<AttributeKvEntry>>() {
@Override
public void onSuccess(@Nullable List<AttributeKvEntry> ssAttributes) {
if (ssAttributes != null && !ssAttributes.isEmpty()) {
try {
Map<String, Object> entityData = new HashMap<>();
ObjectNode attributes = mapper.createObjectNode();
for (AttributeKvEntry attr : ssAttributes) {
if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) {
attributes.put(attr.getKey(), attr.getBooleanValue().get());
} else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) {
attributes.put(attr.getKey(), attr.getDoubleValue().get());
} else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) {
attributes.put(attr.getKey(), attr.getLongValue().get());
} else {
attributes.put(attr.getKey(), attr.getValueAsString());
}
}
entityData.put("kv", attributes);
entityData.put("scope", DataConstants.SERVER_SCOPE);
JsonNode body = mapper.valueToTree(entityData);
log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, body);
saveEdgeEvent(edge.getTenantId(),
edge.getId(),
type,
ActionType.ATTRIBUTES_UPDATED,
entityId,
body);
} catch (Exception e) {
log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e);
throw new RuntimeException("[" + edge.getName() + "] Failed to send attribute updates to the edge", e);
}
}
futureToSet.set(null);
}
}
if (!uniqueEntityRelations.isEmpty()) {
log.trace("[{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), uniqueEntityRelations.size());
for (EntityRelation relation : uniqueEntityRelations) {
@Override
public void onFailure(Throwable t) {
log.error("Can't save attributes [{}]", attributesRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
return futureToSet;
// TODO: voba - push shared attributes to edge?
// ListenableFuture<List<AttributeKvEntry>> shAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.SHARED_SCOPE);
// ListenableFuture<List<AttributeKvEntry>> clAttrFuture = attributesService.findAll(edge.getTenantId(), entityId, DataConstants.CLIENT_SCOPE);
} else {
return Futures.immediateFuture(null);
}
}
private EdgeEventType getEdgeQueueTypeByEntityType(EntityType entityType) {
switch (entityType) {
case DEVICE:
return EdgeEventType.DEVICE;
case ASSET:
return EdgeEventType.ASSET;
case ENTITY_VIEW:
return EdgeEventType.ENTITY_VIEW;
default:
return null;
}
}
@Override
public ListenableFuture<Void> processRelationRequestMsg(Edge edge, RelationRequestMsg relationRequestMsg) {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(
EntityType.valueOf(relationRequestMsg.getEntityType()),
new UUID(relationRequestMsg.getEntityIdMSB(), relationRequestMsg.getEntityIdLSB()));
List<ListenableFuture<List<EntityRelation>>> futures = new ArrayList<>();
futures.add(findRelationByQuery(edge, entityId, EntitySearchDirection.FROM));
futures.add(findRelationByQuery(edge, entityId, EntitySearchDirection.TO));
ListenableFuture<List<List<EntityRelation>>> relationsListFuture = Futures.allAsList(futures);
return Futures.transform(relationsListFuture, relationsList -> {
try {
if (relationsList != null && !relationsList.isEmpty()) {
for (List<EntityRelation> entityRelations : relationsList) {
log.trace("[{}] [{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), entityId, entityRelations.size());
for (EntityRelation relation : entityRelations) {
try {
RelationUpdateMsg relationUpdateMsg =
relationUpdateMsgConstructor.constructRelationUpdatedMsg(
UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE,
relation);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setRelationUpdateMsg(relationUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
!relation.getTo().getEntityType().equals(EntityType.EDGE)) {
saveEdgeEvent(edge.getTenantId(),
edge.getId(),
EdgeEventType.RELATION,
ActionType.ADDED,
null,
mapper.valueToTree(relation));
}
} catch (Exception e) {
log.error("Exception during loading relation [{}] to edge on sync!", relation, e);
}
}
}
} catch (Exception e) {
log.error("Exception during loading relation(s) to edge on sync!", e);
}
return null;
}, ctx.getDbCallbackExecutor());
} else {
return Futures.immediateFuture(null);
}
} catch (Exception e) {
log.error("Exception during loading relation(s) to edge on sync!", e);
throw new RuntimeException("Exception during loading relation(s) to edge on sync!", e);
}
return null;
}, dbCallbackExecutorService);
}
private ListenableFuture<List<EntityRelation>> syncRelations(Edge edge, EntityId entityId, EntitySearchDirection direction) {
private ListenableFuture<List<EntityRelation>> findRelationByQuery(Edge edge, EntityId entityId, EntitySearchDirection direction) {
EntityRelationsQuery query = new EntityRelationsQuery();
query.setParameters(new RelationsSearchParameters(entityId, direction, -1, false));
return relationService.findByQuery(edge.getTenantId(), query);
}
@Override
public void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver<ResponseMsg> outputStream) {
if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) {
RuleChainId ruleChainId = new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB()));
RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(edge.getTenantId(), ruleChainId);
RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg =
ruleChainUpdateMsgConstructor.constructRuleChainMetadataUpdatedMsg(
UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE,
ruleChainMetaData);
if (ruleChainMetadataUpdateMsg != null) {
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setRuleChainMetadataUpdateMsg(ruleChainMetadataUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
}
public ListenableFuture<Void> processDeviceCredentialsRequestMsg(Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg) {
SettableFuture<Void> futureToSet = SettableFuture.create();
if (deviceCredentialsRequestMsg.getDeviceIdMSB() != 0 && deviceCredentialsRequestMsg.getDeviceIdLSB() != 0) {
DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsRequestMsg.getDeviceIdMSB(), deviceCredentialsRequestMsg.getDeviceIdLSB()));
ListenableFuture<EdgeEvent> future = saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_UPDATED, deviceId, null);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't save edge event [{}]", deviceCredentialsRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
return futureToSet;
}
@Override
public ListenableFuture<Void> processUserCredentialsRequestMsg(Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg) {
SettableFuture<Void> futureToSet = SettableFuture.create();
if (userCredentialsRequestMsg.getUserIdMSB() != 0 && userCredentialsRequestMsg.getUserIdLSB() != 0) {
UserId userId = new UserId(new UUID(userCredentialsRequestMsg.getUserIdMSB(), userCredentialsRequestMsg.getUserIdLSB()));
ListenableFuture<EdgeEvent> future = saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, ActionType.CREDENTIALS_UPDATED, userId, null);
Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
@Override
public void onSuccess(@Nullable EdgeEvent result) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't save edge event [{}]", userCredentialsRequestMsg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
}
return futureToSet;
}
private ListenableFuture<EdgeEvent> saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
ActionType action,
EntityId entityId,
JsonNode body) {
log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
EdgeEvent edgeEvent = new EdgeEvent();
edgeEvent.setTenantId(tenantId);
edgeEvent.setEdgeId(edgeId);
edgeEvent.setType(type);
edgeEvent.setAction(action.name());
if (entityId != null) {
edgeEvent.setEntityId(entityId.getId());
}
edgeEvent.setBody(body);
return edgeEventService.saveAsync(edgeEvent);
}
}

20
application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java

@ -15,15 +15,25 @@
*/
package org.thingsboard.server.service.edge.rpc.init;
import io.grpc.stub.StreamObserver;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.gen.edge.ResponseMsg;
import org.thingsboard.server.gen.edge.AttributesRequestMsg;
import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.RelationRequestMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg;
import org.thingsboard.server.service.edge.EdgeContextComponent;
import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg;
public interface SyncEdgeService {
void sync(EdgeContextComponent ctx, Edge edge, StreamObserver<ResponseMsg> outputStream);
void sync(Edge edge);
void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver<ResponseMsg> outputStream);
ListenableFuture<Void> processRuleChainMetadataRequestMsg(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg);
ListenableFuture<Void> processAttributesRequestMsg(Edge edge, AttributesRequestMsg attributesRequestMsg);
ListenableFuture<Void> processRelationRequestMsg(Edge edge, RelationRequestMsg relationRequestMsg);
ListenableFuture<Void> processDeviceCredentialsRequestMsg(Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg);
ListenableFuture<Void> processUserCredentialsRequestMsg(Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg);
}

98
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/AlarmProcessor.java

@ -0,0 +1,98 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.AlarmUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
@Component
@Slf4j
@TbCoreComponent
public class AlarmProcessor extends BaseProcessor {
public ListenableFuture<Void> onAlarmUpdate(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg) {
EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(),
EntityType.valueOf(alarmUpdateMsg.getOriginatorType()));
if (originatorId == null) {
return Futures.immediateFuture(null);
}
try {
Alarm existentAlarm = alarmService.findLatestByOriginatorAndType(tenantId, originatorId, alarmUpdateMsg.getType()).get();
switch (alarmUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE:
if (existentAlarm == null || existentAlarm.getStatus().isCleared()) {
existentAlarm = new Alarm();
existentAlarm.setTenantId(tenantId);
existentAlarm.setType(alarmUpdateMsg.getName());
existentAlarm.setOriginator(originatorId);
existentAlarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity()));
existentAlarm.setStartTs(alarmUpdateMsg.getStartTs());
existentAlarm.setClearTs(alarmUpdateMsg.getClearTs());
existentAlarm.setPropagate(alarmUpdateMsg.getPropagate());
}
existentAlarm.setStatus(AlarmStatus.valueOf(alarmUpdateMsg.getStatus()));
existentAlarm.setAckTs(alarmUpdateMsg.getAckTs());
existentAlarm.setEndTs(alarmUpdateMsg.getEndTs());
existentAlarm.setDetails(mapper.readTree(alarmUpdateMsg.getDetails()));
alarmService.createOrUpdateAlarm(existentAlarm);
break;
case ALARM_ACK_RPC_MESSAGE:
if (existentAlarm != null) {
alarmService.ackAlarm(tenantId, existentAlarm.getId(), alarmUpdateMsg.getAckTs());
}
break;
case ALARM_CLEAR_RPC_MESSAGE:
if (existentAlarm != null) {
alarmService.clearAlarm(tenantId, existentAlarm.getId(), mapper.readTree(alarmUpdateMsg.getDetails()), alarmUpdateMsg.getAckTs());
}
break;
case ENTITY_DELETED_RPC_MESSAGE:
if (existentAlarm != null) {
alarmService.deleteAlarm(tenantId, existentAlarm.getId());
}
break;
}
return Futures.immediateFuture(null);
} catch (Exception e) {
log.error("Failed to process alarm update msg [{}]", alarmUpdateMsg, e);
return Futures.immediateFailedFuture(new RuntimeException("Failed to process alarm update msg", e));
}
}
private EntityId getAlarmOriginator(TenantId tenantId, String entityName, EntityType entityType) {
switch (entityType) {
case DEVICE:
return deviceService.findDeviceByTenantIdAndName(tenantId, entityName).getId();
case ASSET:
return assetService.findAssetByTenantIdAndName(tenantId, entityName).getId();
case ENTITY_VIEW:
return entityViewService.findEntityViewByTenantIdAndName(tenantId, entityName).getId();
default:
return null;
}
}
}

113
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseProcessor.java

@ -0,0 +1,113 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
import org.thingsboard.server.service.state.DeviceStateService;
@Slf4j
public abstract class BaseProcessor {
protected static final ObjectMapper mapper = new ObjectMapper();
@Autowired
protected AlarmService alarmService;
@Autowired
protected DeviceService deviceService;
@Autowired
protected DashboardService dashboardService;
@Autowired
protected AssetService assetService;
@Autowired
protected EntityViewService entityViewService;
@Autowired
protected CustomerService customerService;
@Autowired
protected UserService userService;
@Autowired
protected RelationService relationService;
@Autowired
protected DeviceCredentialsService deviceCredentialsService;
@Autowired
protected AttributesService attributesService;
@Autowired
protected TbClusterService tbClusterService;
@Autowired
protected DeviceStateService deviceStateService;
@Autowired
protected EdgeEventService edgeEventService;
@Autowired
protected DbCallbackExecutorService dbCallbackExecutorService;
protected ListenableFuture<EdgeEvent> saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType type,
ActionType action,
EntityId entityId,
JsonNode body) {
log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], " +
"action [{}], entityId [{}], body [{}]",
tenantId, edgeId, type, action, entityId, body);
EdgeEvent edgeEvent = new EdgeEvent();
edgeEvent.setTenantId(tenantId);
edgeEvent.setEdgeId(edgeId);
edgeEvent.setType(type);
edgeEvent.setAction(action.name());
if (entityId != null) {
edgeEvent.setEntityId(entityId.getId());
}
edgeEvent.setBody(body);
return edgeEventService.saveAsync(edgeEvent);
}
}

252
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/DeviceProcessor.java

@ -0,0 +1,252 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock;
@Component
@Slf4j
@TbCoreComponent
public class DeviceProcessor extends BaseProcessor {
private static final ReentrantLock deviceCreationLock = new ReentrantLock();
public ListenableFuture<Void> onDeviceUpdate(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
DeviceId edgeDeviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
switch (deviceUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
String deviceName = deviceUpdateMsg.getName();
Device device = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName);
if (device != null) {
// device with this name already exists on the cloud - update ID on the edge
if (!device.getId().equals(edgeDeviceId)) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, ActionType.ENTITY_EXISTS_REQUEST, device.getId(), null);
}
} else {
Device deviceById = deviceService.findDeviceById(edge.getTenantId(), edgeDeviceId);
if (deviceById != null) {
// this ID already used by other device - create new device and update ID on the edge
device = createDevice(tenantId, edge, deviceUpdateMsg);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, ActionType.ENTITY_EXISTS_REQUEST, device.getId(), null);
} else {
device = createDevice(tenantId, edge, deviceUpdateMsg);
}
}
// TODO: voba - assign device only in case device is not assigned yet. Missing functionality to check this relation prior assignment
deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId());
break;
case ENTITY_UPDATED_RPC_MESSAGE:
updateDevice(tenantId, edge, deviceUpdateMsg);
break;
case ENTITY_DELETED_RPC_MESSAGE:
Device deviceToDelete = deviceService.findDeviceById(tenantId, edgeDeviceId);
if (deviceToDelete != null) {
deviceService.unassignDeviceFromEdge(tenantId, edgeDeviceId, edge.getId());
}
break;
case UNRECOGNIZED:
log.error("Unsupported msg type {}", deviceUpdateMsg.getMsgType());
return Futures.immediateFailedFuture(new RuntimeException("Unsupported msg type " + deviceUpdateMsg.getMsgType()));
}
return Futures.immediateFuture(null);
}
public ListenableFuture<Void> onDeviceCredentialsUpdate(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
log.debug("Executing onDeviceCredentialsUpdate, deviceCredentialsUpdateMsg [{}]", deviceCredentialsUpdateMsg);
DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB()));
ListenableFuture<Device> deviceFuture = deviceService.findDeviceByIdAsync(tenantId, deviceId);
return Futures.transform(deviceFuture, device -> {
if (device != null) {
log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]",
device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue());
try {
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType()));
deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId());
deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.getCredentialsValue());
deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials);
} catch (Exception e) {
log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", device.getName(), deviceCredentialsUpdateMsg, e);
throw new RuntimeException(e);
}
}
return null;
}, dbCallbackExecutorService);
}
private void updateDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
Device device = deviceService.findDeviceById(tenantId, deviceId);
device.setName(deviceUpdateMsg.getName());
device.setType(deviceUpdateMsg.getType());
device.setLabel(deviceUpdateMsg.getLabel());
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()));
deviceService.saveDevice(device);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_REQUEST, deviceId, null);
}
private Device createDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) {
Device device;
try {
deviceCreationLock.lock();
DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB()));
device = new Device();
device.setTenantId(edge.getTenantId());
device.setCustomerId(edge.getCustomerId());
device.setId(deviceId);
device.setName(deviceUpdateMsg.getName());
device.setType(deviceUpdateMsg.getType());
device.setLabel(deviceUpdateMsg.getLabel());
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo()));
device = deviceService.saveDevice(device);
createDeviceCredentials(device);
createRelationFromEdge(tenantId, edge.getId(), device.getId());
deviceStateService.onDeviceAdded(device);
pushDeviceCreatedEventToRuleEngine(tenantId, edge, device);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_REQUEST, deviceId, null);
} finally {
deviceCreationLock.unlock();
}
return device;
}
private void createRelationFromEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId) {
EntityRelation relation = new EntityRelation();
relation.setFrom(edgeId);
relation.setTo(entityId);
relation.setTypeGroup(RelationTypeGroup.COMMON);
relation.setType(EntityRelation.EDGE_TYPE);
relationService.saveRelation(tenantId, relation);
}
private void createDeviceCredentials(Device device) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(device.getId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
}
private void pushDeviceCreatedEventToRuleEngine(TenantId tenantId, Edge edge, Device device) {
try {
DeviceId deviceId = device.getId();
ObjectNode entityNode = mapper.valueToTree(device);
TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId,
getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, mapper.writeValueAsString(entityNode));
tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
// TODO: voba - handle success
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", device);
}
@Override
public void onFailure(Throwable t) {
// TODO: voba - handle failure
log.debug("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", device, t);
}
;
});
} catch (JsonProcessingException | IllegalArgumentException e) {
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e);
}
}
private TbMsgMetaData getActionTbMsgMetaData(Edge edge, CustomerId customerId) {
TbMsgMetaData metaData = getTbMsgMetaData(edge);
if (customerId != null && !customerId.isNullUid()) {
metaData.putValue("customerId", customerId.toString());
}
return metaData;
}
private TbMsgMetaData getTbMsgMetaData(Edge edge) {
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("edgeId", edge.getId().toString());
metaData.putValue("edgeName", edge.getName());
return metaData;
}
public ListenableFuture<Void> processDeviceRpcCallResponseMsg(TenantId tenantId, DeviceRpcCallMsg deviceRpcCallMsg) {
SettableFuture<Void> futureToSet = SettableFuture.create();
UUID uuid = new UUID(deviceRpcCallMsg.getRequestIdMSB(), deviceRpcCallMsg.getRequestIdLSB());
FromDeviceRpcResponse response;
if (!StringUtils.isEmpty(deviceRpcCallMsg.getResponseMsg().getError())) {
response = new FromDeviceRpcResponse(uuid, null, RpcError.valueOf(deviceRpcCallMsg.getResponseMsg().getError()));
} else {
response = new FromDeviceRpcResponse(uuid, deviceRpcCallMsg.getResponseMsg().getResponse(), null);
}
TbQueueCallback callback = new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't process push notification to core [{}]", deviceRpcCallMsg, t);
futureToSet.setException(t);
}
};
tbClusterService.pushNotificationToCore(deviceRpcCallMsg.getOriginServiceId(), response, callback);
return futureToSet;
}
}

104
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/RelationProcessor.java

@ -0,0 +1,104 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.gen.edge.RelationUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID;
@Component
@Slf4j
@TbCoreComponent
public class RelationProcessor extends BaseProcessor {
public ListenableFuture<Void> onRelationUpdate(TenantId tenantId, RelationUpdateMsg relationUpdateMsg) {
log.info("onRelationUpdate {}", relationUpdateMsg);
try {
EntityRelation entityRelation = new EntityRelation();
UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB());
EntityId fromId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getFromEntityType()), fromUUID);
entityRelation.setFrom(fromId);
UUID toUUID = new UUID(relationUpdateMsg.getToIdMSB(), relationUpdateMsg.getToIdLSB());
EntityId toId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getToEntityType()), toUUID);
entityRelation.setTo(toId);
entityRelation.setType(relationUpdateMsg.getType());
entityRelation.setTypeGroup(RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup()));
entityRelation.setAdditionalInfo(mapper.readTree(relationUpdateMsg.getAdditionalInfo()));
switch (relationUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE:
case ENTITY_UPDATED_RPC_MESSAGE:
if (isEntityExists(tenantId, entityRelation.getTo())
&& isEntityExists(tenantId, entityRelation.getFrom())) {
relationService.saveRelationAsync(tenantId, entityRelation);
}
break;
case ENTITY_DELETED_RPC_MESSAGE:
relationService.deleteRelation(tenantId, entityRelation);
break;
case UNRECOGNIZED:
log.error("Unsupported msg type");
}
return Futures.immediateFuture(null);
} catch (Exception e) {
log.error("Failed to process relation update msg [{}]", relationUpdateMsg, e);
return Futures.immediateFailedFuture(new RuntimeException("Failed to process relation update msg", e));
}
}
private boolean isEntityExists(TenantId tenantId, EntityId entityId) throws ThingsboardException {
switch (entityId.getEntityType()) {
case DEVICE:
return deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null;
case ASSET:
return assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null;
case ENTITY_VIEW:
return entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null;
case CUSTOMER:
return customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null;
case USER:
return userService.findUserById(tenantId, new UserId(entityId.getId())) != null;
case DASHBOARD:
return dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null;
default:
throw new ThingsboardException("Unsupported entity type " + entityId.getEntityType(), ThingsboardErrorCode.INVALID_ARGUMENTS);
}
}
}

244
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/TelemetryProcessor.java

@ -0,0 +1,244 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.common.transport.util.JsonUtils;
import org.thingsboard.server.gen.edge.AttributeDeleteMsg;
import org.thingsboard.server.gen.edge.EntityDataProto;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.TbQueueCallback;
import org.thingsboard.server.queue.TbQueueMsgMetadata;
import org.thingsboard.server.queue.util.TbCoreComponent;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@Component
@Slf4j
@TbCoreComponent
public class TelemetryProcessor extends BaseProcessor {
private final Gson gson = new Gson();
public List<ListenableFuture<Void>> onTelemetryUpdate(TenantId tenantId, EntityDataProto entityData) {
List<ListenableFuture<Void>> result = new ArrayList<>();
EntityId entityId = constructEntityId(entityData);
if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg() || entityData.hasAttributesUpdatedMsg()) && entityId != null) {
TbMsgMetaData metaData = constructBaseMsgMetadata(tenantId, entityId);
metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE);
if (entityData.hasPostAttributesMsg()) {
result.add(processPostAttributes(tenantId, entityId, entityData.getPostAttributesMsg(), metaData));
}
if (entityData.hasAttributesUpdatedMsg()) {
metaData.putValue("scope", entityData.getPostAttributeScope());
result.add(processAttributesUpdate(tenantId, entityId, entityData.getAttributesUpdatedMsg(), metaData));
}
if (entityData.hasPostTelemetryMsg()) {
result.add(processPostTelemetry(tenantId, entityId, entityData.getPostTelemetryMsg(), metaData));
}
}
if (entityData.hasAttributeDeleteMsg()) {
result.add(processAttributeDeleteMsg(tenantId, entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType()));
}
return result;
}
private TbMsgMetaData constructBaseMsgMetadata(TenantId tenantId, EntityId entityId) {
TbMsgMetaData metaData = new TbMsgMetaData();
switch (entityId.getEntityType()) {
case DEVICE:
Device device = deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId()));
if (device != null) {
metaData.putValue("deviceName", device.getName());
metaData.putValue("deviceType", device.getType());
}
break;
case ASSET:
Asset asset = assetService.findAssetById(tenantId, new AssetId(entityId.getId()));
if (asset != null) {
metaData.putValue("assetName", asset.getName());
metaData.putValue("assetType", asset.getType());
}
break;
case ENTITY_VIEW:
EntityView entityView = entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId()));
if (entityView != null) {
metaData.putValue("entityViewName", entityView.getName());
metaData.putValue("entityViewType", entityView.getType());
}
break;
default:
log.debug("Using empty metadata for entityId [{}]", entityId);
break;
}
return metaData;
}
private ListenableFuture<Void> processPostTelemetry(TenantId tenantId, EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) {
SettableFuture<Void> futureToSet = SettableFuture.create();
for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) {
JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList());
metaData.putValue("ts", tsKv.getTs() + "");
TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, metaData, gson.toJson(json));
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't process post telemetry [{}]", msg, t);
futureToSet.setException(t);
}
});
}
return futureToSet;
}
private ListenableFuture<Void> processPostAttributes(TenantId tenantId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
SettableFuture<Void> futureToSet = SettableFuture.create();
JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json));
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't process post attributes [{}]", msg, t);
futureToSet.setException(t);
}
});
return futureToSet;
}
private ListenableFuture<Void> processAttributesUpdate(TenantId tenantId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) {
SettableFuture<Void> futureToSet = SettableFuture.create();
JsonObject json = JsonUtils.getJsonObject(msg.getKvList());
Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(json);
ListenableFuture<List<Void>> future = attributesService.save(tenantId, entityId, metaData.getValue("scope"), new ArrayList<>(attributes));
Futures.addCallback(future, new FutureCallback<List<Void>>() {
@Override
public void onSuccess(@Nullable List<Void> voids) {
TbMsg tbMsg = TbMsg.newMsg(DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, gson.toJson(json));
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't process attributes update [{}]", msg, t);
futureToSet.setException(t);
}
});
}
@Override
public void onFailure(Throwable t) {
log.error("Can't process attributes update [{}]", msg, t);
futureToSet.setException(t);
}
}, dbCallbackExecutorService);
return futureToSet;
}
private ListenableFuture<Void> processAttributeDeleteMsg(TenantId tenantId, EntityId entityId, AttributeDeleteMsg attributeDeleteMsg, String entityType) {
SettableFuture<Void> futureToSet = SettableFuture.create();
String scope = attributeDeleteMsg.getScope();
List<String> attributeNames = attributeDeleteMsg.getAttributeNamesList();
attributesService.removeAll(tenantId, entityId, scope, attributeNames);
if (EntityType.DEVICE.name().equals(entityType)) {
Set<AttributeKey> attributeKeys = new HashSet<>();
for (String attributeName : attributeNames) {
attributeKeys.add(new AttributeKey(scope, attributeName));
}
tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(
tenantId, (DeviceId) entityId, attributeKeys), new TbQueueCallback() {
@Override
public void onSuccess(TbQueueMsgMetadata metadata) {
futureToSet.set(null);
}
@Override
public void onFailure(Throwable t) {
log.error("Can't process attribute delete msg [{}]", attributeDeleteMsg, t);
futureToSet.setException(t);
}
});
}
return futureToSet;
}
private EntityId constructEntityId(EntityDataProto entityData) {
EntityType entityType = EntityType.valueOf(entityData.getEntityType());
switch (entityType) {
case DEVICE:
return new DeviceId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB()));
case ASSET:
return new AssetId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB()));
case ENTITY_VIEW:
return new EntityViewId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB()));
case DASHBOARD:
return new DashboardId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB()));
case TENANT:
return new TenantId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB()));
case CUSTOMER:
return new CustomerId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB()));
case USER:
return new UserId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB()));
default:
log.warn("Unsupported entity type [{}] during construct of entity id. EntityDataProto [{}]", entityData.getEntityType(), entityData);
return null;
}
}
}

2
application/src/main/java/org/thingsboard/server/service/install/AbstractSqlTsDatabaseUpgradeService.java

@ -50,7 +50,7 @@ public abstract class AbstractSqlTsDatabaseUpgradeService {
@Autowired
protected InstallScripts installScripts;
protected abstract void loadSql(Connection conn, String fileName);
protected abstract void loadSql(Connection conn, String version, String fileName);
protected void loadFunctions(Path sqlFile, Connection conn) throws Exception {
String sql = new String(Files.readAllBytes(sqlFile), StandardCharsets.UTF_8);

2
application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java

@ -49,6 +49,8 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase
log.info("Schema updated.");
break;
case "2.5.0":
case "2.5.4":
case "2.5.5":
case "3.1.1":
break;
default:

1
application/src/main/java/org/thingsboard/server/service/install/DatabaseHelper.java

@ -57,6 +57,7 @@ public class DatabaseHelper {
public static final String DASHBOARD = "dashboard";
public static final String ENTITY_VIEWS = "entity_views";
public static final String ENTITY_VIEW = "entity_view";
public static final String RULE_CHAIN = "rule_chain";
public static final String ID = "id";
public static final String TITLE = "title";
public static final String TYPE = "type";

9
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -122,10 +122,6 @@ public class InstallScripts {
}
}
);
// TODO: voba
// dirStream.forEach(
// path -> loadRuleChainFromFile(tenantId, path)
// );
}
}
@ -151,6 +147,11 @@ public class InstallScripts {
}
public void createDefaultEdgeRuleChains(TenantId tenantId) {
Path tenantChainsDir = getTenantRuleChainsDir();
loadRuleChainFromFile(tenantId, tenantChainsDir.resolve("edge_root_rule_chain.json"));
}
public void loadSystemWidgets() throws Exception {
Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) {

20
application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java

@ -94,7 +94,7 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
log.info("PostgreSQL version is valid!");
if (isOldSchema(conn, 2004003)) {
log.info("Load upgrade functions ...");
loadSql(conn, LOAD_FUNCTIONS_SQL);
loadSql(conn, "2.4.3", LOAD_FUNCTIONS_SQL);
log.info("Updating timeseries schema ...");
executeQuery(conn, CALL_CREATE_PARTITION_TS_KV_TABLE);
if (!partitionType.equals("INDEFINITE")) {
@ -179,9 +179,9 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
}
log.info("Load TTL functions ...");
loadSql(conn, LOAD_TTL_FUNCTIONS_SQL);
loadSql(conn, "2.4.3", LOAD_TTL_FUNCTIONS_SQL);
log.info("Load Drop Partitions functions ...");
loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL);
loadSql(conn, "2.4.3", LOAD_DROP_PARTITIONS_FUNCTIONS_SQL);
executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005000");
@ -195,12 +195,18 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001");
}
break;
case "2.5.5":
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
log.info("Load TTL functions ...");
loadSql(conn, "2.6.0", LOAD_TTL_FUNCTIONS_SQL);
}
break;
case "3.1.1":
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
log.info("Load TTL functions ...");
loadSql(conn, LOAD_TTL_FUNCTIONS_SQL);
loadSql(conn, "2.4.3", LOAD_TTL_FUNCTIONS_SQL);
log.info("Load Drop Partitions functions ...");
loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL);
loadSql(conn, "2.4.3", LOAD_DROP_PARTITIONS_FUNCTIONS_SQL);
}
break;
default:
@ -238,8 +244,8 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
}
@Override
protected void loadSql(Connection conn, String fileName) {
Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.3", fileName);
protected void loadSql(Connection conn, String version, String fileName) {
Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", version, fileName);
try {
loadFunctions(schemaUpdateFile, conn);
log.info("Functions successfully loaded!");

2
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -260,7 +260,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
log.info("Schema updated.");
}
break;
case "2.6.0":
case "2.5.5":
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.5.0", SCHEMA_UPDATE_SQL);

10
application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java

@ -89,7 +89,7 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
log.info("PostgreSQL version is valid!");
if (isOldSchema(conn, 2004003)) {
log.info("Load upgrade functions ...");
loadSql(conn, LOAD_FUNCTIONS_SQL);
loadSql(conn, "2.4.3", LOAD_FUNCTIONS_SQL);
log.info("Updating timescale schema ...");
executeQuery(conn, CALL_CREATE_TS_KV_LATEST_TABLE);
executeQuery(conn, CALL_CREATE_NEW_TENANT_TS_KV_TABLE);
@ -165,7 +165,7 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
}
log.info("Load TTL functions ...");
loadSql(conn, LOAD_TTL_FUNCTIONS_SQL);
loadSql(conn, "2.4.3", LOAD_TTL_FUNCTIONS_SQL);
executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005000");
log.info("schema timescale updated!");
@ -179,6 +179,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
break;
case "3.1.1":
break;
case "2.5.5":
break;
default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
}
@ -200,8 +202,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
}
@Override
protected void loadSql(Connection conn, String fileName) {
Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.3", fileName);
protected void loadSql(Connection conn, String version, String fileName) {
Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", version, fileName);
try {
loadFunctions(schemaUpdateFile, conn);
log.info("Functions successfully loaded!");

5
application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java

@ -37,7 +37,6 @@ import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
@ -159,6 +158,8 @@ public class CassandraDbHelper {
str = new Float(row.getFloat(index)).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) {
str = ""+row.getInstant(index).toEpochMilli();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) {
str = new Boolean(row.getBoolean(index)).toString();
} else {
str = row.getString(index);
}
@ -207,6 +208,8 @@ public class CassandraDbHelper {
boundStatementBuilder.setFloat(column, Float.valueOf(value));
} else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) {
boundStatementBuilder.setInstant(column, Instant.ofEpochMilli(Long.valueOf(value)));
} else if (type.getProtocolCode() == ProtocolConstants.DataType.BOOLEAN) {
boundStatementBuilder.setBoolean(column, Boolean.valueOf(value));
} else {
boundStatementBuilder.setString(column, value);
}

33
application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java

@ -15,9 +15,7 @@
*/
package org.thingsboard.server.service.install.update;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
@ -25,16 +23,12 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.TsKvEntry;
@ -50,7 +44,6 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.service.install.InstallScripts;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -58,7 +51,6 @@ import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper;
@Service
@Profile("install")
@ -87,6 +79,10 @@ public class DefaultDataUpdateService implements DataUpdateService {
log.info("Updating data from version 1.4.0 to 2.0.0 ...");
tenantsDefaultRuleChainUpdater.updateEntities(null);
break;
case "2.5.5":
log.info("Updating data from version 2.5.5 to 2.6.0 ...");
tenantsDefaultEdgeRuleChainUpdater.updateEntities(null);
break;
case "3.0.1":
log.info("Updating data from version 3.0.1 to 3.1.0 ...");
tenantsEntityViewsUpdater.updateEntities(null);
@ -121,6 +117,27 @@ public class DefaultDataUpdateService implements DataUpdateService {
}
};
private PaginatedUpdater<String, Tenant> tenantsDefaultEdgeRuleChainUpdater =
new PaginatedUpdater<String, Tenant>() {
@Override
protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
return tenantService.findTenants(pageLink);
}
@Override
protected void updateEntity(Tenant tenant) {
try {
RuleChain defaultEdgeRuleChain = ruleChainService.getDefaultRootEdgeRuleChain(tenant.getId());
if (defaultEdgeRuleChain == null) {
installScripts.createDefaultEdgeRuleChains(tenant.getId());
}
} catch (Exception e) {
log.error("Unable to update Tenant", e);
}
}
};
private PaginatedUpdater<String, Tenant> tenantsRootRuleChainUpdater =
new PaginatedUpdater<String, Tenant>() {

11
application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java

@ -35,9 +35,9 @@ public class TbCoreConsumerStats {
public static final String SUBSCRIPTION_INFO = "subInfo";
public static final String DEVICE_CLAIMS = "claimDevice";
public static final String DEVICE_STATES = "deviceState";
public static final String EDGE_EVENTS = "edgeEvents";
public static final String SUBSCRIPTION_MSGS = "subMsgs";
public static final String TO_CORE_NOTIFICATIONS = "coreNfs";
public static final String EDGE_NOTIFICATIONS = "edgeNfs";
private final StatsCounter totalCounter;
private final StatsCounter sessionEventCounter;
@ -49,9 +49,9 @@ public class TbCoreConsumerStats {
private final StatsCounter claimDeviceCounter;
private final StatsCounter deviceStateCounter;
private final StatsCounter edgeNotificationCounter;
private final StatsCounter subscriptionMsgCounter;
private final StatsCounter toCoreNotificationsCounter;
private final StatsCounter edgeNotificationsCounter;
private final List<StatsCounter> counters = new ArrayList<>();
@ -67,10 +67,9 @@ public class TbCoreConsumerStats {
this.subscriptionInfoCounter = statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_INFO);
this.claimDeviceCounter = statsFactory.createStatsCounter(statsKey, DEVICE_CLAIMS);
this.deviceStateCounter = statsFactory.createStatsCounter(statsKey, DEVICE_STATES);
this.edgeNotificationCounter = statsFactory.createStatsCounter(statsKey, EDGE_EVENTS);
this.subscriptionMsgCounter = statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_MSGS);
this.toCoreNotificationsCounter = statsFactory.createStatsCounter(statsKey, TO_CORE_NOTIFICATIONS);
this.edgeNotificationsCounter = statsFactory.createStatsCounter(statsKey, EDGE_NOTIFICATIONS);
counters.add(totalCounter);
counters.add(sessionEventCounter);
@ -82,9 +81,9 @@ public class TbCoreConsumerStats {
counters.add(claimDeviceCounter);
counters.add(deviceStateCounter);
counters.add(edgeNotificationCounter);
counters.add(subscriptionMsgCounter);
counters.add(toCoreNotificationsCounter);
counters.add(edgeNotificationsCounter);
}
public void log(TransportProtos.TransportToDeviceActorMsg msg) {
@ -119,7 +118,7 @@ public class TbCoreConsumerStats {
public void log(TransportProtos.EdgeNotificationMsgProto msg) {
totalCounter.increment();
edgeNotificationCounter.increment();
edgeNotificationsCounter.increment();
}
public void log(TransportProtos.SubscriptionMgrMsgProto msg) {

3
application/src/main/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcService.java

@ -151,7 +151,8 @@ public class DefaultTbRuleEngineRpcService implements TbRuleEngineDeviceRpcServi
}
}
private void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response) {
@Override
public void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response) {
if (serviceId.equals(originServiceId)) {
if (tbCoreRpcService.isPresent()) {
tbCoreRpcService.get().processRpcResponseFromRuleEngine(response);

9
application/src/main/java/org/thingsboard/server/service/rpc/TbRuleEngineDeviceRpcService.java

@ -29,4 +29,13 @@ public interface TbRuleEngineDeviceRpcService extends RuleEngineRpcService {
*/
void processRpcResponseFromDevice(FromDeviceRpcResponse response);
/**
* Sends Rpc response from the Device to TB Core.
*
* @param originServiceId Service ID of the origin component
* @param response the RPC response
*/
void sendRpcResponseToTbCore(String originServiceId, FromDeviceRpcResponse response);
}

2
application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java

@ -112,7 +112,7 @@ public class AccessValidator {
@Autowired
protected EntityViewService entityViewService;
@Autowired
@Autowired(required = false)
protected EdgeService edgeService;
@Autowired

56
application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java

@ -0,0 +1,56 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.ttl.edge;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.util.PsqlDao;
import org.thingsboard.server.service.ttl.AbstractCleanUpService;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@PsqlDao
@Slf4j
@Service
public class EdgeEventsCleanUpService extends AbstractCleanUpService {
@Value("${sql.ttl.edge_events.edge_events_ttl}")
private long ttl;
@Value("${sql.ttl.edge_events.enabled}")
private boolean ttlTaskExecutionEnabled;
@Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}")
public void cleanUp() {
if (ttlTaskExecutionEnabled) {
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
doCleanUp(conn);
} catch (SQLException e) {
log.error("SQLException occurred during TTL task execution ", e);
}
}
}
@Override
protected void doCleanUp(Connection connection) throws SQLException {
long totalEdgeEventsRemoved = executeQuery(connection, "call cleanup_edge_events_by_ttl(" + ttl + ", 0);");
log.info("Total edge events removed by TTL: [{}]", totalEdgeEventsRemoved);
}
}

12
application/src/main/resources/thingsboard.yml

@ -308,6 +308,10 @@ sql:
execution_interval_ms: "${SQL_TTL_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day
events_ttl: "${SQL_TTL_EVENTS_EVENTS_TTL:0}" # Number of seconds
debug_events_ttl: "${SQL_TTL_EVENTS_DEBUG_EVENTS_TTL:604800}" # Number of seconds. The current value corresponds to one week
edge_events:
enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}"
execution_interval_ms: "${SQL_TTL_EDGE_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day
edge_events_ttl: "${SQL_TTL_EDGE_EVENTS_TTL:2628000}" # Number of seconds. The current value corresponds to one month
# Actor system parameters
actors:
@ -625,8 +629,9 @@ transport:
# Edges parameters
edges:
rpc:
enabled: "${EDGES_RPC_ENABLED:true}"
port: "${EDGES_RPC_PORT:60100}"
enabled: "${EDGES_RPC_ENABLED:false}"
port: "${EDGES_RPC_PORT:7070}"
client_max_keep_alive_time_sec: "${EDGES_RPC_CLIENT_MAX_KEEP_ALIVE_TIME_SEC:300}"
ssl:
# Enable/disable SSL support
enabled: "${EDGES_RPC_SSL_ENABLED:false}"
@ -636,6 +641,9 @@ edges:
max_read_records_count: "${EDGES_RPC_STORAGE_MAX_READ_RECORDS_COUNT:50}"
no_read_records_sleep: "${EDGES_RPC_NO_READ_RECORDS_SLEEP:1000}"
sleep_between_batches: "${EDGES_RPC_SLEEP_BETWEEN_BATCHES:1000}"
edge_events_ttl: "${EDGES_EDGE_EVENTS_TTL:0}"
state:
persistToTelemetry: "${EDGES_PERSIST_STATE_TO_TELEMETRY:false}"
swagger:
api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}"

16
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -24,6 +24,7 @@ import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.hamcrest.Matcher;
import org.junit.After;
@ -68,6 +69,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
@ -543,4 +545,18 @@ public abstract class AbstractWebTest {
return jsonPath("$.message", matcher);
}
protected Edge constructEdge(String name, String type) {
return constructEdge(tenantId, name, type);
}
protected Edge constructEdge(TenantId tenantId, String name, String type) {
Edge edge = new Edge();
edge.setTenantId(tenantId);
edge.setName(name);
edge.setType(type);
edge.setSecret(RandomStringUtils.randomAlphanumeric(20));
edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20));
edge.setEdgeLicenseKey(RandomStringUtils.randomAlphanumeric(20));
edge.setCloudEndpoint("http://localhost:8080");
return edge;
}
}

27
application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -672,4 +673,30 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testAssignAssetToEdge() throws Exception {
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
doPost("/api/edge/" + savedEdge.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
PageData<Asset> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>() {}, new PageLink(100));
Assert.assertEquals(1, pageData.getData().size());
doDelete("/api/edge/" + savedEdge.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>() {}, new PageLink(100));
Assert.assertEquals(0, pageData.getData().size());
}
}

57
application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java

@ -15,27 +15,30 @@
*/
package org.thingsboard.server.controller;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import com.fasterxml.jackson.core.type.TypeReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public abstract class BaseDashboardControllerTest extends AbstractControllerTest {
@ -348,4 +351,30 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
Assert.assertEquals(dashboards, loadedDashboards);
}
@Test
public void testAssignDashboardToEdge() throws Exception {
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
Dashboard dashboard = new Dashboard();
dashboard.setTitle("My dashboard");
Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
doPost("/api/edge/" + savedEdge.getId().getId().toString()
+ "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class);
PageData<Dashboard> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/dashboards?",
new TypeReference<PageData<Dashboard>>() {}, new PageLink(100));
Assert.assertEquals(1, pageData.getData().size());
doDelete("/api/edge/" + savedEdge.getId().getId().toString()
+ "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class);
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/dashboards?",
new TypeReference<PageData<Dashboard>>() {}, new PageLink(100));
Assert.assertEquals(0, pageData.getData().size());
}
}

37
application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java

@ -24,10 +24,10 @@ import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceCredentialsId;
import org.thingsboard.server.common.data.id.DeviceId;
@ -463,7 +463,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -473,7 +473,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -669,7 +669,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -679,7 +679,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -827,4 +827,31 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testAssignDeviceToEdge() throws Exception {
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/edge/" + savedEdge.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString(), Device.class);
PageData<Device> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/devices?",
new TypeReference<PageData<Device>>() {}, new PageLink(100));
Assert.assertEquals(1, pageData.getData().size());
doDelete("/api/edge/" + savedEdge.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString(), Device.class);
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/devices?",
new TypeReference<PageData<Device>>() {}, new PageLink(100));
Assert.assertEquals(0, pageData.getData().size());
}
}

73
application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java

@ -18,14 +18,17 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
@ -33,6 +36,12 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.edge.imitator.EdgeImitator;
import org.thingsboard.server.gen.edge.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.UserUpdateMsg;
import java.util.ArrayList;
import java.util.Collections;
@ -90,6 +99,8 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
Assert.assertNotNull(savedEdge.getCustomerId());
Assert.assertEquals(NULL_UUID, savedEdge.getCustomerId().getId());
Assert.assertEquals(edge.getName(), savedEdge.getName());
Assert.assertTrue(StringUtils.isNoneBlank(savedEdge.getEdgeLicenseKey()));
Assert.assertTrue(StringUtils.isNoneBlank(savedEdge.getCloudEndpoint()));
savedEdge.setName("My new edge");
doPost("/api/edge", savedEdge, Edge.class);
@ -638,17 +649,57 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
Assert.assertEquals(0, pageData.getData().size());
}
private Edge constructEdge(String name, String type) {
return constructEdge(tenantId, name, type);
@Test
public void testSyncEdge() throws Exception {
Edge edge = doPost("/api/edge", constructEdge("Test Edge", "test"), Edge.class);
Device device = new Device();
device.setName("Edge Device 1");
device.setType("test");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/edge/" + edge.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString(), Device.class);
Asset asset = new Asset();
asset.setName("Edge Asset 1");
asset.setType("test");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
doPost("/api/edge/" + edge.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
EdgeImitator edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret());
edgeImitator.ignoreType(UserCredentialsUpdateMsg.class);
edgeImitator.expectMessageAmount(7);
edgeImitator.connect();
edgeImitator.waitForMessages();
Assert.assertEquals(7, edgeImitator.getDownlinkMsgs().size());
Assert.assertTrue(edgeImitator.findMessageByType(RuleChainUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(DeviceUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(AssetUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(UserUpdateMsg.class).isPresent());
edgeImitator.getDownlinkMsgs().clear();
edgeImitator.expectMessageAmount(4);
doPost("/api/edge/sync", edge.getId());
edgeImitator.waitForMessages();
Assert.assertEquals(4, edgeImitator.getDownlinkMsgs().size());
Assert.assertTrue(edgeImitator.findMessageByType(RuleChainUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(DeviceUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(AssetUpdateMsg.class).isPresent());
Assert.assertTrue(edgeImitator.findMessageByType(UserUpdateMsg.class).isPresent());
edgeImitator.allowIgnoredTypes();
edgeImitator.disconnect();
doDelete("/api/device/" + savedDevice.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/asset/" + savedAsset.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/edge/" + edge.getId().getId().toString())
.andExpect(status().isOk());
}
private Edge constructEdge(TenantId tenantId, String name, String type) {
Edge edge = new Edge();
edge.setTenantId(tenantId);
edge.setName(name);
edge.setType(type);
edge.setSecret(RandomStringUtils.randomAlphanumeric(20));
edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20));
return edge;
}
}

125
application/src/test/java/org/thingsboard/server/controller/BaseEdgeEventControllerTest.java

@ -0,0 +1,125 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.Authority;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@Slf4j
public class BaseEdgeEventControllerTest extends AbstractControllerTest {
private Tenant savedTenant;
private TenantId tenantId;
private User tenantAdmin;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
tenantId = savedTenant.getId();
Assert.assertNotNull(savedTenant);
tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
}
@After
public void afterTest() throws Exception {
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testGetEdgeEvents() throws Exception {
Thread.sleep(1000);
Edge edge = constructEdge("TestEdge", "default");
edge = doPost("/api/edge", edge, Edge.class);
Device device = constructDevice("TestDevice", "default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/edge/" + edge.getId().toString() + "/device/" + savedDevice.getId().toString(), Device.class);
Thread.sleep(1000);
Asset asset = constructAsset("TestAsset", "default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
doPost("/api/edge/" + edge.getId().toString() + "/asset/" + savedAsset.getId().toString(), Asset.class);
Thread.sleep(1000);
EntityRelation relation = new EntityRelation(savedAsset.getId(), savedDevice.getId(), EntityRelation.CONTAINS_TYPE);
doPost("/api/relation", relation);
Thread.sleep(1000);
List<EdgeEvent> edgeEvents = doGetTypedWithTimePageLink("/api/edge/" + edge.getId().toString() + "/events?",
new TypeReference<PageData<EdgeEvent>>() {
}, new TimePageLink(4)).getData();
Assert.assertFalse(edgeEvents.isEmpty());
Assert.assertEquals(4, edgeEvents.size());
Assert.assertEquals(EdgeEventType.RELATION, edgeEvents.get(0).getType());
Assert.assertEquals(EdgeEventType.ASSET, edgeEvents.get(1).getType());
Assert.assertEquals(EdgeEventType.DEVICE, edgeEvents.get(2).getType());
Assert.assertEquals(EdgeEventType.RULE_CHAIN, edgeEvents.get(3).getType());
}
private Device constructDevice(String name, String type) {
Device device = new Device();
device.setName(name);
device.setType(type);
return device;
}
private Asset constructAsset(String name, String type) {
Asset asset = new Asset();
asset.setName(name);
asset.setType(type);
return asset;
}
}

36
application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java

@ -27,7 +27,13 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.objects.AttributesEntityView;
import org.thingsboard.server.common.data.objects.TelemetryEntityView;
@ -49,7 +55,6 @@ import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@ -568,4 +573,31 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
return loadedItems;
}
@Test
public void testAssignEntityViewToEdge() throws Exception {
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
EntityView savedEntityView = getNewSavedEntityView("My entityView");
doPost("/api/edge/" + savedEdge.getId().getId().toString()
+ "/device/" + testDevice.getId().getId().toString(), Device.class);
doPost("/api/edge/" + savedEdge.getId().getId().toString()
+ "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class);
PageData<EntityView> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/entityViews?",
new TypeReference<PageData<EntityView>>() {}, new PageLink(100));
Assert.assertEquals(1, pageData.getData().size());
doDelete("/api/edge/" + savedEdge.getId().getId().toString()
+ "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class);
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/entityViews?",
new TypeReference<PageData<EntityView>>() {}, new PageLink(100));
Assert.assertEquals(0, pageData.getData().size());
}
}

23
application/src/test/java/org/thingsboard/server/controller/nosql/EdgeEventControllerNoSqlTest.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller.nosql;
import org.thingsboard.server.controller.BaseEdgeEventControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
@DaoNoSqlTest
public class EdgeEventControllerNoSqlTest extends BaseEdgeEventControllerTest {
}

23
application/src/test/java/org/thingsboard/server/controller/sql/EdgeEventControllerSqlTest.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller.sql;
import org.thingsboard.server.controller.BaseEdgeEventControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
@DaoSqlTest
public class EdgeEventControllerSqlTest extends BaseEdgeEventControllerTest {
}

1090
application/src/test/java/org/thingsboard/server/edge/BaseEdgeTest.java

File diff suppressed because it is too large

47
application/src/test/java/org/thingsboard/server/edge/EdgeNoSqlTestSuite.java

@ -0,0 +1,47 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.edge;
import org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomCassandraCQLUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.edge.nosql.*Test"})
public class EdgeNoSqlTestSuite {
@ClassRule
public static CustomCassandraCQLUnit cassandraUnit =
new CustomCassandraCQLUnit(
Arrays.asList(
new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
new ClassPathCQLDataSet("cassandra/schema-entities.cql", false, false),
new ClassPathCQLDataSet("cassandra/system-data.cql", false, false),
new ClassPathCQLDataSet("cassandra/system-test.cql", false, false)),
"cassandra-test.yaml", 30000l);
@BeforeClass
public static void cleanupInMemStorage(){
InMemoryStorage.getInstance().cleanup();
}
}

41
application/src/test/java/org/thingsboard/server/edge/EdgeSqlTestSuite.java

@ -0,0 +1,41 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.edge;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomSqlUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.edge.sql.*Test"})
public class EdgeSqlTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"),
"sql/hsql/drop-all-tables.sql",
"sql-test.properties");
@BeforeClass
public static void cleanupInMemStorage(){
InMemoryStorage.getInstance().cleanup();
}
}

268
application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java

@ -0,0 +1,268 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.edge.imitator;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.AbstractMessage;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.thingsboard.edge.rpc.EdgeGrpcClient;
import org.thingsboard.edge.rpc.EdgeRpcClient;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.gen.edge.AlarmUpdateMsg;
import org.thingsboard.server.gen.edge.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.CustomerUpdateMsg;
import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.DeviceUpdateMsg;
import org.thingsboard.server.gen.edge.DownlinkMsg;
import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
import org.thingsboard.server.gen.edge.EdgeConfiguration;
import org.thingsboard.server.gen.edge.EntityDataProto;
import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg;
import org.thingsboard.server.gen.edge.RuleChainUpdateMsg;
import org.thingsboard.server.gen.edge.UplinkMsg;
import org.thingsboard.server.gen.edge.UplinkResponseMsg;
import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.UserUpdateMsg;
import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg;
import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@Slf4j
public class EdgeImitator {
private String routingKey;
private String routingSecret;
private EdgeRpcClient edgeRpcClient;
private CountDownLatch messagesLatch;
private CountDownLatch responsesLatch;
private List<Class<? extends AbstractMessage>> ignoredTypes;
@Getter
private EdgeConfiguration configuration;
@Getter
private UserId userId;
@Getter
private List<AbstractMessage> downlinkMsgs;
public EdgeImitator(String host, int port, String routingKey, String routingSecret) throws NoSuchFieldException, IllegalAccessException {
edgeRpcClient = new EdgeGrpcClient();
messagesLatch = new CountDownLatch(0);
responsesLatch = new CountDownLatch(0);
downlinkMsgs = new ArrayList<>();
ignoredTypes = new ArrayList<>();
this.routingKey = routingKey;
this.routingSecret = routingSecret;
setEdgeCredentials("rpcHost", host);
setEdgeCredentials("rpcPort", port);
setEdgeCredentials("keepAliveTimeSec", 300);
}
private void setEdgeCredentials(String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
Field fieldToSet = edgeRpcClient.getClass().getDeclaredField(fieldName);
fieldToSet.setAccessible(true);
fieldToSet.set(edgeRpcClient, value);
fieldToSet.setAccessible(false);
}
public void connect() {
edgeRpcClient.connect(routingKey, routingSecret,
this::onUplinkResponse,
this::onEdgeUpdate,
this::onDownlink,
this::onClose);
edgeRpcClient.sendSyncRequestMsg();
}
public void disconnect() throws InterruptedException {
edgeRpcClient.disconnect(false);
}
public void sendUplinkMsg(UplinkMsg uplinkMsg) {
edgeRpcClient.sendUplinkMsg(uplinkMsg);
}
private void onUplinkResponse(UplinkResponseMsg msg) {
log.info("onUplinkResponse: {}", msg);
responsesLatch.countDown();
}
private void onEdgeUpdate(EdgeConfiguration edgeConfiguration) {
this.configuration = edgeConfiguration;
}
private void onUserUpdate(UserUpdateMsg userUpdateMsg) {
this.userId = new UserId(new UUID(userUpdateMsg.getIdMSB(), userUpdateMsg.getIdLSB()));
}
private void onDownlink(DownlinkMsg downlinkMsg) {
ListenableFuture<List<Void>> future = processDownlinkMsg(downlinkMsg);
Futures.addCallback(future, new FutureCallback<List<Void>>() {
@Override
public void onSuccess(@Nullable List<Void> result) {
DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder().setSuccess(true).build();
edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg);
}
@Override
public void onFailure(Throwable t) {
DownlinkResponseMsg downlinkResponseMsg = DownlinkResponseMsg.newBuilder().setSuccess(false).setErrorMsg(t.getMessage()).build();
edgeRpcClient.sendDownlinkResponseMsg(downlinkResponseMsg);
}
}, MoreExecutors.directExecutor());
}
private void onClose(Exception e) {
log.info("onClose: {}", e.getMessage());
}
private ListenableFuture<List<Void>> processDownlinkMsg(DownlinkMsg downlinkMsg) {
List<ListenableFuture<Void>> result = new ArrayList<>();
if (downlinkMsg.getDeviceUpdateMsgList() != null && !downlinkMsg.getDeviceUpdateMsgList().isEmpty()) {
for (DeviceUpdateMsg deviceUpdateMsg: downlinkMsg.getDeviceUpdateMsgList()) {
result.add(saveDownlinkMsg(deviceUpdateMsg));
}
}
if (downlinkMsg.getDeviceCredentialsUpdateMsgList() != null && !downlinkMsg.getDeviceCredentialsUpdateMsgList().isEmpty()) {
for (DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg: downlinkMsg.getDeviceCredentialsUpdateMsgList()) {
result.add(saveDownlinkMsg(deviceCredentialsUpdateMsg));
}
}
if (downlinkMsg.getAssetUpdateMsgList() != null && !downlinkMsg.getAssetUpdateMsgList().isEmpty()) {
for (AssetUpdateMsg assetUpdateMsg: downlinkMsg.getAssetUpdateMsgList()) {
result.add(saveDownlinkMsg(assetUpdateMsg));
}
}
if (downlinkMsg.getRuleChainUpdateMsgList() != null && !downlinkMsg.getRuleChainUpdateMsgList().isEmpty()) {
for (RuleChainUpdateMsg ruleChainUpdateMsg: downlinkMsg.getRuleChainUpdateMsgList()) {
result.add(saveDownlinkMsg(ruleChainUpdateMsg));
}
}
if (downlinkMsg.getRuleChainMetadataUpdateMsgList() != null && !downlinkMsg.getRuleChainMetadataUpdateMsgList().isEmpty()) {
for (RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg: downlinkMsg.getRuleChainMetadataUpdateMsgList()) {
result.add(saveDownlinkMsg(ruleChainMetadataUpdateMsg));
}
}
if (downlinkMsg.getDashboardUpdateMsgList() != null && !downlinkMsg.getDashboardUpdateMsgList().isEmpty()) {
for (DashboardUpdateMsg dashboardUpdateMsg: downlinkMsg.getDashboardUpdateMsgList()) {
result.add(saveDownlinkMsg(dashboardUpdateMsg));
}
}
if (downlinkMsg.getRelationUpdateMsgList() != null && !downlinkMsg.getRelationUpdateMsgList().isEmpty()) {
for (RelationUpdateMsg relationUpdateMsg: downlinkMsg.getRelationUpdateMsgList()) {
result.add(saveDownlinkMsg(relationUpdateMsg));
}
}
if (downlinkMsg.getAlarmUpdateMsgList() != null && !downlinkMsg.getAlarmUpdateMsgList().isEmpty()) {
for (AlarmUpdateMsg alarmUpdateMsg: downlinkMsg.getAlarmUpdateMsgList()) {
result.add(saveDownlinkMsg(alarmUpdateMsg));
}
}
if (downlinkMsg.getEntityDataList() != null && !downlinkMsg.getEntityDataList().isEmpty()) {
for (EntityDataProto entityData: downlinkMsg.getEntityDataList()) {
result.add(saveDownlinkMsg(entityData));
}
}
if (downlinkMsg.getEntityViewUpdateMsgList() != null && !downlinkMsg.getEntityViewUpdateMsgList().isEmpty()) {
for (EntityViewUpdateMsg entityViewUpdateMsg: downlinkMsg.getEntityViewUpdateMsgList()) {
result.add(saveDownlinkMsg(entityViewUpdateMsg));
}
}
if (downlinkMsg.getCustomerUpdateMsgList() != null && !downlinkMsg.getCustomerUpdateMsgList().isEmpty()) {
for (CustomerUpdateMsg customerUpdateMsg: downlinkMsg.getCustomerUpdateMsgList()) {
result.add(saveDownlinkMsg(customerUpdateMsg));
}
}
if (downlinkMsg.getWidgetsBundleUpdateMsgList() != null && !downlinkMsg.getWidgetsBundleUpdateMsgList().isEmpty()) {
for (WidgetsBundleUpdateMsg widgetsBundleUpdateMsg: downlinkMsg.getWidgetsBundleUpdateMsgList()) {
result.add(saveDownlinkMsg(widgetsBundleUpdateMsg));
}
}
if (downlinkMsg.getWidgetTypeUpdateMsgList() != null && !downlinkMsg.getWidgetTypeUpdateMsgList().isEmpty()) {
for (WidgetTypeUpdateMsg widgetTypeUpdateMsg: downlinkMsg.getWidgetTypeUpdateMsgList()) {
result.add(saveDownlinkMsg(widgetTypeUpdateMsg));
}
}
if (downlinkMsg.getUserUpdateMsgList() != null && !downlinkMsg.getUserUpdateMsgList().isEmpty()) {
for (UserUpdateMsg userUpdateMsg: downlinkMsg.getUserUpdateMsgList()) {
onUserUpdate(userUpdateMsg);
result.add(saveDownlinkMsg(userUpdateMsg));
}
}
if (downlinkMsg.getUserCredentialsUpdateMsgList() != null && !downlinkMsg.getUserCredentialsUpdateMsgList().isEmpty()) {
for (UserCredentialsUpdateMsg userCredentialsUpdateMsg: downlinkMsg.getUserCredentialsUpdateMsgList()) {
result.add(saveDownlinkMsg(userCredentialsUpdateMsg));
}
}
return Futures.allAsList(result);
}
private ListenableFuture<Void> saveDownlinkMsg(AbstractMessage message) {
if (!ignoredTypes.contains(message.getClass())) {
downlinkMsgs.add(message);
messagesLatch.countDown();
}
return Futures.immediateFuture(null);
}
public void waitForMessages() throws InterruptedException {
messagesLatch.await(5, TimeUnit.SECONDS);
}
public void expectMessageAmount(int messageAmount) {
messagesLatch = new CountDownLatch(messageAmount);
}
public void waitForResponses() throws InterruptedException { responsesLatch.await(5, TimeUnit.SECONDS); }
public void expectResponsesAmount(int messageAmount) {
responsesLatch = new CountDownLatch(messageAmount);
}
public <T> Optional<T> findMessageByType(Class<T> tClass) {
return (Optional<T>) downlinkMsgs.stream().filter(downlinkMsg -> downlinkMsg.getClass().isAssignableFrom(tClass)).findAny();
}
public AbstractMessage getLatestMessage() {
return downlinkMsgs.get(downlinkMsgs.size() - 1);
}
public void ignoreType(Class<? extends AbstractMessage> type) {
ignoredTypes.add(type);
}
public void allowIgnoredTypes() {
ignoredTypes.clear();
}
}

23
application/src/test/java/org/thingsboard/server/edge/nosql/EdgeNoSqlTest.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.edge.nosql;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
import org.thingsboard.server.edge.BaseEdgeTest;
@DaoNoSqlTest
public class EdgeNoSqlTest extends BaseEdgeTest {
}

23
application/src/test/java/org/thingsboard/server/edge/sql/EdgeSqlTest.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.edge.sql;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.edge.BaseEdgeTest;
@DaoSqlTest
public class EdgeSqlTest extends BaseEdgeTest {
}

3
application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java

@ -28,8 +28,7 @@ import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
// TODO: voba - fix before final test on cassandra
"org.thingsboard.server.mqtt.*.nosql.*VOBA_FIX_BEFORE_FINAL_TESTTest"})
"org.thingsboard.server.mqtt.*.nosql.*Test"})
public class MqttNoSqlTestSuite {
@ClassRule

2
common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java

@ -58,7 +58,5 @@ public interface DashboardService {
Dashboard unassignDashboardFromEdge(TenantId tenantId, DashboardId dashboardId, EdgeId edgeId);
void unassignEdgeDashboards(TenantId tenantId, EdgeId edgeId);
PageData<DashboardInfo> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}

2
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -89,5 +89,5 @@ public interface DeviceService {
Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
PageData<Device> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}

6
common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java

@ -16,9 +16,7 @@
package org.thingsboard.server.dao.edge;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -26,10 +24,8 @@ import org.thingsboard.server.common.data.page.TimePageLink;
public interface EdgeEventService {
EdgeEventType getEdgeEventTypeByEntityType(EntityType entityType);
ListenableFuture<EdgeEvent> saveAsync(EdgeEvent edgeEvent);
PageData<EdgeEvent> findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
PageData<EdgeEvent> findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink, boolean withTsUpdate);
}

7
common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java

@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -78,4 +79,10 @@ public interface EdgeService {
ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId);
ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId);
ListenableFuture<List<EdgeId>> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId);
Object checkInstance(Object request);
Object activateInstance(String licenseSecret, String releaseDate);
}

3
common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List;
@ -81,5 +82,5 @@ public interface EntityViewService {
EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId);
PageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink);
PageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}

5
common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java

@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainData;
@ -76,9 +77,7 @@ public interface RuleChainService {
RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId, boolean remove);
void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId);
PageData<RuleChain> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink);
PageData<RuleChain> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
RuleChain getDefaultRootEdgeRuleChain(TenantId tenantId);

7
common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java

@ -16,15 +16,12 @@
package org.thingsboard.server.common.data;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.*;
import java.util.HashSet;
import java.util.Set;
public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName, HasTenantId {

76
common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java

@ -15,59 +15,41 @@
*/
package org.thingsboard.server.common.data;
import org.thingsboard.server.common.data.id.EdgeId;
import java.util.Set;
import org.thingsboard.server.common.data.edge.EdgeEventType;
public final class EdgeUtils {
private EdgeUtils() {
}
public static boolean isAssignedToEdge(Set<ShortEdgeInfo> assignedEdges, EdgeId edgeId) {
return assignedEdges != null && assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null));
}
public static ShortEdgeInfo getAssignedEdgeInfo(Set<ShortEdgeInfo> assignedEdges, EdgeId edgeId) {
if (assignedEdges != null) {
for (ShortEdgeInfo edgeInfo : assignedEdges) {
if (edgeInfo.getEdgeId().equals(edgeId)) {
return edgeInfo;
}
}
}
return null;
}
public static boolean addAssignedEdge(Set<ShortEdgeInfo> assignedEdges, ShortEdgeInfo edgeInfo) {
if (assignedEdges != null && assignedEdges.contains(edgeInfo)) {
return false;
} else {
if (assignedEdges != null) {
assignedEdges.add(edgeInfo);
return true;
} else {
return false;
}
}
}
public static boolean updateAssignedEdge(Set<ShortEdgeInfo> assignedEdges, ShortEdgeInfo edgeInfo) {
if (assignedEdges != null && assignedEdges.contains(edgeInfo)) {
assignedEdges.remove(edgeInfo);
assignedEdges.add(edgeInfo);
return true;
} else {
return false;
}
}
public static boolean removeAssignedEdge(Set<ShortEdgeInfo> assignedEdges, ShortEdgeInfo edgeInfo) {
if (assignedEdges != null && assignedEdges.contains(edgeInfo)) {
assignedEdges.remove(edgeInfo);
return true;
} else {
return false;
public static EdgeEventType getEdgeEventTypeByEntityType(EntityType entityType) {
switch (entityType) {
case EDGE:
return EdgeEventType.EDGE;
case DEVICE:
return EdgeEventType.DEVICE;
case ASSET:
return EdgeEventType.ASSET;
case ENTITY_VIEW:
return EdgeEventType.ENTITY_VIEW;
case DASHBOARD:
return EdgeEventType.DASHBOARD;
case USER:
return EdgeEventType.USER;
case RULE_CHAIN:
return EdgeEventType.RULE_CHAIN;
case ALARM:
return EdgeEventType.ALARM;
case TENANT:
return EdgeEventType.TENANT;
case CUSTOMER:
return EdgeEventType.CUSTOMER;
case WIDGETS_BUNDLE:
return EdgeEventType.WIDGETS_BUNDLE;
case WIDGET_TYPE:
return EdgeEventType.WIDGET_TYPE;
default:
return null;
}
}
}

50
common/data/src/main/java/org/thingsboard/server/common/data/ShortEdgeInfo.java

@ -1,50 +0,0 @@
/**
* Copyright © 2016-2020 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
@AllArgsConstructor
public class ShortEdgeInfo {
@Getter @Setter
private EdgeId edgeId;
@Getter @Setter
private String title;
@Getter @Setter
private RuleChainId rootRuleChainId;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ShortEdgeInfo that = (ShortEdgeInfo) o;
return edgeId.equals(that.edgeId);
}
@Override
public int hashCode() {
return edgeId.hashCode();
}
}

4
common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java

@ -45,7 +45,9 @@ public enum ActionType {
ASSIGNED_FROM_TENANT(false),
ASSIGNED_TO_TENANT(false),
ASSIGNED_TO_EDGE(false), // log edge name
UNASSIGNED_FROM_EDGE(false); // log edge name
UNASSIGNED_FROM_EDGE(false), // log edge name
CREDENTIALS_REQUEST(false), // request credentials from edge
ENTITY_EXISTS_REQUEST(false); // request to recreate entity on edge
private final boolean isRead;

12
common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.common.data.edge;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@ -25,8 +24,6 @@ import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
@ -48,6 +45,8 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
private String label;
private String routingKey;
private String secret;
private String edgeLicenseKey;
private String cloudEndpoint;
private transient JsonNode configuration;
public Edge() {
@ -67,14 +66,11 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
this.name = edge.getName();
this.routingKey = edge.getRoutingKey();
this.secret = edge.getSecret();
this.edgeLicenseKey = edge.getEdgeLicenseKey();
this.cloudEndpoint = edge.getCloudEndpoint();
this.configuration = edge.getConfiguration();
}
@JsonIgnore
public ShortEdgeInfo toShortEdgeInfo() {
return new ShortEdgeInfo(id, name, rootRuleChainId);
}
@Override
public String getSearchText() {
return getName();

8
common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java

@ -20,7 +20,6 @@ import lombok.Data;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.EdgeEventId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.UUID;
@ -30,10 +29,11 @@ public class EdgeEvent extends BaseData<EdgeEventId> {
private TenantId tenantId;
private EdgeId edgeId;
private String edgeEventAction;
private String action;
private UUID entityId;
private EdgeEventType edgeEventType;
private transient JsonNode entityBody;
private String uid;
private EdgeEventType type;
private transient JsonNode body;
public EdgeEvent() {
super();

16
common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java

@ -16,5 +16,19 @@
package org.thingsboard.server.common.data.edge;
public enum EdgeEventType {
DASHBOARD, ASSET, DEVICE, ENTITY_VIEW, ALARM, RULE_CHAIN, RULE_CHAIN_METADATA, EDGE, USER, CUSTOMER, RELATION
DASHBOARD,
ASSET,
DEVICE,
ENTITY_VIEW,
ALARM,
RULE_CHAIN,
RULE_CHAIN_METADATA,
EDGE,
USER,
CUSTOMER,
RELATION,
TENANT,
WIDGETS_BUNDLE,
WIDGET_TYPE,
ADMIN_SETTINGS
}

3
common/data/src/main/java/org/thingsboard/server/common/data/exception/ThingsboardErrorCode.java

@ -28,7 +28,8 @@ public enum ThingsboardErrorCode {
BAD_REQUEST_PARAMS(31),
ITEM_NOT_FOUND(32),
TOO_MANY_REQUESTS(33),
TOO_MANY_UPDATES(34);
TOO_MANY_UPDATES(34),
SUBSCRIPTION_VIOLATION(40);
private int errorCode;

4
common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java

@ -91,6 +91,10 @@ public class EntityIdFactory {
return new RuleChainId(uuid);
case ENTITY_VIEW:
return new EntityViewId(uuid);
case WIDGETS_BUNDLE:
return new WidgetsBundleId(uuid);
case WIDGET_TYPE:
return new WidgetTypeId(uuid);
case EDGE:
return new EdgeId(uuid);
}

109
common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java

@ -28,9 +28,9 @@ import org.thingsboard.server.gen.edge.ConnectRequestMsg;
import org.thingsboard.server.gen.edge.ConnectResponseCode;
import org.thingsboard.server.gen.edge.ConnectResponseMsg;
import org.thingsboard.server.gen.edge.DownlinkMsg;
import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
import org.thingsboard.server.gen.edge.EdgeConfiguration;
import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc;
import org.thingsboard.server.gen.edge.EntityUpdateMsg;
import org.thingsboard.server.gen.edge.RequestMsg;
import org.thingsboard.server.gen.edge.RequestMsgType;
import org.thingsboard.server.gen.edge.ResponseMsg;
@ -40,7 +40,9 @@ import org.thingsboard.server.gen.edge.UplinkResponseMsg;
import javax.net.ssl.SSLException;
import java.io.File;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
@Service
@ -53,6 +55,8 @@ public class EdgeGrpcClient implements EdgeRpcClient {
private int rpcPort;
@Value("${cloud.rpc.timeout}")
private int timeoutSecs;
@Value("${cloud.rpc.keep_alive_time_sec}")
private int keepAliveTimeSec;
@Value("${cloud.rpc.ssl.enabled}")
private boolean sslEnabled;
@Value("${cloud.rpc.ssl.cert}")
@ -62,15 +66,18 @@ public class EdgeGrpcClient implements EdgeRpcClient {
private StreamObserver<RequestMsg> inputStream;
private static final ReentrantLock uplinkMsgLock = new ReentrantLock();
@Override
public void connect(String edgeKey,
String edgeSecret,
Consumer<UplinkResponseMsg> onUplinkResponse,
Consumer<EdgeConfiguration> onEdgeUpdate,
Consumer<EntityUpdateMsg> onEntityUpdate,
Consumer<DownlinkMsg> onDownlink,
Consumer<Exception> onError) {
NettyChannelBuilder builder = NettyChannelBuilder.forAddress(rpcHost, rpcPort).usePlaintext();
NettyChannelBuilder builder = NettyChannelBuilder.forAddress(rpcHost, rpcPort)
.keepAliveTime(keepAliveTimeSec, TimeUnit.SECONDS)
.usePlaintext();
if (sslEnabled) {
try {
builder.sslContext(GrpcSslContexts.forClient().trustManager(new File(Resources.getResource(certResource).toURI())).build());
@ -79,47 +86,19 @@ public class EdgeGrpcClient implements EdgeRpcClient {
throw new RuntimeException(e);
}
}
gracefulShutdown();
channel = builder.build();
EdgeRpcServiceGrpc.EdgeRpcServiceStub stub = EdgeRpcServiceGrpc.newStub(channel);
log.info("[{}] Sending a connect request to the TB!", edgeKey);
this.inputStream = stub.handleMsgs(initOutputStream(edgeKey, onUplinkResponse, onEdgeUpdate, onEntityUpdate, onDownlink, onError));
this.inputStream = stub.handleMsgs(initOutputStream(edgeKey, onUplinkResponse, onEdgeUpdate, onDownlink, onError));
this.inputStream.onNext(RequestMsg.newBuilder()
.setMsgType(RequestMsgType.CONNECT_RPC_MESSAGE)
.setConnectRequestMsg(ConnectRequestMsg.newBuilder().setEdgeRoutingKey(edgeKey).setEdgeSecret(edgeSecret).build())
.build());
}
private void gracefulShutdown() {
try {
if (channel != null) {
channel.shutdown().awaitTermination(timeoutSecs, TimeUnit.SECONDS);
}
} catch (InterruptedException e) {
log.debug("Error during shutdown of the previous channel", e);
}
}
@Override
public void disconnect() throws InterruptedException {
inputStream.onCompleted();
if (channel != null) {
channel.shutdown().awaitTermination(timeoutSecs, TimeUnit.SECONDS);
}
}
@Override
public void sendUplinkMsg(UplinkMsg msg) {
this.inputStream.onNext(RequestMsg.newBuilder()
.setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
.setUplinkMsg(msg)
.build());
}
private StreamObserver<ResponseMsg> initOutputStream(String edgeKey,
Consumer<UplinkResponseMsg> onUplinkResponse,
Consumer<EdgeConfiguration> onEdgeUpdate,
Consumer<EntityUpdateMsg> onEntityUpdate,
Consumer<DownlinkMsg> onDownlink,
Consumer<Exception> onError) {
return new StreamObserver<ResponseMsg>() {
@ -132,14 +111,19 @@ public class EdgeGrpcClient implements EdgeRpcClient {
onEdgeUpdate.accept(connectResponseMsg.getConfiguration());
} else {
log.error("[{}] Failed to establish the connection! Code: {}. Error message: {}.", edgeKey, connectResponseMsg.getResponseCode(), connectResponseMsg.getErrorMsg());
try {
EdgeGrpcClient.this.disconnect(true);
} catch (InterruptedException e) {
log.error("[{}] Got interruption during disconnect!", edgeKey, e);
}
onError.accept(new EdgeConnectionException("Failed to establish the connection! Response code: " + connectResponseMsg.getResponseCode().name()));
}
} else if (responseMsg.hasEdgeUpdateMsg()) {
log.debug("[{}] Edge update message received {}", edgeKey, responseMsg.getEdgeUpdateMsg());
onEdgeUpdate.accept(responseMsg.getEdgeUpdateMsg().getConfiguration());
} else if (responseMsg.hasUplinkResponseMsg()) {
log.debug("[{}] Uplink response message received {}", edgeKey, responseMsg.getUplinkResponseMsg());
onUplinkResponse.accept(responseMsg.getUplinkResponseMsg());
} else if (responseMsg.hasEntityUpdateMsg()) {
log.debug("[{}] Entity update message received {}", edgeKey, responseMsg.getEntityUpdateMsg());
onEntityUpdate.accept(responseMsg.getEntityUpdateMsg());
} else if (responseMsg.hasDownlinkMsg()) {
log.debug("[{}] Downlink message received {}", edgeKey, responseMsg.getDownlinkMsg());
onDownlink.accept(responseMsg.getDownlinkMsg());
@ -149,6 +133,11 @@ public class EdgeGrpcClient implements EdgeRpcClient {
@Override
public void onError(Throwable t) {
log.debug("[{}] The rpc session received an error!", edgeKey, t);
try {
EdgeGrpcClient.this.disconnect(true);
} catch (InterruptedException e) {
log.error("[{}] Got interruption during disconnect!", edgeKey, e);
}
onError.accept(new RuntimeException(t));
}
@ -158,4 +147,54 @@ public class EdgeGrpcClient implements EdgeRpcClient {
}
};
}
@Override
public void disconnect(boolean onError) throws InterruptedException {
if (!onError) {
try {
inputStream.onCompleted();
} catch (Exception ignored) {}
}
if (channel != null) {
channel.shutdown().awaitTermination(timeoutSecs, TimeUnit.SECONDS);
}
}
@Override
public void sendUplinkMsg(UplinkMsg msg) {
try {
uplinkMsgLock.lock();
this.inputStream.onNext(RequestMsg.newBuilder()
.setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
.setUplinkMsg(msg)
.build());
} finally {
uplinkMsgLock.unlock();
}
}
@Override
public void sendSyncRequestMsg() {
try {
uplinkMsgLock.lock();
this.inputStream.onNext(RequestMsg.newBuilder()
.setMsgType(RequestMsgType.SYNC_REQUEST_RPC_MESSAGE)
.build());
} finally {
uplinkMsgLock.unlock();
}
}
@Override
public void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg) {
try {
uplinkMsgLock.lock();
this.inputStream.onNext(RequestMsg.newBuilder()
.setMsgType(RequestMsgType.UPLINK_RPC_MESSAGE)
.setDownlinkResponseMsg(downlinkResponseMsg)
.build());
} finally {
uplinkMsgLock.unlock();
}
}
}

11
common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeRpcClient.java

@ -16,8 +16,8 @@
package org.thingsboard.edge.rpc;
import org.thingsboard.server.gen.edge.DownlinkMsg;
import org.thingsboard.server.gen.edge.DownlinkResponseMsg;
import org.thingsboard.server.gen.edge.EdgeConfiguration;
import org.thingsboard.server.gen.edge.EntityUpdateMsg;
import org.thingsboard.server.gen.edge.UplinkMsg;
import org.thingsboard.server.gen.edge.UplinkResponseMsg;
@ -29,11 +29,14 @@ public interface EdgeRpcClient {
String integrationSecret,
Consumer<UplinkResponseMsg> onUplinkResponse,
Consumer<EdgeConfiguration> onEdgeUpdate,
Consumer<EntityUpdateMsg> onEntityUpdate,
Consumer<DownlinkMsg> onDownlink,
Consumer<Exception> onError);
void disconnect() throws InterruptedException;
void disconnect(boolean onError) throws InterruptedException;
void sendUplinkMsg(UplinkMsg uplinkMsg) throws InterruptedException;
void sendSyncRequestMsg();
void sendUplinkMsg(UplinkMsg uplinkMsg);
void sendDownlinkResponseMsg(DownlinkResponseMsg downlinkResponseMsg);
}

220
common/edge-api/src/main/proto/edge.proto

@ -37,31 +37,24 @@ message RequestMsg {
RequestMsgType msgType = 1;
ConnectRequestMsg connectRequestMsg = 2;
UplinkMsg uplinkMsg = 3;
DownlinkResponseMsg downlinkResponseMsg = 4;
}
message ResponseMsg {
ConnectResponseMsg connectResponseMsg = 1;
UplinkResponseMsg uplinkResponseMsg = 2;
EntityUpdateMsg entityUpdateMsg = 3;
DownlinkMsg downlinkMsg = 4;
}
message EntityUpdateMsg {
DeviceUpdateMsg deviceUpdateMsg = 1;
RuleChainUpdateMsg ruleChainUpdateMsg = 2;
RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = 3;
DashboardUpdateMsg dashboardUpdateMsg = 4;
AssetUpdateMsg assetUpdateMsg = 5;
EntityViewUpdateMsg entityViewUpdateMsg = 6;
AlarmUpdateMsg alarmUpdateMsg = 7;
UserUpdateMsg userUpdateMsg = 8;
CustomerUpdateMsg customerUpdateMsg = 9;
RelationUpdateMsg relationUpdateMsg = 10;
DownlinkMsg downlinkMsg = 3;
EdgeUpdateMsg edgeUpdateMsg = 4;
}
enum RequestMsgType {
CONNECT_RPC_MESSAGE = 0;
UPLINK_RPC_MESSAGE = 1;
SYNC_REQUEST_RPC_MESSAGE = 2;
}
message EdgeUpdateMsg {
EdgeConfiguration configuration = 1;
}
message ConnectRequestMsg {
@ -82,12 +75,16 @@ message ConnectResponseMsg {
}
message EdgeConfiguration {
int64 tenantIdMSB = 1;
int64 tenantIdLSB = 2;
string name = 3;
string routingKey = 4;
string type = 5;
string cloudType = 6;
int64 edgeIdMSB = 1;
int64 edgeIdLSB = 2;
int64 tenantIdMSB = 3;
int64 tenantIdLSB = 4;
string name = 5;
string routingKey = 6;
string type = 7;
string edgeLicenseKey = 8;
string cloudEndpoint = 9;
string cloudType = 10;
}
enum UpdateMsgType {
@ -105,9 +102,17 @@ message EntityDataProto {
string entityType = 3;
transport.PostTelemetryMsg postTelemetryMsg = 4;
transport.PostAttributeMsg postAttributesMsg = 5;
transport.PostAttributeMsg attributesUpdatedMsg = 6;
string postAttributeScope = 7;
AttributeDeleteMsg attributeDeleteMsg = 8;
// transport.ToDeviceRpcRequestMsg ???
}
message AttributeDeleteMsg {
string scope = 1;
repeated string attributeNames = 2;
}
message RuleChainUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
@ -158,40 +163,56 @@ message DashboardUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string title = 4;
string configuration = 5;
int64 customerIdMSB = 4;
int64 customerIdLSB = 5;
string title = 6;
string configuration = 7;
}
message DeviceUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string name = 4;
string type = 5;
string label = 6;
string credentialsType = 7;
string credentialsId = 8;
string credentialsValue = 9;
int64 customerIdMSB = 4;
int64 customerIdLSB = 5;
string name = 6;
string type = 7;
string label = 8;
string additionalInfo = 9;
}
message DeviceCredentialsUpdateMsg {
int64 deviceIdMSB = 1;
int64 deviceIdLSB = 2;
string credentialsType = 3;
string credentialsId = 4;
string credentialsValue = 5;
}
message AssetUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string name = 4;
string type = 5;
string label = 6;
int64 customerIdMSB = 4;
int64 customerIdLSB = 5;
string name = 6;
string type = 7;
string label = 8;
string additionalInfo = 9;
}
message EntityViewUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string name = 4;
string type = 5;
int64 entityIdMSB = 6;
int64 entityIdLSB = 7;
EdgeEntityType entityType = 8;
int64 customerIdMSB = 4;
int64 customerIdLSB = 5;
string name = 6;
string type = 7;
int64 entityIdMSB = 8;
int64 entityIdLSB = 9;
EdgeEntityType entityType = 10;
string additionalInfo = 11;
}
message AlarmUpdateMsg {
@ -243,13 +264,47 @@ message UserUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string email = 4;
string authority = 5;
string firstName = 6;
string lastName = 7;
string additionalInfo = 8;
bool enabled = 9;
string password = 10;
int64 customerIdMSB = 4;
int64 customerIdLSB = 5;
string email = 6;
string authority = 7;
string firstName = 8;
string lastName = 9;
string additionalInfo = 10;
}
message WidgetsBundleUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string title = 4;
string alias = 5;
bytes image = 6;
bool isSystem = 7;
}
message WidgetTypeUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string bundleAlias = 4;
string alias = 5;
string name = 6;
string descriptorJson = 7;
bool isSystem = 8;
}
message AdminSettingsUpdateMsg {
bool isSystem = 1;
string key = 2;
string jsonValue = 3;
}
message UserCredentialsUpdateMsg {
int64 userIdMSB = 1;
int64 userIdLSB = 2;
bool enabled = 3;
string password = 4;
}
message RuleChainMetadataRequestMsg {
@ -257,6 +312,50 @@ message RuleChainMetadataRequestMsg {
int64 ruleChainIdLSB = 2;
}
message AttributesRequestMsg {
int64 entityIdMSB = 1;
int64 entityIdLSB = 2;
string entityType = 3;
}
message RelationRequestMsg {
int64 entityIdMSB = 1;
int64 entityIdLSB = 2;
string entityType = 3;
}
message UserCredentialsRequestMsg {
int64 userIdMSB = 1;
int64 userIdLSB = 2;
}
message DeviceCredentialsRequestMsg {
int64 deviceIdMSB = 1;
int64 deviceIdLSB = 2;
}
message DeviceRpcCallMsg {
int64 deviceIdMSB = 1;
int64 deviceIdLSB = 2;
int64 requestIdMSB = 3;
int64 requestIdLSB = 4;
int64 expirationTime = 5;
bool oneway = 6;
string originServiceId = 7;
RpcRequestMsg requestMsg = 8;
RpcResponseMsg responseMsg = 9;
}
message RpcRequestMsg {
string method = 1;
string params = 2;
}
message RpcResponseMsg {
string response = 1;
string error = 2;
}
enum EdgeEntityType {
DEVICE = 0;
ASSET = 1;
@ -270,8 +369,15 @@ message UplinkMsg {
int32 uplinkMsgId = 1;
repeated EntityDataProto entityData = 2;
repeated DeviceUpdateMsg deviceUpdateMsg = 3;
repeated AlarmUpdateMsg alarmUpdateMsg = 4;
repeated RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg = 5;
repeated DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = 4;
repeated AlarmUpdateMsg alarmUpdateMsg = 5;
repeated RelationUpdateMsg relationUpdateMsg = 6;
repeated RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg = 7;
repeated AttributesRequestMsg attributesRequestMsg = 8;
repeated RelationRequestMsg relationRequestMsg = 9;
repeated UserCredentialsRequestMsg userCredentialsRequestMsg = 10;
repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 11;
repeated DeviceRpcCallMsg deviceRpcCallMsg = 12;
}
message UplinkResponseMsg {
@ -279,8 +385,30 @@ message UplinkResponseMsg {
string errorMsg = 2;
}
message DownlinkResponseMsg {
bool success = 1;
string errorMsg = 2;
}
message DownlinkMsg {
int32 downlinkMsgId = 1;
repeated EntityDataProto entityData = 2;
repeated DeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 3;
repeated DeviceUpdateMsg deviceUpdateMsg = 4;
repeated DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg = 5;
repeated RuleChainUpdateMsg ruleChainUpdateMsg = 6;
repeated RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = 7;
repeated DashboardUpdateMsg dashboardUpdateMsg = 8;
repeated AssetUpdateMsg assetUpdateMsg = 9;
repeated EntityViewUpdateMsg entityViewUpdateMsg = 10;
repeated AlarmUpdateMsg alarmUpdateMsg = 11;
repeated UserUpdateMsg userUpdateMsg = 12;
repeated UserCredentialsUpdateMsg userCredentialsUpdateMsg = 13;
repeated CustomerUpdateMsg customerUpdateMsg = 14;
repeated RelationUpdateMsg relationUpdateMsg = 15;
repeated WidgetsBundleUpdateMsg widgetsBundleUpdateMsg = 16;
repeated WidgetTypeUpdateMsg widgetTypeUpdateMsg = 17;
repeated AdminSettingsUpdateMsg adminSettingsUpdateMsg = 18;
repeated DeviceRpcCallMsg deviceRpcCallMsg = 19;
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save