Browse Source

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

pull/3811/head
Volodymyr Babak 6 years ago
parent
commit
1633959912
  1. 143
      application/src/main/data/json/demo/rule_chains/edge_root_rule_chain.json
  2. 1
      application/src/main/data/json/demo/rule_chains/thermostat_alarms.json
  3. 2
      application/src/main/data/json/tenant/rule_chains/root_rule_chain.json
  4. 63
      application/src/main/data/upgrade/2.4.x/schema_update.cql
  5. 63
      application/src/main/data/upgrade/2.6.0/schema_update.cql
  6. 0
      application/src/main/data/upgrade/2.6.0/schema_update.sql
  7. 7
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  8. 6
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  9. 4
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
  10. 5
      application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java
  11. 5
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  12. 12
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  13. 47
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  14. 72
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  15. 14
      application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
  16. 171
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  17. 46
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  18. 27
      application/src/main/java/org/thingsboard/server/controller/EdgeController.java
  19. 9
      application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
  20. 54
      application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
  21. 248
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  22. 17
      application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
  23. 1
      application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java
  24. 322
      application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
  25. 54
      application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
  26. 37
      application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java
  27. 882
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  28. 12
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java
  29. 8
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java
  30. 34
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java
  31. 56
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java
  32. 53
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java
  33. 45
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java
  34. 14
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java
  35. 72
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java
  36. 271
      application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java
  37. 398
      application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java
  38. 7
      application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java
  39. 37
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  40. 2
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  41. 16
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java
  42. 6
      application/src/main/java/org/thingsboard/server/service/queue/TbCoreConsumerStats.java
  43. 3
      application/src/main/resources/thingsboard.yml
  44. 18
      application/src/test/java/org/thingsboard/server/controller/BaseComponentDescriptorControllerTest.java
  45. 101
      application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
  46. 7
      common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
  47. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
  48. 7
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  49. 35
      common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java
  50. 12
      common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java
  51. 7
      common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java
  52. 11
      common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
  53. 52
      common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
  54. 4
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  55. 13
      common/data/src/main/java/org/thingsboard/server/common/data/Device.java
  56. 73
      common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java
  57. 2
      common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
  58. 14
      common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java
  59. 1
      common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java
  60. 50
      common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java
  61. 4
      common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java
  62. 24
      common/data/src/main/java/org/thingsboard/server/common/data/id/EdgeEventId.java
  63. 25
      common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
  64. 3
      common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java
  65. 59
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java
  66. 2
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java
  67. 4
      common/edge-api/pom.xml
  68. 13
      common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java
  69. 224
      common/edge-api/src/main/proto/edge.proto
  70. 2
      common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java
  71. 17
      common/queue/src/main/proto/queue.proto
  72. 58
      dao/pom.xml
  73. 16
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  74. 16
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
  75. 55
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  76. 2
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java
  77. 96
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  78. 14
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  79. 50
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  80. 84
      dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java
  81. 743
      dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java
  82. 17
      dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java
  83. 51
      dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java
  84. 407
      dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java
  85. 15
      dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java
  86. 21
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java
  87. 62
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java
  88. 16
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  89. 11
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java
  90. 10
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java
  91. 9
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java
  92. 20
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java
  93. 20
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java
  94. 121
      dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java
  95. 177
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  96. 9
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java
  97. 18
      dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java
  98. 1
      dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java
  99. 18
      dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java
  100. 48
      dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java

143
application/src/main/data/json/demo/rule_chains/edge_root_rule_chain.json

@ -0,0 +1,143 @@
{
"ruleChain": {
"additionalInfo": null,
"name": "Edge Root Rule Chain",
"type": "EDGE",
"firstRuleNodeId": null,
"root": true,
"debugMode": false,
"configuration": null
},
"metadata": {
"firstNodeIndex": 2,
"nodes": [
{
"additionalInfo": {
"layoutX": 823,
"layoutY": 157
},
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
"name": "Save Timeseries",
"debugMode": false,
"configuration": {
"defaultTTL": 0
}
},
{
"additionalInfo": {
"layoutX": 824,
"layoutY": 52
},
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
"name": "Save Client Attributes",
"debugMode": false,
"configuration": {
"scope": "CLIENT_SCOPE"
}
},
{
"additionalInfo": {
"layoutX": 347,
"layoutY": 149
},
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
"name": "Message Type Switch",
"debugMode": false,
"configuration": {
"version": 0
}
},
{
"additionalInfo": {
"layoutX": 825,
"layoutY": 266
},
"type": "org.thingsboard.rule.engine.action.TbLogNode",
"name": "Log RPC from Device",
"debugMode": false,
"configuration": {
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
}
},
{
"additionalInfo": {
"layoutX": 824,
"layoutY": 378
},
"type": "org.thingsboard.rule.engine.action.TbLogNode",
"name": "Log Other",
"debugMode": false,
"configuration": {
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
}
},
{
"additionalInfo": {
"layoutX": 824,
"layoutY": 466
},
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
"name": "RPC Call Request",
"debugMode": false,
"configuration": {
"timeoutInSeconds": 60
}
},
{
"additionalInfo": {
"layoutX": 1134,
"layoutY": 132
},
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
"name": "Push to cloud",
"debugMode": false,
"configuration": {
"version": 0
}
}
],
"connections": [
{
"fromIndex": 0,
"toIndex": 6,
"type": "Success"
},
{
"fromIndex": 1,
"toIndex": 6,
"type": "Success"
},
{
"fromIndex": 2,
"toIndex": 4,
"type": "Other"
},
{
"fromIndex": 2,
"toIndex": 1,
"type": "Post attributes"
},
{
"fromIndex": 2,
"toIndex": 0,
"type": "Post telemetry"
},
{
"fromIndex": 2,
"toIndex": 3,
"type": "RPC Request from Device"
},
{
"fromIndex": 2,
"toIndex": 5,
"type": "RPC Request to Device"
},
{
"fromIndex": 3,
"toIndex": 6,
"type": "Success"
}
],
"ruleChainConnections": null
}
}

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

@ -2,6 +2,7 @@
"ruleChain": {
"additionalInfo": null,
"name": "Thermostat Alarms",
"type": "CORE",
"firstRuleNodeId": null,
"root": false,
"debugMode": false,

2
application/src/main/data/json/tenant/rule_chains/root_rule_chain.json

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

63
application/src/main/data/upgrade/2.4.x/schema_update.cql

@ -1,63 +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.
--
CREATE TABLE IF NOT EXISTS thingsboard.edge (
id timeuuid,
tenant_id timeuuid,
customer_id timeuuid,
name text,
search_text text,
configuration text,
additional_info text,
PRIMARY KEY (id, tenant_id)
);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_name AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
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_tenant_and_search_text AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, search_text, id, customer_id, type)
WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_by_type_and_search_text AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, type, search_text, id, customer_id)
WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_customer_and_search_text AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( customer_id, tenant_id, search_text, id, type )
WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_customer_by_type_and_search_text AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
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

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

@ -0,0 +1,63 @@
--
-- 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 TABLE IF NOT EXISTS thingsboard.edge (
id timeuuid,
tenant_id timeuuid,
customer_id timeuuid,
name text,
search_text text,
configuration text,
additional_info text,
PRIMARY KEY (id, tenant_id)
);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_and_name AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
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_tenant_and_search_text AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, search_text, id, customer_id, type)
WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_tenant_by_type_and_search_text AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, type, search_text, id, customer_id)
WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_customer_and_search_text AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( customer_id, tenant_id, search_text, id, type )
WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.edge_by_customer_by_type_and_search_text AS
SELECT *
from thingsboard.edge
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
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

0
application/src/main/data/upgrade/2.4.x/schema_update.sql → application/src/main/data/upgrade/2.6.0/schema_update.sql

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

@ -53,7 +53,7 @@ import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.ClaimDevicesService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeService;
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.event.EventService;
@ -254,6 +254,11 @@ public class ActorSystemContext {
@Getter
private TbCoreDeviceRpcService tbCoreDeviceRpcService;
@Lazy
@Autowired
@Getter
private EdgeEventService edgeEventService;
@Value("${actors.session.max_concurrent_sessions_per_device:1}")
@Getter
private long maxConcurrentSessionsPerDevice;

6
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -52,6 +52,7 @@ import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeService;
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.nosql.CassandraStatementTask;
@ -387,6 +388,11 @@ class DefaultTbContext implements TbContext {
return mainCtx.getEdgeService();
}
@Override
public EdgeEventService getEdgeEventService() {
return mainCtx.getEdgeEventService();
}
@Override
public EventLoopGroup getSharedEventLoop() {
return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();

4
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java

@ -97,7 +97,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
if (!started) {
RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
if (ruleChain != null) {
if (ruleChain.getType().equals(RuleChainType.SYSTEM)) {
if (ruleChain.getType().equals(RuleChainType.CORE)) {
List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
// Creating and starting the actors;
@ -119,7 +119,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
public void onUpdate(TbActorCtx context) {
RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
if (ruleChain != null) {
if (ruleChain.getType().equals(RuleChainType.SYSTEM)) {
if (ruleChain.getType().equals(RuleChainType.CORE)) {
ruleChainName = ruleChain.getName();
List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());

5
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java

@ -23,7 +23,6 @@ import org.thingsboard.server.actors.TbEntityActorId;
import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate;
import org.thingsboard.server.actors.service.ContextAwareActor;
import org.thingsboard.server.actors.service.DefaultActorService;
import org.thingsboard.server.actors.tenant.TenantActor;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
@ -56,7 +55,7 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
}
protected void initRuleChains() {
for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {
for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {
RuleChainId ruleChainId = ruleChain.getId();
log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId());
TbActorRef actorRef = getOrCreateActor(ruleChainId, id -> ruleChain);
@ -66,7 +65,7 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
}
protected void visit(RuleChain entity, TbActorRef actorRef) {
if (entity != null && entity.isRoot()) {
if (entity != null && entity.isRoot() && entity.getType().equals(RuleChainType.CORE)) {
rootChain = entity;
rootChainActor = actorRef;
}

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

@ -36,6 +36,7 @@ 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.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.msg.MsgType;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbMsg;
@ -207,7 +208,9 @@ public class TenantActor extends RuleChainManagerActor {
if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
RuleChain ruleChain = systemContext.getRuleChainService().
findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId()));
visit(ruleChain, target);
if (ruleChain != null && ruleChain.getType().equals(RuleChainType.CORE)) {
visit(ruleChain, target);
}
}
target.tellWithHighPriority(msg);
} else {

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

@ -93,6 +93,9 @@ public class AlarmController extends BaseController {
logEntityAction(savedAlarm.getId(), savedAlarm,
getCurrentUser().getCustomerId(),
alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
sendNotificationMsgToEdgeService(getTenantId(), savedAlarm.getId(), alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED);
return savedAlarm;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ALARM), alarm,
@ -109,8 +112,11 @@ public class AlarmController extends BaseController {
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
checkAlarmId(alarmId, Operation.WRITE);
sendNotificationMsgToEdgeService(getTenantId(), alarmId, ActionType.DELETED);
return alarmService.deleteAlarm(getTenantId(), alarmId);
} catch (Exception e) {
} catch (Exception e) {
throw handleException(e);
}
}
@ -127,6 +133,8 @@ public class AlarmController extends BaseController {
alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, ackTs).get();
alarm.setAckTs(ackTs);
logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null);
sendNotificationMsgToEdgeService(getTenantId(), alarmId, ActionType.ALARM_ACK);
} catch (Exception e) {
throw handleException(e);
}
@ -144,6 +152,8 @@ public class AlarmController extends BaseController {
alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, clearTs).get();
alarm.setClearTs(clearTs);
logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null);
sendNotificationMsgToEdgeService(getTenantId(), alarmId, ActionType.ALARM_CLEAR);
} catch (Exception e) {
throw handleException(e);
}

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

@ -27,6 +27,7 @@ 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.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset;
@ -34,6 +35,7 @@ import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.asset.AssetSearchQuery;
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.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
@ -41,6 +43,7 @@ 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.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -98,6 +101,9 @@ public class AssetController extends BaseController {
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);
@ -124,6 +130,7 @@ public class AssetController extends BaseController {
asset.getCustomerId(),
ActionType.DELETED, null, strAssetId);
sendNotificationMsgToEdgeService(getTenantId(), null, assetId, EdgeEventType.ASSET, ActionType.DELETED);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ASSET),
null,
@ -402,7 +409,7 @@ public class AssetController extends BaseController {
@RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST)
@ResponseBody
public Asset assignAssetToEdge(@PathVariable(EDGE_ID) String strEdgeId,
@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(ASSET_ID, strAssetId);
try {
@ -418,6 +425,8 @@ 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);
return savedAsset;
} catch (Exception e) {
@ -430,25 +439,28 @@ public class AssetController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/asset/{assetId}", method = RequestMethod.DELETE)
@RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE)
@ResponseBody
public Asset unassignAssetFromEdge(@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
public Asset unassignAssetFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(ASSET_ID, strAssetId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
AssetId assetId = new AssetId(toUUID(strAssetId));
Asset asset = checkAssetId(assetId, Operation.UNASSIGN_FROM_EDGE);
if (asset.getEdgeId() == null || asset.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Asset isn't assigned to any edge!");
}
Edge edge = checkEdgeId(asset.getEdgeId(), Operation.READ);
Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId));
Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId, edgeId));
logEntityAction(assetId, asset,
asset.getCustomerId(),
ActionType.UNASSIGNED_FROM_EDGE, null, strAssetId, edge.getId().toString(), edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedAsset.getId(),
EdgeEventType.ASSET, ActionType.UNASSIGNED_FROM_EDGE);
return savedAsset;
} catch (Exception e) {
@ -464,24 +476,21 @@ public class AssetController extends BaseController {
@RequestMapping(value = "/edge/{edgeId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<Asset> getEdgeAssets(
@PathVariable("edgeId") String strEdgeId,
@PathVariable(EDGE_ID) String strEdgeId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
@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);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
if (type != null && type.trim().length()>0) {
return checkNotNull(assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
} else {
return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
}
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
} catch (Exception e) {
throw handleException(e);
}

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

@ -26,33 +26,34 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.thingsboard.server.common.data.*;
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.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo;
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.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmId;
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.EdgeId;
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.EntityViewId;
@ -69,9 +70,9 @@ import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
@ -87,7 +88,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.ClaimDevicesService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeService;
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;
@ -100,10 +101,12 @@ 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.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionService;
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.queue.TbClusterService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.AccessControlService;
@ -205,6 +208,12 @@ public abstract class BaseController {
@Autowired
protected TbQueueProducerProvider producerProvider;
@Autowired
protected EdgeNotificationService edgeNotificationService;
@Autowired
protected EdgeEventService edgeEventService;
@Value("${server.log_controller_error_stack_trace}")
@Getter
private boolean logControllerErrorStackTrace;
@ -565,15 +574,6 @@ public abstract class BaseController {
}
}
ComponentDescriptor checkComponentDescriptorByClazz(String clazz, RuleChainType ruleChainType) throws ThingsboardException {
try {
log.debug("[{}] Lookup component descriptor", clazz);
return checkNotNull(componentDescriptorService.getComponent(clazz));
} catch (Exception e) {
throw handleException(e, false);
}
}
ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException {
try {
log.debug("[{}] Lookup component descriptor", clazz);
@ -698,7 +698,8 @@ public abstract class BaseController {
String strCustomerName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("unassignedCustomerId", strCustomerId);
metaData.putValue("unassignedCustomerName", strCustomerName);
} if (actionType == ActionType.ASSIGNED_TO_EDGE) {
}
if (actionType == ActionType.ASSIGNED_TO_EDGE) {
String strEdgeId = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("assignedEdgeId", strEdgeId);
} else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) {
@ -767,5 +768,46 @@ public abstract class BaseController {
return result;
}
protected void sendNotificationMsgToEdgeService(TenantId tenantId, EntityRelation relation, ActionType edgeEventAction) {
try {
sendNotificationMsgToEdgeService(tenantId, null, null, json.writeValueAsString(relation), EdgeEventType.RELATION, edgeEventAction);
} catch (Exception e) {
log.warn("Failed to push relation to core: {}", relation, 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, EdgeId edgeId, EntityId entityId, EdgeEventType edgeEventType, ActionType edgeEventAction) {
sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, edgeEventType, edgeEventAction);
}
private void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String entityBody, EdgeEventType edgeEventType, ActionType edgeEventAction) {
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());
if (entityId != null) {
builder.setEntityIdMSB(entityId.getId().getMostSignificantBits());
builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits());
builder.setEntityType(entityId.getEntityType().name());
}
if (edgeId != null) {
builder.setEdgeIdMSB(edgeId.getId().getMostSignificantBits());
builder.setEdgeIdLSB(edgeId.getId().getLeastSignificantBits());
}
if (entityBody != null) {
builder.setEntityBody(entityBody);
}
TransportProtos.EdgeNotificationMsgProto msg = builder.build();
tbClusterService.pushMsgToCore(tenantId, entityId != null ? entityId : tenantId,
TransportProtos.ToCoreMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null);
}
}

14
application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java

@ -51,10 +51,10 @@ public class ComponentDescriptorController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
@RequestMapping(value = "/components/{componentType}/{ruleChainType}", method = RequestMethod.GET)
@RequestMapping(value = "/components/{componentType}", method = RequestMethod.GET)
@ResponseBody
public List<ComponentDescriptor> getComponentDescriptorsByType(@PathVariable(value = "ruleChainType", required = false) String strRuleChainType,
@PathVariable("componentType") String strComponentType) throws ThingsboardException {
public List<ComponentDescriptor> getComponentDescriptorsByType(@PathVariable("componentType") String strComponentType,
@RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException {
checkParameter("componentType", strComponentType);
try {
return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType), getRuleChainType(strRuleChainType));
@ -64,10 +64,10 @@ public class ComponentDescriptorController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
@RequestMapping(value = "/components/{ruleChainType}", params = {"componentTypes"}, method = RequestMethod.GET)
@RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET)
@ResponseBody
public List<ComponentDescriptor> getComponentDescriptorsByTypes(@PathVariable(value = "ruleChainType", required = false) String strRuleChainType,
@RequestParam("componentTypes") String[] strComponentTypes) throws ThingsboardException {
public List<ComponentDescriptor> getComponentDescriptorsByTypes(@RequestParam("componentTypes") String[] strComponentTypes,
@RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException {
checkArrayParameter("componentTypes", strComponentTypes);
try {
Set<ComponentType> componentTypes = new HashSet<>();
@ -83,7 +83,7 @@ public class ComponentDescriptorController extends BaseController {
private RuleChainType getRuleChainType(String strRuleChainType) {
RuleChainType ruleChainType;
if (StringUtils.isEmpty(strRuleChainType)) {
ruleChainType = RuleChainType.SYSTEM;
ruleChainType = RuleChainType.CORE;
} else {
ruleChainType = RuleChainType.valueOf(strRuleChainType);
}

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

@ -34,6 +34,7 @@ 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;
@ -115,6 +116,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);
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), dashboard,
@ -138,6 +142,7 @@ public class DashboardController extends BaseController {
null,
ActionType.DELETED, null, strDashboardId);
sendNotificationMsgToEdgeService(getTenantId(), null, dashboardId, EdgeEventType.DASHBOARD, ActionType.DELETED);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD),
@ -496,6 +501,8 @@ 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);
return savedDashboard;
} catch (Exception e) {
@ -527,159 +534,10 @@ public class DashboardController extends BaseController {
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edge.getId().toString(), edge.getName());
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strDashboardId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/edges", method = RequestMethod.POST)
@ResponseBody
public Dashboard updateDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
edgeIds.add(new EdgeId(toUUID(strEdgeId)));
}
}
Set<EdgeId> addedEdgeIds = new HashSet<>();
Set<EdgeId> removedEdgeIds = new HashSet<>();
for (EdgeId edgeId : edgeIds) {
if (!dashboard.isAssignedToEdge(edgeId)) {
addedEdgeIds.add(edgeId);
}
}
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDashboard.getId(),
EdgeEventType.DASHBOARD, ActionType.UNASSIGNED_FROM_EDGE);
Set<ShortEdgeInfo> assignedEdges = dashboard.getAssignedEdges();
if (assignedEdges != null) {
for (ShortEdgeInfo edgeInfo : assignedEdges) {
if (!edgeIds.contains(edgeInfo.getEdgeId())) {
removedEdgeIds.add(edgeInfo.getEdgeId());
}
}
}
if (addedEdgeIds.isEmpty() && removedEdgeIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (EdgeId edgeId : addedEdgeIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
ShortEdgeInfo edgeInfo = savedDashboard.getAssignedEdgeInfo(edgeId);
logEntityAction(dashboardId, savedDashboard,
null,
ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle());
}
for (EdgeId edgeId : removedEdgeIds) {
ShortEdgeInfo edgeInfo = dashboard.getAssignedEdgeInfo(edgeId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
logEntityAction(dashboardId, dashboard,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strDashboardId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/edges/add", method = RequestMethod.POST)
@ResponseBody
public Dashboard addDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
if (!dashboard.isAssignedToEdge(edgeId)) {
edgeIds.add(edgeId);
}
}
}
if (edgeIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (EdgeId edgeId : edgeIds) {
savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
ShortEdgeInfo edgeInfo = savedDashboard.getAssignedEdgeInfo(edgeId);
logEntityAction(dashboardId, savedDashboard,
null,
ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle());
}
return savedDashboard;
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strDashboardId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/edges/remove", method = RequestMethod.POST)
@ResponseBody
public Dashboard removeDashboardEdges(@PathVariable(DASHBOARD_ID) String strDashboardId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
try {
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
if (dashboard.isAssignedToEdge(edgeId)) {
edgeIds.add(edgeId);
}
}
}
if (edgeIds.isEmpty()) {
return dashboard;
} else {
Dashboard savedDashboard = null;
for (EdgeId edgeId : edgeIds) {
ShortEdgeInfo edgeInfo = dashboard.getAssignedEdgeInfo(edgeId);
savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
logEntityAction(dashboardId, dashboard,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, edgeId.toString(), edgeInfo.getTitle());
}
return savedDashboard;
}
return savedDashboard;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DASHBOARD), null,
@ -697,17 +555,18 @@ public class DashboardController extends BaseController {
@PathVariable("edgeId") String strEdgeId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String sortOrder,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
checkEdgeId(edgeId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
} catch (Exception e) {
throw handleException(e);
}

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

@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.EntityType;
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.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
@ -50,11 +51,13 @@ 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.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.device.claim.ClaimResponse;
import org.thingsboard.server.dao.device.claim.ClaimResult;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
@ -117,6 +120,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);
logEntityAction(savedDevice.getId(), savedDevice,
savedDevice.getCustomerId(),
device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
@ -148,6 +154,8 @@ public class DeviceController extends BaseController {
device.getCustomerId(),
ActionType.DELETED, null, strDeviceId);
sendNotificationMsgToEdgeService(getTenantId(), null, deviceId, EdgeEventType.DEVICE, ActionType.DELETED);
deviceStateService.onDeviceDeleted(device);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE),
@ -271,6 +279,8 @@ public class DeviceController extends BaseController {
tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId()), null);
sendNotificationMsgToEdgeService(getTenantId(), null, device.getId(), EdgeEventType.DEVICE, ActionType.CREDENTIALS_UPDATED);
logEntityAction(device.getId(), device,
device.getCustomerId(),
ActionType.CREDENTIALS_UPDATED, null, deviceCredentials);
@ -572,6 +582,8 @@ 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);
return savedDevice;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), null,
@ -582,24 +594,27 @@ public class DeviceController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/device/{deviceId}", method = RequestMethod.DELETE)
@RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE)
@ResponseBody
public Device unassignDeviceFromEdge(@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
public Device unassignDeviceFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(DEVICE_ID, strDeviceId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId, Operation.UNASSIGN_FROM_EDGE);
if (device.getEdgeId() == null || device.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Device isn't assigned to any edge!");
}
Edge edge = checkEdgeId(device.getEdgeId(), Operation.READ);
Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId));
Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId, edgeId));
logEntityAction(deviceId, device,
device.getCustomerId(),
ActionType.UNASSIGNED_FROM_EDGE, null, strDeviceId, edge.getId().toString(), edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedDevice.getId(), EdgeEventType.DEVICE, ActionType.UNASSIGNED_FROM_EDGE);
return savedDevice;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), null,
@ -613,24 +628,21 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/edge/{edgeId}/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<Device> getEdgeDevices(
@PathVariable("edgeId") String strEdgeId,
@PathVariable(EDGE_ID) String strEdgeId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
@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);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
if (type != null && type.trim().length()>0) {
return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
} else {
return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
}
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
} catch (Exception e) {
throw handleException(e);
}

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

@ -15,6 +15,7 @@
*/
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;
@ -41,6 +42,7 @@ 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.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.service.security.model.SecurityUser;
@ -79,21 +81,29 @@ public class EdgeController extends BaseController {
edge.setTenantId(tenantId);
boolean created = edge.getId() == null;
RuleChain defaultRootEdgeRuleChain = null;
if (created) {
defaultRootEdgeRuleChain = ruleChainService.getDefaultRootEdgeRuleChain(tenantId);
if (defaultRootEdgeRuleChain == null) {
throw new DataValidationException("Root edge rule chain is not available!");
}
}
Operation operation = created ? Operation.CREATE : Operation.WRITE;
accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation,
edge.getId(), edge);
Edge result = checkNotNull(edgeService.saveEdge(edge));
Edge savedEdge = checkNotNull(edgeService.saveEdge(edge));
if (created) {
RuleChain defaultRootEdgeRuleChain = ruleChainService.getDefaultRootEdgeRuleChain(tenantId);
ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), result.getId());
edgeService.setRootRuleChain(tenantId, result, defaultRootEdgeRuleChain.getId());
ruleChainService.assignRuleChainToEdge(tenantId, defaultRootEdgeRuleChain.getId(), savedEdge.getId());
edgeNotificationService.setEdgeRootRuleChain(tenantId, savedEdge, defaultRootEdgeRuleChain.getId());
edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId());
}
logEntityAction(result.getId(), result, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
return result;
logEntityAction(savedEdge.getId(), savedEdge, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
return savedEdge;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.EDGE), edge,
null, edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
@ -273,8 +283,7 @@ public class EdgeController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET)
@ResponseBody
public Edge getTenantEdge(
@RequestParam String edgeName) throws ThingsboardException {
public Edge getTenantEdge(@RequestParam String edgeName) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName));
@ -299,7 +308,7 @@ public class EdgeController extends BaseController {
accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.WRITE,
edge.getId(), edge);
Edge updatedEdge = edgeService.setRootRuleChain(getTenantId(), edge, ruleChainId);
Edge updatedEdge = edgeNotificationService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId);
logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null);

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

@ -24,7 +24,9 @@ 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;
@ -63,10 +65,13 @@ public class EntityRelationController extends BaseController {
relation.setTypeGroup(RelationTypeGroup.COMMON);
}
relationService.saveRelation(getTenantId(), relation);
logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
ActionType.RELATION_ADD_OR_UPDATE, null, relation);
logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
ActionType.RELATION_ADD_OR_UPDATE, null, relation);
sendNotificationMsgToEdgeService(getTenantId(), relation, ActionType.RELATION_ADD_OR_UPDATE);
} catch (Exception e) {
logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
ActionType.RELATION_ADD_OR_UPDATE, e, relation);
@ -104,6 +109,8 @@ public class EntityRelationController extends BaseController {
ActionType.RELATION_DELETED, null, relation);
logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
ActionType.RELATION_DELETED, null, relation);
sendNotificationMsgToEdgeService(getTenantId(), relation, ActionType.RELATION_DELETED);
} catch (Exception e) {
logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
ActionType.RELATION_DELETED, e, relation);
@ -125,6 +132,8 @@ 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);

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

@ -30,9 +30,15 @@ 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.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
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.entityview.EntityViewSearchQuery;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
@ -44,6 +50,7 @@ import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
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.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -123,6 +130,9 @@ 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);
return savedEntityView;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null,
@ -192,6 +202,8 @@ public class EntityViewController extends BaseController {
entityViewService.deleteEntityView(getTenantId(), entityViewId);
logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
ActionType.DELETED, null, strEntityViewId);
sendNotificationMsgToEdgeService(getTenantId(), null, entityViewId, EdgeEventType.ENTITY_VIEW, ActionType.DELETED);
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ENTITY_VIEW),
null,
@ -448,6 +460,10 @@ public class EntityViewController extends BaseController {
logEntityAction(entityViewId, savedEntityView,
savedEntityView.getCustomerId(),
ActionType.ASSIGNED_TO_EDGE, null, strEntityViewId, strEdgeId, edge.getName());
sendNotificationMsgToEdgeService(getTenantId(), edgeId, savedEntityView.getId(),
EdgeEventType.ENTITY_VIEW, ActionType.ASSIGNED_TO_EDGE);
return savedEntityView;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
@ -458,22 +474,27 @@ public class EntityViewController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/entityView/{entityViewId}", method = RequestMethod.DELETE)
@RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE)
@ResponseBody
public EntityView unassignEntityViewFromEdge(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
public EntityView unassignEntityViewFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
try {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
Edge edge = checkEdgeId(edgeId, Operation.READ);
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_EDGE);
if (entityView.getEdgeId() == null || entityView.getEdgeId().getId().equals(ModelConstants.NULL_UUID)) {
throw new IncorrectParameterException("Entity View isn't assigned to any edge!");
}
Edge edge = checkEdgeId(entityView.getEdgeId(), Operation.READ);
EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId));
EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId, edgeId));
logEntityAction(entityViewId, entityView,
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);
return savedEntityView;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
@ -487,24 +508,21 @@ public class EntityViewController extends BaseController {
@RequestMapping(value = "/edge/{edgeId}/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<EntityView> getEdgeEntityViews(
@PathVariable("edgeId") String strEdgeId,
@PathVariable(EDGE_ID) String strEdgeId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
@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);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
if (type != null && type.trim().length()>0) {
return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink));
} else {
return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
}
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
} catch (Exception e) {
throw handleException(e);
}

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

@ -39,9 +39,9 @@ import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Event;
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.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
@ -65,7 +65,6 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -135,13 +134,21 @@ public class RuleChainController extends BaseController {
RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
if (RuleChainType.CORE.equals(savedRuleChain.getType())) {
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
}
logEntityAction(savedRuleChain.getId(), savedRuleChain,
null,
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);
}
return savedRuleChain;
} catch (Exception e) {
@ -208,12 +215,21 @@ public class RuleChainController extends BaseController {
RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE);
RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData));
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
if (RuleChainType.CORE.equals(ruleChain.getType())) {
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
}
logEntityAction(ruleChain.getId(), ruleChain,
null,
ActionType.UPDATED, null, ruleChainMetaData);
if (RuleChainType.EDGE.equals(ruleChain.getType())) {
sendNotificationMsgToEdgeService(ruleChain.getTenantId(),
null,
ruleChain.getId(), EdgeEventType.RULE_CHAIN,
ActionType.UPDATED);
}
return savedRuleChainMetaData;
} catch (Exception e) {
@ -241,7 +257,7 @@ public class RuleChainController extends BaseController {
RuleChainType type = RuleChainType.valueOf(typeStr);
return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink));
} else {
return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink));
return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink));
}
} catch (Exception e) {
throw handleException(e);
@ -265,15 +281,22 @@ public class RuleChainController extends BaseController {
referencingRuleChainIds.remove(ruleChain.getId());
referencingRuleChainIds.forEach(referencingRuleChainId ->
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
if (RuleChainType.CORE.equals(ruleChain.getType())) {
referencingRuleChainIds.forEach(referencingRuleChainId ->
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
}
logEntityAction(ruleChainId, ruleChain,
null,
ActionType.DELETED, null, strRuleChainId);
if (RuleChainType.EDGE.equals(ruleChain.getType())) {
sendNotificationMsgToEdgeService(ruleChain.getTenantId(), null,
ruleChain.getId(), EdgeEventType.RULE_CHAIN, ActionType.DELETED);
}
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN),
null,
@ -403,6 +426,8 @@ 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);
return savedRuleChain;
} catch (Exception e) {
@ -434,6 +459,9 @@ 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);
return savedRuleChain;
} catch (Exception e) {
@ -445,191 +473,77 @@ public class RuleChainController extends BaseController {
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/edges", method = RequestMethod.POST)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public RuleChain updateRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
public PageData<RuleChain> getEdgeRuleChains(
@PathVariable("edgeId") 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("edgeId", strEdgeId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
edgeIds.add(new EdgeId(toUUID(strEdgeId)));
}
}
Set<EdgeId> addedEdgeIds = new HashSet<>();
Set<EdgeId> removedEdgeIds = new HashSet<>();
for (EdgeId edgeId : edgeIds) {
if (!ruleChain.isAssignedToEdge(edgeId)) {
addedEdgeIds.add(edgeId);
}
}
Set<ShortEdgeInfo> assignedEdges = ruleChain.getAssignedEdges();
if (assignedEdges != null) {
for (ShortEdgeInfo edgeInfo : assignedEdges) {
if (!edgeIds.contains(edgeInfo.getEdgeId())) {
removedEdgeIds.add(edgeInfo.getEdgeId());
}
}
}
if (addedEdgeIds.isEmpty() && removedEdgeIds.isEmpty()) {
return ruleChain;
} else {
RuleChain savedRuleChain = null;
for (EdgeId edgeId : addedEdgeIds) {
savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
ShortEdgeInfo edgeInfo = savedRuleChain.getAssignedEdgeInfo(edgeId);
logEntityAction(ruleChainId, savedRuleChain,
null,
ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle());
}
for (EdgeId edgeId : removedEdgeIds) {
ShortEdgeInfo edgeInfo = ruleChain.getAssignedEdgeInfo(edgeId);
savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false));
logEntityAction(ruleChainId, ruleChain,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle());
}
return savedRuleChain;
}
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(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink).get());
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
null,
ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/edges/add", method = RequestMethod.POST)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/defaultRootEdge", method = RequestMethod.POST)
@ResponseBody
public RuleChain addRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
public RuleChain setDefaultRootEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
if (!ruleChain.isAssignedToEdge(edgeId)) {
edgeIds.add(edgeId);
}
}
}
if (edgeIds.isEmpty()) {
return ruleChain;
} else {
RuleChain savedRuleChain = null;
for (EdgeId edgeId : edgeIds) {
savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
ShortEdgeInfo edgeInfo = savedRuleChain.getAssignedEdgeInfo(edgeId);
logEntityAction(ruleChainId, savedRuleChain,
null,
ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle());
}
return savedRuleChain;
}
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
ruleChainService.setDefaultRootEdgeRuleChain(getTenantId(), ruleChainId);
return ruleChain;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
logEntityAction(emptyId(EntityType.RULE_CHAIN),
null,
ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId);
null,
ActionType.UPDATED, e, strRuleChainId);
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/edges/remove", method = RequestMethod.POST)
@RequestMapping(value = "/ruleChain/{ruleChainId}/defaultEdge", method = RequestMethod.POST)
@ResponseBody
public RuleChain removeRuleChainEdges(@PathVariable(RULE_CHAIN_ID) String strRuleChainId,
@RequestBody String[] strEdgeIds) throws ThingsboardException {
public RuleChain addDefaultEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.UNASSIGN_FROM_EDGE);
Set<EdgeId> edgeIds = new HashSet<>();
if (strEdgeIds != null) {
for (String strEdgeId : strEdgeIds) {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
if (ruleChain.isAssignedToEdge(edgeId)) {
edgeIds.add(edgeId);
}
}
}
if (edgeIds.isEmpty()) {
return ruleChain;
} else {
RuleChain savedRuleChain = null;
for (EdgeId edgeId : edgeIds) {
ShortEdgeInfo edgeInfo = ruleChain.getAssignedEdgeInfo(edgeId);
savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false));
logEntityAction(ruleChainId, ruleChain,
null,
ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, edgeId.toString(), edgeInfo.getTitle());
}
return savedRuleChain;
}
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
ruleChainService.addDefaultEdgeRuleChain(getTenantId(), ruleChainId);
return ruleChain;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
logEntityAction(emptyId(EntityType.RULE_CHAIN),
null,
ActionType.UNASSIGNED_FROM_EDGE, e, strRuleChainId);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<RuleChain> getEdgeRuleChains(
@PathVariable("edgeId") 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("edgeId", 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(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
} catch (Exception e) {
null,
ActionType.UPDATED, e, strRuleChainId);
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/defaultRootEdge", method = RequestMethod.POST)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/defaultEdge", method = RequestMethod.DELETE)
@ResponseBody
public RuleChain setDefaultRootEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
public RuleChain removeDefaultEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
ruleChainService.setDefaultRootEdgeRuleChain(getTenantId(), ruleChainId);
ruleChainService.removeDefaultEdgeRuleChain(getTenantId(), ruleChainId);
return ruleChain;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.RULE_CHAIN),
@ -639,4 +553,16 @@ public class RuleChainController extends BaseController {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/defaultEdgeRuleChains", method = RequestMethod.GET)
@ResponseBody
public List<RuleChain> getDefaultEdgeRuleChains() throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId)).get();
} catch (Exception e) {
throw handleException(e);
}
}
}

17
application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java

@ -40,7 +40,6 @@ import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@ -65,7 +64,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
private Map<String, ComponentDescriptor> components = new HashMap<>();
private Map<ComponentType, List<ComponentDescriptor>> systemComponentsMap = new HashMap<>();
private Map<ComponentType, List<ComponentDescriptor>> coreComponentsMap = new HashMap<>();
private Map<ComponentType, List<ComponentDescriptor>> edgeComponentsMap = new HashMap<>();
@ -116,8 +115,8 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
}
private void putComponentIntoMaps(ComponentType type, RuleNode ruleNodeAnnotation, ComponentDescriptor component) {
if (ruleChainTypeContainsArray(RuleChainType.SYSTEM, ruleNodeAnnotation.ruleChainTypes())) {
systemComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
if (ruleChainTypeContainsArray(RuleChainType.CORE, ruleNodeAnnotation.ruleChainTypes())) {
coreComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
}
if (ruleChainTypeContainsArray(RuleChainType.EDGE, ruleNodeAnnotation.ruleChainTypes())) {
edgeComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
@ -225,9 +224,9 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
@Override
public List<ComponentDescriptor> getComponents(ComponentType type, RuleChainType ruleChainType) {
if (RuleChainType.SYSTEM.equals(ruleChainType)) {
if (systemComponentsMap.containsKey(type)) {
return Collections.unmodifiableList(systemComponentsMap.get(type));
if (RuleChainType.CORE.equals(ruleChainType)) {
if (coreComponentsMap.containsKey(type)) {
return Collections.unmodifiableList(coreComponentsMap.get(type));
} else {
return Collections.emptyList();
}
@ -245,8 +244,8 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
@Override
public List<ComponentDescriptor> getComponents(Set<ComponentType> types, RuleChainType ruleChainType) {
if (RuleChainType.SYSTEM.equals(ruleChainType)) {
return getComponents(types, systemComponentsMap);
if (RuleChainType.CORE.equals(ruleChainType)) {
return getComponents(types, coreComponentsMap);
} else if (RuleChainType.EDGE.equals(ruleChainType)) {
return getComponents(types, edgeComponentsMap);
} else {

1
application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java

@ -35,5 +35,4 @@ public interface ComponentDiscoveryService {
List<ComponentDescriptor> getComponents(Set<ComponentType> types, RuleChainType ruleChainType);
Optional<ComponentDescriptor> getComponent(String clazz);
}

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

@ -0,0 +1,322 @@
/**
* 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;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
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.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.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.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.gen.transport.TransportProtos;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
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
@Slf4j
public class DefaultEdgeNotificationService implements EdgeNotificationService {
private static final ObjectMapper mapper = new ObjectMapper();
@Autowired
private EdgeService edgeService;
@Autowired
private RuleChainService ruleChainService;
@Autowired
private AlarmService alarmService;
@Autowired
private RelationService relationService;
@Autowired
private EdgeEventService edgeEventService;
@Autowired
private DbCallbackExecutorService dbCallbackExecutorService;
private ExecutorService tsCallBackExecutor;
@PostConstruct
public void initExecutor() {
tsCallBackExecutor = Executors.newSingleThreadExecutor();
}
@PreDestroy
public void shutdownExecutor() {
if (tsCallBackExecutor != null) {
tsCallBackExecutor.shutdownNow();
}
}
@Override
public PageData<EdgeEvent> findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
return edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink);
}
@Override
public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException {
edge.setRootRuleChainId(ruleChainId);
Edge savedEdge = edgeService.saveEdge(edge);
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, ActionType.UPDATED, ruleChainId, null);
return savedEdge;
}
private void saveEdgeEvent(TenantId tenantId,
EdgeId edgeId,
EdgeEventType edgeEventType,
ActionType edgeEventAction,
EntityId entityId,
JsonNode entityBody) {
log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], edgeEventType [{}], edgeEventAction[{}], entityId [{}], entityBody [{}]",
tenantId, edgeId, edgeEventType, edgeEventAction, entityId, entityBody);
EdgeEvent edgeEvent = new EdgeEvent();
edgeEvent.setEdgeId(edgeId);
edgeEvent.setTenantId(tenantId);
edgeEvent.setEdgeEventType(edgeEventType);
edgeEvent.setEdgeEventAction(edgeEventAction.name());
if (entityId != null) {
edgeEvent.setEntityId(entityId.getId());
}
edgeEvent.setEntityBody(entityBody);
edgeEventService.saveAsync(edgeEvent);
}
@Override
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:
case ASSET:
case DEVICE:
case ENTITY_VIEW:
case DASHBOARD:
case RULE_CHAIN:
processEntities(tenantId, edgeNotificationMsg);
break;
case ALARM:
processAlarm(tenantId, edgeNotificationMsg);
break;
case RELATION:
processRelation(tenantId, edgeNotificationMsg);
break;
default:
log.debug("Edge event type [{}] is not designed to be pushed to edge", edgeEventType);
}
} catch (Exception e) {
callback.onFailure(e);
log.error("Can't push to edge updates, edgeNotificationMsg [{}]", edgeNotificationMsg, e);
} finally {
callback.onSuccess();
}
}
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:
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);
}
} catch (Exception e) {
log.error("[{}] Failed to push event to edge, edgeId [{}], edgeEventType [{}], edgeEventActionType [{}], entityId [{}]",
tenantId, edgeId, edgeEventType, edgeEventActionType, entityId, e);
}
}
}
return null;
}, 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);
}
}
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
break;
}
}
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());
Futures.transform(relatedEdgeIdsByEntityIdFuture, relatedEdgeIdsByEntityId -> {
if (relatedEdgeIdsByEntityId != null) {
for (EdgeId edgeId : relatedEdgeIdsByEntityId) {
saveEdgeEvent(tenantId,
edgeId,
EdgeEventType.ALARM,
ActionType.valueOf(edgeNotificationMsg.getEdgeEventAction()),
alarmId,
null);
}
}
return null;
}, dbCallbackExecutorService);
}
}
return null;
}, 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);
}
}
}
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();
}
}, 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());
}
}
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:
return EdgeEventType.DEVICE;
case ASSET:
return EdgeEventType.ASSET;
case ENTITY_VIEW:
return EdgeEventType.ENTITY_VIEW;
default:
log.info("Unsupported entity type: [{}]", entityType);
return null;
}
}
}

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

@ -16,6 +16,7 @@
package org.thingsboard.server.service.edge;
import lombok.Data;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@ -25,18 +26,27 @@ 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.EdgeService;
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.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.EntityDataMsgConstructor;
import org.thingsboard.server.service.edge.rpc.constructor.EntityViewUpdateMsgConstructor;
import org.thingsboard.server.service.edge.rpc.init.InitEdgeService;
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.init.SyncEdgeService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.state.DeviceStateService;
@Component
@Data
@ -46,6 +56,10 @@ public class EdgeContextComponent {
@Autowired
private EdgeService edgeService;
@Lazy
@Autowired
private EdgeNotificationService edgeNotificationService;
@Lazy
@Autowired
private AssetService assetService;
@ -54,6 +68,10 @@ public class EdgeContextComponent {
@Autowired
private DeviceService deviceService;
@Lazy
@Autowired
private DeviceCredentialsService deviceCredentialsService;
@Lazy
@Autowired
private EntityViewService entityViewService;
@ -78,13 +96,29 @@ public class EdgeContextComponent {
@Autowired
private DashboardService dashboardService;
@Lazy
@Autowired
private RuleChainService ruleChainService;
@Lazy
@Autowired
private UserService userService;
@Lazy
@Autowired
private ActorService actorService;
@Lazy
@Autowired
private InitEdgeService initEdgeService;
private DeviceStateService deviceStateService;
@Lazy
@Autowired
private TbClusterService tbClusterService;
@Lazy
@Autowired
private SyncEdgeService syncEdgeService;
@Lazy
@Autowired
@ -110,7 +144,23 @@ public class EdgeContextComponent {
@Autowired
private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor;
@Lazy
@Autowired
private UserUpdateMsgConstructor userUpdateMsgConstructor;
@Lazy
@Autowired
private RelationUpdateMsgConstructor relationUpdateMsgConstructor;
@Lazy
@Autowired
private EntityDataMsgConstructor entityDataMsgConstructor;
@Lazy
@Autowired
private EdgeEventStorageSettings edgeEventStorageSettings;
@Autowired
@Getter
private DbCallbackExecutorService dbCallbackExecutor;
}

37
application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java

@ -0,0 +1,37 @@
/**
* 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;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
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.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.io.IOException;
public interface EdgeNotificationService {
PageData<EdgeEvent> findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException;
void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback);
}

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

File diff suppressed because it is too large

12
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java

@ -18,6 +18,7 @@ package org.thingsboard.server.service.edge.rpc.constructor;
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.gen.edge.AssetUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
@ -28,9 +29,20 @@ public class AssetUpdateMsgConstructor {
public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset) {
AssetUpdateMsg.Builder builder = AssetUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(asset.getId().getId().getMostSignificantBits())
.setIdLSB(asset.getId().getId().getLeastSignificantBits())
.setName(asset.getName())
.setType(asset.getType());
if (asset.getLabel() != null) {
builder.setLabel(asset.getLabel());
}
return builder.build();
}
public AssetUpdateMsg constructAssetDeleteMsg(AssetId assetId) {
return AssetUpdateMsg.newBuilder()
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(assetId.getId().getMostSignificantBits())
.setIdLSB(assetId.getId().getLeastSignificantBits()).build();
}
}

8
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java

@ -19,6 +19,7 @@ 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.DashboardId;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.DashboardUpdateMsg;
@ -42,4 +43,11 @@ public class DashboardUpdateMsgConstructor {
return builder.build();
}
public DashboardUpdateMsg constructDashboardDeleteMsg(DashboardId dashboardId) {
return DashboardUpdateMsg.newBuilder()
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(dashboardId.getId().getMostSignificantBits())
.setIdLSB(dashboardId.getId().getLeastSignificantBits()).build();
}
}

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

@ -16,8 +16,12 @@
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;
@ -25,11 +29,41 @@ import org.thingsboard.server.gen.edge.UpdateMsgType;
@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();
}
}

56
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.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.edge.rpc.constructor;
import com.google.gson.JsonElement;
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.EntityDataProto;
@Component
@Slf4j
public class EntityDataMsgConstructor {
public EntityDataProto constructEntityDataMsg(EntityId entityId, ActionType actionType, JsonElement entityData) {
EntityDataProto.Builder builder = EntityDataProto.newBuilder()
.setEntityIdMSB(entityId.getId().getMostSignificantBits())
.setEntityIdLSB(entityId.getId().getLeastSignificantBits())
.setEntityType(entityId.getEntityType().name());
switch (actionType) {
case TIMESERIES_UPDATED:
try {
builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(entityData));
} catch (Exception e) {
log.warn("Can't convert to telemetry proto, entityData [{}]", entityData, e);
}
break;
case ATTRIBUTES_UPDATED:
try {
builder.setPostAttributesMsg(JsonConverter.convertToAttributesProto(entityData));
} 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:
}
return builder.build();
}
}

53
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java

@ -16,16 +16,10 @@
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.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.DeviceId;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.gen.edge.EdgeEntityType;
import org.thingsboard.server.gen.edge.EntityViewUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
@ -33,35 +27,34 @@ import org.thingsboard.server.gen.edge.UpdateMsgType;
@Slf4j
public class EntityViewUpdateMsgConstructor {
@Autowired
private DeviceService deviceService;
@Autowired
private AssetService assetService;
public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView) {
String relatedName;
String relatedType;
org.thingsboard.server.gen.edge.EntityType relatedEntityType;
if (entityView.getEntityId().getEntityType().equals(EntityType.DEVICE)) {
Device device = deviceService.findDeviceById(entityView.getTenantId(), new DeviceId(entityView.getEntityId().getId()));
relatedName = device.getName();
relatedType = device.getType();
relatedEntityType = org.thingsboard.server.gen.edge.EntityType.DEVICE;
} else {
Asset asset = assetService.findAssetById(entityView.getTenantId(), new AssetId(entityView.getEntityId().getId()));
relatedName = asset.getName();
relatedType = asset.getType();
relatedEntityType = org.thingsboard.server.gen.edge.EntityType.ASSET;
EdgeEntityType entityType;
switch (entityView.getEntityId().getEntityType()) {
case DEVICE:
entityType = EdgeEntityType.DEVICE;
break;
case ASSET:
entityType = EdgeEntityType.ASSET;
break;
default:
throw new RuntimeException("Unsupported entity type [" + entityView.getEntityId().getEntityType() + "]");
}
EntityViewUpdateMsg.Builder builder = EntityViewUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(entityView.getId().getId().getMostSignificantBits())
.setIdLSB(entityView.getId().getId().getLeastSignificantBits())
.setName(entityView.getName())
.setType(entityView.getType())
.setRelatedName(relatedName)
.setRelatedType(relatedType)
.setRelatedEntityType(relatedEntityType);
.setIdMSB(entityView.getEntityId().getId().getMostSignificantBits())
.setIdLSB(entityView.getEntityId().getId().getLeastSignificantBits())
.setEntityType(entityType);
return builder.build();
}
public EntityViewUpdateMsg constructEntityViewDeleteMsg(EntityViewId entityViewId) {
return EntityViewUpdateMsg.newBuilder()
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(entityViewId.getId().getMostSignificantBits())
.setIdLSB(entityViewId.getId().getLeastSignificantBits()).build();
}
}

45
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java

@ -0,0 +1,45 @@
/**
* 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.relation.EntityRelation;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.gen.edge.RelationUpdateMsg;
import org.thingsboard.server.gen.edge.UpdateMsgType;
@Component
@Slf4j
public class RelationUpdateMsgConstructor {
public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) {
RelationUpdateMsg.Builder builder = RelationUpdateMsg.newBuilder()
.setMsgType(msgType)
.setFromIdMSB(entityRelation.getFrom().getId().getMostSignificantBits())
.setFromIdLSB(entityRelation.getFrom().getId().getLeastSignificantBits())
.setFromEntityType(entityRelation.getFrom().getEntityType().name())
.setToIdMSB(entityRelation.getTo().getId().getMostSignificantBits())
.setToIdLSB(entityRelation.getTo().getId().getLeastSignificantBits())
.setToEntityType(entityRelation.getTo().getEntityType().name())
.setType(entityRelation.getType())
.setAdditionalInfo(JacksonUtil.toString(entityRelation.getAdditionalInfo()));
if (entityRelation.getTypeGroup() != null) {
builder.setTypeGroup(entityRelation.getTypeGroup().name());
}
return builder.build();
}
}

14
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java

@ -19,7 +19,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
@ -42,13 +42,13 @@ public class RuleChainUpdateMsgConstructor {
private static final ObjectMapper objectMapper = new ObjectMapper();
public RuleChainUpdateMsg constructRuleChainUpdatedMsg(Edge edge, UpdateMsgType msgType, RuleChain ruleChain) {
public RuleChainUpdateMsg constructRuleChainUpdatedMsg(RuleChainId edgeRootRuleChainId, UpdateMsgType msgType, RuleChain ruleChain) {
RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder()
.setMsgType(msgType)
.setIdMSB(ruleChain.getId().getId().getMostSignificantBits())
.setIdLSB(ruleChain.getId().getId().getLeastSignificantBits())
.setName(ruleChain.getName())
.setRoot(ruleChain.getId().equals(edge.getRootRuleChainId()))
.setRoot(ruleChain.getId().equals(edgeRootRuleChainId))
.setDebugMode(ruleChain.isDebugMode())
.setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration()));
if (ruleChain.getFirstRuleNodeId() != null) {
@ -125,7 +125,6 @@ public class RuleChainUpdateMsgConstructor {
.build();
}
private RuleNodeProto constructNode(RuleNode node) throws JsonProcessingException {
return RuleNodeProto.newBuilder()
.setIdMSB(node.getId().getId().getMostSignificantBits())
@ -138,4 +137,11 @@ public class RuleChainUpdateMsgConstructor {
.build();
}
public RuleChainUpdateMsg constructRuleChainDeleteMsg(RuleChainId ruleChainId) {
return RuleChainUpdateMsg.newBuilder()
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(ruleChainId.getId().getMostSignificantBits())
.setIdLSB(ruleChainId.getId().getLeastSignificantBits()).build();
}
}

72
application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.User;
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.UserUpdateMsg;
@Component
@Slf4j
public class UserUpdateMsgConstructor {
@Autowired
private UserService userService;
public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) {
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);
if (user.getFirstName() != null) {
builder.setFirstName(user.getFirstName());
}
if (user.getLastName() != null) {
builder.setLastName(user.getLastName());
}
if (user.getAdditionalInfo() != null) {
builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo()));
}
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();
}
public UserUpdateMsg constructUserDeleteMsg(UserId userId) {
return UserUpdateMsg.newBuilder()
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(userId.getId().getMostSignificantBits())
.setIdLSB(userId.getId().getLeastSignificantBits()).build();
}
}

271
application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java

@ -1,271 +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.init;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.RuleChainId;
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.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.rule.RuleChainService;
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.ResponseMsg;
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.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.RuleChainUpdateMsgConstructor;
import java.util.UUID;
@Service
@Slf4j
public class DefaultInitEdgeService implements InitEdgeService {
@Autowired
private RuleChainService ruleChainService;
@Autowired
private DeviceService deviceService;
@Autowired
private AssetService assetService;
@Autowired
private EntityViewService entityViewService;
@Autowired
private DashboardService dashboardService;
@Autowired
private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor;
@Autowired
private DeviceUpdateMsgConstructor deviceUpdateMsgConstructor;
@Autowired
private AssetUpdateMsgConstructor assetUpdateMsgConstructor;
@Autowired
private EntityViewUpdateMsgConstructor entityViewUpdateMsgConstructor;
@Autowired
private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor;
@Override
public void init(Edge edge, StreamObserver<ResponseMsg> outputStream) {
initRuleChains(edge, outputStream);
initDevices(edge, outputStream);
initAssets(edge, outputStream);
initEntityViews(edge, outputStream);
initDashboards(edge, outputStream);
}
private void initDevices(Edge edge, StreamObserver<ResponseMsg> outputStream) {
try {
PageLink pageLink = new PageLink(100);
PageData<Device> pageData;
do {
pageData = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
if (!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());
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge device(s) on init!");
}
}
private void initAssets(Edge edge, StreamObserver<ResponseMsg> outputStream) {
try {
PageLink pageLink = new PageLink(100);
PageData<Asset> pageData;
do {
pageData = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
if (!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());
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge asset(s) on init!");
}
}
private void initEntityViews(Edge edge, StreamObserver<ResponseMsg> outputStream) {
try {
PageLink pageLink = new PageLink(100);
PageData<EntityView> pageData;
do {
pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
if (!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());
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge entity view(s) on init!");
}
}
private void initDashboards(Edge edge, StreamObserver<ResponseMsg> outputStream) {
try {
TimePageLink pageLink = new TimePageLink(100);
PageData<DashboardInfo> pageData;
do {
pageData = dashboardService.findDashboardsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
if (!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());
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge dashboard(s) on init!");
}
}
private void initRuleChains(Edge edge, StreamObserver<ResponseMsg> outputStream) {
try {
TimePageLink pageLink = new TimePageLink(100);
PageData<RuleChain> pageData;
do {
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), pageLink);
if (!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,
UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE,
ruleChain);
EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder()
.setRuleChainUpdateMsg(ruleChainUpdateMsg)
.build();
outputStream.onNext(ResponseMsg.newBuilder()
.setEntityUpdateMsg(entityUpdateMsg)
.build());
}
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
} catch (Exception e) {
log.error("Exception during loading edge rule chain(s) on init!");
}
}
@Override
public void initRuleChainMetadata(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());
}
}
}
}

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

@ -0,0 +1,398 @@
/**
* 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.init;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.Device;
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.edge.Edge;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
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.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.dao.asset.AssetService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
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.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.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 java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@Service
@Slf4j
public class DefaultSyncEdgeService implements SyncEdgeService {
@Autowired
private RuleChainService ruleChainService;
@Autowired
private RelationService relationService;
@Autowired
private DeviceService deviceService;
@Autowired
private AssetService assetService;
@Autowired
private EntityViewService entityViewService;
@Autowired
private DashboardService dashboardService;
@Autowired
private UserService userService;
@Autowired
private RuleChainUpdateMsgConstructor ruleChainUpdateMsgConstructor;
@Autowired
private DeviceUpdateMsgConstructor deviceUpdateMsgConstructor;
@Autowired
private AssetUpdateMsgConstructor assetUpdateMsgConstructor;
@Autowired
private EntityViewUpdateMsgConstructor entityViewUpdateMsgConstructor;
@Autowired
private DashboardUpdateMsgConstructor dashboardUpdateMsgConstructor;
@Autowired
private UserUpdateMsgConstructor userUpdateMsgConstructor;
@Autowired
private RelationUpdateMsgConstructor relationUpdateMsgConstructor;
@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());
}
private ListenableFuture<Void> syncRuleChains(EdgeContextComponent ctx, Edge edge, Set<EntityId> pushedEntityIds, StreamObserver<ResponseMsg> outputStream) {
try {
ListenableFuture<PageData<RuleChain>> future = ruleChainService.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
return Futures.transform(future, pageData -> {
try {
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());
}
}
} catch (Exception e) {
log.error("Exception during loading edge rule chain(s) on sync!", e);
}
return null;
}, ctx.getDbCallbackExecutor());
} 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) {
try {
ListenableFuture<PageData<Device>> future = deviceService.findDevicesByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
return Futures.transform(future, pageData -> {
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());
}
}
return null;
}, ctx.getDbCallbackExecutor());
} 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) {
try {
ListenableFuture<PageData<Asset>> future = assetService.findAssetsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
return Futures.transform(future, pageData -> {
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());
}
}
return null;
}, ctx.getDbCallbackExecutor());
} 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) {
try {
ListenableFuture<PageData<EntityView>> future = entityViewService.findEntityViewsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
return Futures.transform(future, pageData -> {
try {
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());
}
}
} catch (Exception e) {
log.error("Exception during loading edge entity view(s) on sync!", e);
}
return null;
}, ctx.getDbCallbackExecutor());
} 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) {
try {
ListenableFuture<PageData<DashboardInfo>> future = dashboardService.findDashboardsByTenantIdAndEdgeId(edge.getTenantId(), edge.getId(), new TimePageLink(Integer.MAX_VALUE));
return Futures.transform(future, pageData -> {
try {
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());
}
}
} catch (Exception e) {
log.error("Exception during loading edge dashboard(s) on sync!", e);
}
return null;
}, ctx.getDbCallbackExecutor());
} 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) {
try {
PageData<User> pageData = userService.findTenantAdmins(edge.getTenantId(), new PageLink(Integer.MAX_VALUE));
pushUsersToEdge(pageData, edge, pushedEntityIds, outputStream);
if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) {
pageData = userService.findCustomerUsers(edge.getTenantId(), edge.getCustomerId(), new PageLink(Integer.MAX_VALUE));
pushUsersToEdge(pageData, edge, pushedEntityIds, outputStream);
}
} 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) {
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());
}
}
}
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);
}
}
}
if (!uniqueEntityRelations.isEmpty()) {
log.trace("[{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), uniqueEntityRelations.size());
for (EntityRelation relation : uniqueEntityRelations) {
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());
} 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);
}
}
private ListenableFuture<List<EntityRelation>> syncRelations(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());
}
}
}
}

7
application/src/main/java/org/thingsboard/server/service/edge/rpc/init/InitEdgeService.java → application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java

@ -19,10 +19,11 @@ import io.grpc.stub.StreamObserver;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.gen.edge.ResponseMsg;
import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg;
import org.thingsboard.server.service.edge.EdgeContextComponent;
public interface InitEdgeService {
public interface SyncEdgeService {
void init(Edge edge, StreamObserver<ResponseMsg> outputStream);
void sync(EdgeContextComponent ctx, Edge edge, StreamObserver<ResponseMsg> outputStream);
void initRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver<ResponseMsg> outputStream);
void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver<ResponseMsg> outputStream);
}

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

@ -36,6 +36,7 @@ import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
@ -109,22 +110,7 @@ public class InstallScripts {
Path tenantChainsDir = getTenantRuleChainsDir();
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(tenantChainsDir, path -> path.toString().endsWith(InstallScripts.JSON_EXT))) {
dirStream.forEach(
path -> {
try {
JsonNode ruleChainJson = objectMapper.readTree(path.toFile());
RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class);
RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class);
ruleChain.setTenantId(tenantId);
ruleChain = ruleChainService.saveRuleChain(ruleChain);
ruleChainMetaData.setRuleChainId(ruleChain.getId());
ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData);
} catch (Exception e) {
log.error("Unable to load rule chain from json: [{}]", path.toString());
throw new RuntimeException("Unable to load rule chain from json", e);
}
}
path -> loadRuleChainFromFile(tenantId, path)
);
}
}
@ -205,9 +191,28 @@ public class InstallScripts {
rootChain = ruleChainService.saveRuleChain(rootChain);
rootChainMetaData.setRuleChainId(rootChain.getId());
ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), rootChainMetaData);
loadRuleChainFromFile(tenantId, ruleChainsDir.resolve("edge_root_rule_chain.json"));
} catch (Exception e) {
log.error("Unable to load dashboard from json", e);
throw new RuntimeException("Unable to load dashboard from json", e);
}
}
private void loadRuleChainFromFile(TenantId tenantId, Path ruleChainPath) {
try {
JsonNode ruleChainJson = objectMapper.readTree(ruleChainPath.toFile());
RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class);
RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class);
ruleChain.setTenantId(tenantId);
ruleChain = ruleChainService.saveRuleChain(ruleChain);
ruleChainMetaData.setRuleChainId(ruleChain.getId());
ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData);
} catch (Exception e) {
log.error("Unable to load rule chain from json: [{}]", ruleChainPath.toString());
throw new RuntimeException("Unable to load rule chain from json", e);
}
}
}

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

@ -240,7 +240,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
loadSql(schemaUpdateFile, conn);
try {
conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'SYSTEM'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
conn.createStatement().execute("ALTER TABLE rule_chain ADD type varchar(255) DEFAULT 'CORE'"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
} catch (Exception e) {}
log.info("Schema updated.");
}

16
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
import org.thingsboard.server.gen.transport.TransportProtos.EdgeNotificationMsgProto;
import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto;
import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionMgrMsgProto;
@ -41,6 +42,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.EdgeNotificationService;
import org.thingsboard.server.service.encoding.DataDecodingEncodingService;
import org.thingsboard.server.service.queue.processing.AbstractConsumerService;
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
@ -81,18 +83,20 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
private final TbLocalSubscriptionService localSubscriptionService;
private final SubscriptionManagerService subscriptionManagerService;
private final TbCoreDeviceRpcService tbCoreDeviceRpcService;
private final EdgeNotificationService edgeNotificationService;
private final TbCoreConsumerStats stats = new TbCoreConsumerStats();
public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory, ActorSystemContext actorContext,
DeviceStateService stateService, TbLocalSubscriptionService localSubscriptionService,
SubscriptionManagerService subscriptionManagerService, DataDecodingEncodingService encodingService,
TbCoreDeviceRpcService tbCoreDeviceRpcService) {
TbCoreDeviceRpcService tbCoreDeviceRpcService, EdgeNotificationService edgeNotificationService) {
super(actorContext, encodingService, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer());
this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer();
this.stateService = stateService;
this.localSubscriptionService = localSubscriptionService;
this.subscriptionManagerService = subscriptionManagerService;
this.tbCoreDeviceRpcService = tbCoreDeviceRpcService;
this.edgeNotificationService = edgeNotificationService;
}
@PostConstruct
@ -141,6 +145,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
} else if (toCoreMsg.hasDeviceStateServiceMsg()) {
log.trace("[{}] Forwarding message to state service {}", id, toCoreMsg.getDeviceStateServiceMsg());
forwardToStateService(toCoreMsg.getDeviceStateServiceMsg(), callback);
} else if (toCoreMsg.hasEdgeNotificationMsg()) {
log.trace("[{}] Forwarding message to edge service {}", id, toCoreMsg.getEdgeNotificationMsg());
forwardToEdgeNotificationService(toCoreMsg.getEdgeNotificationMsg(), callback);
} else if (toCoreMsg.getToDeviceActorNotificationMsg() != null && !toCoreMsg.getToDeviceActorNotificationMsg().isEmpty()) {
Optional<TbActorMsg> actorMsg = encodingService.decode(toCoreMsg.getToDeviceActorNotificationMsg().toByteArray());
if (actorMsg.isPresent()) {
@ -274,6 +281,13 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
stateService.onQueueMsg(deviceStateServiceMsg, callback);
}
private void forwardToEdgeNotificationService(EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) {
if (statsEnabled) {
stats.log(edgeNotificationMsg);
}
edgeNotificationService.pushNotificationToEdge(edgeNotificationMsg, callback);
}
private void forwardToDeviceActor(TransportToDeviceActorMsg toDeviceActorMsg, TbCallback callback) {
if (statsEnabled) {
stats.log(toDeviceActorMsg);

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

@ -33,6 +33,7 @@ public class TbCoreConsumerStats {
private final AtomicInteger claimDeviceCounter = new AtomicInteger(0);
private final AtomicInteger deviceStateCounter = new AtomicInteger(0);
private final AtomicInteger edgeNotificationCounter = new AtomicInteger(0);
private final AtomicInteger subscriptionMsgCounter = new AtomicInteger(0);
private final AtomicInteger toCoreNotificationsCounter = new AtomicInteger(0);
@ -66,6 +67,11 @@ public class TbCoreConsumerStats {
deviceStateCounter.incrementAndGet();
}
public void log(TransportProtos.EdgeNotificationMsgProto msg) {
totalCounter.incrementAndGet();
edgeNotificationCounter.incrementAndGet();
}
public void log(TransportProtos.SubscriptionMgrMsgProto msg) {
totalCounter.incrementAndGet();
subscriptionMsgCounter.incrementAndGet();

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

@ -469,6 +469,7 @@ audit-log:
"rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}"
"alarm": "${AUDIT_LOG_MASK_ALARM:W}"
"entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}"
"edge": "${AUDIT_LOG_MASK_EDGE:W}"
sink:
# Type of external sink. possible options: none, elasticsearch
type: "${AUDIT_LOG_SINK_TYPE:none}"
@ -579,7 +580,7 @@ transport:
edges:
rpc:
enabled: "${EDGES_RPC_ENABLED:true}"
port: "${EDGES_RPC_PORT:60061}"
port: "${EDGES_RPC_PORT:60100}"
ssl:
# Enable/disable SSL support
enabled: "${EDGES_RPC_SSL_ENABLED:false}"

18
application/src/test/java/org/thingsboard/server/controller/BaseComponentDescriptorControllerTest.java

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentScope;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.security.Authority;
import java.util.List;
@ -65,17 +66,30 @@ public abstract class BaseComponentDescriptorControllerTest extends AbstractCont
.andExpect(status().isOk());
}
@Test
public void testGetByClazz() throws Exception {
ComponentDescriptor descriptor =
doGet("/api/component/" + TbJsFilterNode.class.getName(), ComponentDescriptor.class);
Assert.assertNotNull(descriptor);
Assert.assertNotNull(descriptor.getId());
Assert.assertNotNull(descriptor.getName());
Assert.assertEquals(ComponentScope.TENANT, descriptor.getScope());
Assert.assertEquals(ComponentType.FILTER, descriptor.getType());
Assert.assertEquals(descriptor.getClazz(), descriptor.getClazz());
}
@Test
public void testGetByType() throws Exception {
List<ComponentDescriptor> descriptors = readResponse(
doGet("/api/components/SYSTEM?componentTypes={componentTypes}", ComponentType.FILTER).andExpect(status().isOk()), new TypeReference<List<ComponentDescriptor>>() {
doGet("/api/components?componentTypes={componentTypes}&ruleChainType={ruleChainType}", ComponentType.FILTER, RuleChainType.CORE).andExpect(status().isOk()), new TypeReference<List<ComponentDescriptor>>() {
});
Assert.assertNotNull(descriptors);
Assert.assertTrue(descriptors.size() >= AMOUNT_OF_DEFAULT_FILTER_NODES);
for (ComponentType type : ComponentType.values()) {
doGet("/api/components/SYSTEM?componentTypes={componentTypes}", type).andExpect(status().isOk());
doGet("/api/components?componentTypes={componentTypes}&ruleChainType={ruleChainType}", type, RuleChainType.CORE).andExpect(status().isOk());
}
}

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

@ -28,8 +28,7 @@ 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.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
@ -48,6 +47,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
private IdComparator<Edge> idComparator = new IdComparator<>();
private Tenant savedTenant;
private TenantId tenantId;
private User tenantAdmin;
@Before
@ -57,6 +57,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
tenantId = savedTenant.getId();
Assert.assertNotNull(savedTenant);
tenantAdmin = new User();
@ -79,9 +80,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
@Test
public void testSaveEdge() throws Exception {
Edge edge = new Edge();
edge.setName("My edge");
edge.setType("default");
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
Assert.assertNotNull(savedEdge);
@ -101,9 +100,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
@Test
public void testFindEdgeById() throws Exception {
Edge edge = new Edge();
edge.setName("My edge");
edge.setType("default");
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
Edge foundEdge = doGet("/api/edge/" + savedEdge.getId().getId().toString(), Edge.class);
Assert.assertNotNull(foundEdge);
@ -114,21 +111,15 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
public void testFindEdgeTypesByTenantId() throws Exception {
List<Edge> edges = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Edge edge = new Edge();
edge.setName("My edge B" + i);
edge.setType("typeB");
Edge edge = constructEdge("My edge B" + i, "typeB");
edges.add(doPost("/api/edge", edge, Edge.class));
}
for (int i = 0; i < 7; i++) {
Edge edge = new Edge();
edge.setName("My edge C" + i);
edge.setType("typeC");
Edge edge = constructEdge("My edge C" + i, "typeC");
edges.add(doPost("/api/edge", edge, Edge.class));
}
for (int i = 0; i < 9; i++) {
Edge edge = new Edge();
edge.setName("My edge A" + i);
edge.setType("typeA");
Edge edge = constructEdge("My edge A" + i, "typeA");
edges.add(doPost("/api/edge", edge, Edge.class));
}
List<EntitySubtype> edgeTypes = doGetTyped("/api/edge/types",
@ -144,9 +135,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
@Test
public void testDeleteEdge() throws Exception {
Edge edge = new Edge();
edge.setName("My edge");
edge.setType("default");
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
doDelete("/api/edge/" + savedEdge.getId().getId().toString())
@ -158,8 +147,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
@Test
public void testSaveEdgeWithEmptyType() throws Exception {
Edge edge = new Edge();
edge.setName("My edge");
Edge edge = constructEdge("My edge", null);
doPost("/api/edge", edge)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Edge type should be specified")));
@ -167,8 +155,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
@Test
public void testSaveEdgeWithEmptyName() throws Exception {
Edge edge = new Edge();
edge.setType("default");
Edge edge = constructEdge(null, "default");
doPost("/api/edge", edge)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Edge name should be specified")));
@ -176,9 +163,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
@Test
public void testAssignUnassignEdgeToCustomer() throws Exception {
Edge edge = new Edge();
edge.setName("My edge");
edge.setType("default");
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
Customer customer = new Customer();
@ -202,9 +187,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
@Test
public void testAssignEdgeToNonExistentCustomer() throws Exception {
Edge edge = new Edge();
edge.setName("My edge");
edge.setType("default");
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
doPost("/api/customer/" + Uuids.timeBased().toString()
@ -236,9 +219,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
login(tenantAdmin.getEmail(), "testPassword1");
Edge edge = new Edge();
edge.setName("My edge");
edge.setType("default");
Edge edge = constructEdge("My edge", "default");
Edge savedEdge = doPost("/api/edge", edge, Edge.class);
doPost("/api/customer/" + savedCustomer.getId().getId().toString()
@ -255,9 +236,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
public void testFindTenantEdges() throws Exception {
List<Edge> edges = new ArrayList<>();
for (int i = 0; i < 178; i++) {
Edge edge = new Edge();
edge.setName("Edge" + i);
edge.setType("default");
Edge edge = constructEdge("Edge" + i, "default");
edges.add(doPost("/api/edge", edge, Edge.class));
}
List<Edge> loadedEdges = new ArrayList<>();
@ -284,23 +263,19 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String title1 = "Edge title 1";
List<Edge> edgesTitle1 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
Edge edge = new Edge();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
edge.setName(name);
edge.setType("default");
Edge edge = constructEdge(name, "default");
edgesTitle1.add(doPost("/api/edge", edge, Edge.class));
}
String title2 = "Edge title 2";
List<Edge> edgesTitle2 = new ArrayList<>();
for (int i = 0; i < 75; i++) {
Edge edge = new Edge();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
edge.setName(name);
edge.setType("default");
Edge edge = constructEdge(name, "default");
edgesTitle2.add(doPost("/api/edge", edge, Edge.class));
}
@ -370,24 +345,20 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String type1 = "typeA";
List<Edge> edgesType1 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
Edge edge = new Edge();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
edge.setName(name);
edge.setType(type1);
Edge edge = constructEdge(name, type1);
edgesType1.add(doPost("/api/edge", edge, Edge.class));
}
String title2 = "Edge title 2";
String type2 = "typeB";
List<Edge> edgesType2 = new ArrayList<>();
for (int i = 0; i < 75; i++) {
Edge edge = new Edge();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
edge.setName(name);
edge.setType(type2);
Edge edge = constructEdge(name, type2);
edgesType2.add(doPost("/api/edge", edge, Edge.class));
}
@ -460,9 +431,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
List<Edge> edges = new ArrayList<>();
for (int i = 0; i < 128; i++) {
Edge edge = new Edge();
edge.setName("Edge" + i);
edge.setType("default");
Edge edge = constructEdge("Edge" + i, "default");
edge = doPost("/api/edge", edge, Edge.class);
edges.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/edge/" + edge.getId().getId().toString(), Edge.class));
@ -497,12 +466,10 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String title1 = "Edge title 1";
List<Edge> edgesTitle1 = new ArrayList<>();
for (int i = 0; i < 125; i++) {
Edge edge = new Edge();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
edge.setName(name);
edge.setType("default");
Edge edge = constructEdge(name, "default");
edge = doPost("/api/edge", edge, Edge.class);
edgesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/edge/" + edge.getId().getId().toString(), Edge.class));
@ -510,12 +477,10 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String title2 = "Edge title 2";
List<Edge> edgesTitle2 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
Edge edge = new Edge();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
edge.setName(name);
edge.setType("default");
Edge edge = constructEdge(name, "default");
edge = doPost("/api/edge", edge, Edge.class);
edgesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/edge/" + edge.getId().getId().toString(), Edge.class));
@ -592,12 +557,10 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String type1 = "typeC";
List<Edge> edgesType1 = new ArrayList<>();
for (int i = 0; i < 125; i++) {
Edge edge = new Edge();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
edge.setName(name);
edge.setType(type1);
Edge edge = constructEdge(name, type1);
edge = doPost("/api/edge", edge, Edge.class);
edgesType1.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/edge/" + edge.getId().getId().toString(), Edge.class));
@ -606,12 +569,10 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
String type2 = "typeD";
List<Edge> edgesType2 = new ArrayList<>();
for (int i = 0; i < 143; i++) {
Edge edge = new Edge();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
edge.setName(name);
edge.setType(type2);
Edge edge = constructEdge(name, type2);
edge = doPost("/api/edge", edge, Edge.class);
edgesType2.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/edge/" + edge.getId().getId().toString(), Edge.class));
@ -676,4 +637,18 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
private Edge constructEdge(String name, String type) {
return constructEdge(tenantId, name, type);
}
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;
}
}

7
common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java

@ -26,6 +26,7 @@ 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.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List;
import java.util.Optional;
@ -78,9 +79,7 @@ public interface AssetService {
Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId);
Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId);
Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId);
PageData<Asset> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink);
PageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink);
ListenableFuture<PageData<Asset>> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}

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

@ -60,7 +60,5 @@ public interface DashboardService {
void unassignEdgeDashboards(TenantId tenantId, EdgeId edgeId);
void updateEdgeDashboards(TenantId tenantId, EdgeId edgeId);
PageData<DashboardInfo> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink);
ListenableFuture<PageData<DashboardInfo>> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}

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

@ -26,6 +26,7 @@ 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.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List;
@ -79,9 +80,7 @@ public interface DeviceService {
Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId);
Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId);
PageData<Device> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink);
ListenableFuture<PageData<Device>> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}

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

@ -0,0 +1,35 @@
/**
* 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.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;
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);
}

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

@ -15,23 +15,19 @@
*/
package org.thingsboard.server.dao.edge;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.Event;
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.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.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.msg.TbMsg;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
@ -77,9 +73,9 @@ public interface EdgeService {
ListenableFuture<List<EntitySubtype>> findEdgeTypesByTenantId(TenantId tenantId);
void pushEventToEdge(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback);
void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId);
PageData<Event> findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId);
Edge setRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException;
ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId);
}

7
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;
@ -79,9 +80,7 @@ public interface EntityViewService {
EntityView assignEntityViewToEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId);
EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId);
EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId);
PageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink);
PageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink);
ListenableFuture<PageData<EntityView>> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
}

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

@ -72,11 +72,16 @@ public interface RuleChainService {
void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId);
void updateEdgeRuleChains(TenantId tenantId, EdgeId edgeId);
PageData<RuleChain> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink);
ListenableFuture<PageData<RuleChain>> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink);
RuleChain getDefaultRootEdgeRuleChain(TenantId tenantId);
boolean setDefaultRootEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId);
boolean addDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId);
boolean removeDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId);
ListenableFuture<List<RuleChain>> findDefaultEdgeRuleChainsByTenantId(TenantId tenantId);
}

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

@ -32,9 +32,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
private String title;
private Set<ShortCustomerInfo> assignedCustomers;
@Getter @Setter
private Set<ShortEdgeInfo> assignedEdges;
public DashboardInfo() {
super();
}
@ -123,55 +120,6 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
}
}
public boolean isAssignedToEdge(EdgeId edgeId) {
return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null));
}
public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) {
if (this.assignedEdges != null) {
for (ShortEdgeInfo edgeInfo : this.assignedEdges) {
if (edgeInfo.getEdgeId().equals(edgeId)) {
return edgeInfo;
}
}
}
return null;
}
public boolean addAssignedEdge(Edge edge) {
ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo();
if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) {
return false;
} else {
if (this.assignedEdges == null) {
this.assignedEdges = new HashSet<>();
}
this.assignedEdges.add(edgeInfo);
return true;
}
}
public boolean updateAssignedEdge(Edge edge) {
ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo();
if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) {
this.assignedEdges.remove(edgeInfo);
this.assignedEdges.add(edgeInfo);
return true;
} else {
return false;
}
}
public boolean removeAssignedEdge(Edge edge) {
ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo();
if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) {
this.assignedEdges.remove(edgeInfo);
return true;
} else {
return false;
}
}
@Override
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public String getName() {

4
common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java

@ -66,6 +66,6 @@ public class DataConstants {
public static final String SECRET_KEY_FIELD_NAME = "secretKey";
public static final String DURATION_MS_FIELD_NAME = "durationMs";
public static final String EDGE_QUEUE_EVENT_TYPE = "EDGE_QUEUE";
public static final String EDGE_MSG_SOURCE = "edge";
public static final String MSG_SOURCE_KEY = "source";
}

13
common/data/src/main/java/org/thingsboard/server/common/data/Device.java

@ -18,7 +18,6 @@ package org.thingsboard.server.common.data;
import lombok.EqualsAndHashCode;
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.TenantId;
@EqualsAndHashCode(callSuper = true)
@ -28,7 +27,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
private TenantId tenantId;
private CustomerId customerId;
private EdgeId edgeId;
private String name;
private String type;
private String label;
@ -48,7 +46,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.name = device.getName();
this.type = device.getType();
this.label = device.getLabel();
this.edgeId = device.getEdgeId();
}
public TenantId getTenantId() {
@ -67,14 +64,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.customerId = customerId;
}
public EdgeId getEdgeId() {
return edgeId;
}
public void setEdgeId(EdgeId edgeId) {
this.edgeId = edgeId;
}
@Override
public String getName() {
return name;
@ -112,8 +101,6 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
builder.append(tenantId);
builder.append(", customerId=");
builder.append(customerId);
builder.append(", edgeId=");
builder.append(edgeId);
builder.append(", name=");
builder.append(name);
builder.append(", type=");

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

@ -0,0 +1,73 @@
/**
* 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 org.thingsboard.server.common.data.id.EdgeId;
import java.util.Set;
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;
}
}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java

@ -19,7 +19,6 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
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.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
@ -40,7 +39,6 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
private EntityId entityId;
private TenantId tenantId;
private CustomerId customerId;
private EdgeId edgeId;
private String name;
private String type;
private TelemetryEntityView keys;

14
common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java

@ -15,12 +15,10 @@
*/
package org.thingsboard.server.common.data.asset;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
@EqualsAndHashCode(callSuper = true)
@ -30,7 +28,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
private TenantId tenantId;
private CustomerId customerId;
private EdgeId edgeId;
private String name;
private String type;
private String label;
@ -47,7 +44,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
super(asset);
this.tenantId = asset.getTenantId();
this.customerId = asset.getCustomerId();
this.edgeId = asset.getEdgeId();
this.name = asset.getName();
this.type = asset.getType();
this.label = asset.getLabel();
@ -69,14 +65,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
this.customerId = customerId;
}
public EdgeId getEdgeId() {
return edgeId;
}
public void setEdgeId(EdgeId edgeId) {
this.edgeId = edgeId;
}
@Override
public String getName() {
return name;
@ -114,8 +102,6 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements
builder.append(tenantId);
builder.append(", customerId=");
builder.append(customerId);
builder.append(", edgeId=");
builder.append(edgeId);
builder.append(", name=");
builder.append(name);
builder.append(", type=");

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

@ -24,6 +24,7 @@ public enum ActionType {
UPDATED(false), // log entity
ATTRIBUTES_UPDATED(false), // log attributes/values
ATTRIBUTES_DELETED(false), // log attributes
TIMESERIES_UPDATED(false), // log timeseries
TIMESERIES_DELETED(false), // log timeseries
RPC_CALL(false), // log method and params
CREDENTIALS_UPDATED(false), // log new credentials

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

@ -0,0 +1,50 @@
/**
* 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.edge;
import com.fasterxml.jackson.databind.JsonNode;
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;
@Data
public class EdgeEvent extends BaseData<EdgeEventId> {
private TenantId tenantId;
private EdgeId edgeId;
private String edgeEventAction;
private UUID entityId;
private EdgeEventType edgeEventType;
private transient JsonNode entityBody;
public EdgeEvent() {
super();
}
public EdgeEvent(EdgeEventId id) {
super(id);
}
public EdgeEvent(EdgeEvent event) {
super(event);
}
}

4
common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java → common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java

@ -15,6 +15,6 @@
*/
package org.thingsboard.server.common.data.edge;
public enum EdgeQueueEntityType {
DASHBOARD, ASSET, DEVICE, ENTITY_VIEW, ALARM, RULE_CHAIN, RULE_CHAIN_METADATA, EDGE, USER, CUSTOMER
public enum EdgeEventType {
DASHBOARD, ASSET, DEVICE, ENTITY_VIEW, ALARM, RULE_CHAIN, RULE_CHAIN_METADATA, EDGE, USER, CUSTOMER, RELATION
}

24
common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntry.java → common/data/src/main/java/org/thingsboard/server/common/data/id/EdgeEventId.java

@ -13,13 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.edge;
package org.thingsboard.server.common.data.id;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
@Data
public class EdgeQueueEntry {
private String type;
private EdgeQueueEntityType entityType;
private String data;
import java.util.UUID;
public class EdgeEventId extends UUIDBased {
private static final long serialVersionUID = 1L;
@JsonCreator
public EdgeEventId(@JsonProperty("id") UUID id) {
super(id);
}
public static EdgeEventId fromString(String edgeEventId) {
return new EdgeEventId(UUID.fromString(edgeEventId));
}
}

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

@ -16,6 +16,7 @@
package org.thingsboard.server.common.data.id;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import java.util.UUID;
@ -68,4 +69,28 @@ public class EntityIdFactory {
throw new IllegalArgumentException("EntityType " + type + " is not supported!");
}
public static EntityId getByEdgeEventTypeAndUuid(EdgeEventType edgeEventType, UUID uuid) {
switch (edgeEventType) {
case CUSTOMER:
return new CustomerId(uuid);
case USER:
return new UserId(uuid);
case DASHBOARD:
return new DashboardId(uuid);
case DEVICE:
return new DeviceId(uuid);
case ASSET:
return new AssetId(uuid);
case ALARM:
return new AlarmId(uuid);
case RULE_CHAIN:
return new RuleChainId(uuid);
case ENTITY_VIEW:
return new EntityViewId(uuid);
case EDGE:
return new EdgeId(uuid);
}
throw new IllegalArgumentException("EdgeEventType " + edgeEventType + " is not supported!");
}
}

3
common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java

@ -22,6 +22,7 @@ public enum RelationTypeGroup {
DASHBOARD,
RULE_CHAIN,
RULE_NODE,
EDGE
EDGE,
EDGE_DEFAULT_RULE_CHAIN
}

59
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java

@ -23,16 +23,10 @@ import lombok.extern.slf4j.Slf4j;
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.ShortEdgeInfo;
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.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.HashSet;
import java.util.Set;
@Data
@EqualsAndHashCode(callSuper = true)
@Slf4j
@ -47,11 +41,10 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
private boolean root;
private boolean debugMode;
private transient JsonNode configuration;
private Set<ShortEdgeInfo> assignedEdges;
@JsonIgnore
private byte[] configurationBytes;
public RuleChain() {
super();
}
@ -67,7 +60,6 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
this.type = ruleChain.getType();
this.firstRuleNodeId = ruleChain.getFirstRuleNodeId();
this.root = ruleChain.isRoot();
this.assignedEdges = ruleChain.getAssignedEdges();
this.setConfiguration(ruleChain.getConfiguration());
}
@ -88,53 +80,4 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
public void setConfiguration(JsonNode data) {
setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes);
}
public boolean isAssignedToEdge(EdgeId edgeId) {
return this.assignedEdges != null && this.assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null));
}
public ShortEdgeInfo getAssignedEdgeInfo(EdgeId edgeId) {
if (this.assignedEdges != null) {
for (ShortEdgeInfo edgeInfo : this.assignedEdges) {
if (edgeInfo.getEdgeId().equals(edgeId)) {
return edgeInfo;
}
}
}
return null;
}
public boolean addAssignedEdge(Edge edge) {
ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo();
if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) {
return false;
} else {
if (this.assignedEdges == null) {
this.assignedEdges = new HashSet<>();
}
this.assignedEdges.add(edgeInfo);
return true;
}
}
public boolean updateAssignedEdge(Edge edge) {
ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo();
if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) {
this.assignedEdges.remove(edgeInfo);
this.assignedEdges.add(edgeInfo);
return true;
} else {
return false;
}
}
public boolean removeAssignedEdge(Edge edge) {
ShortEdgeInfo edgeInfo = edge.toShortEdgeInfo();
if (this.assignedEdges != null && this.assignedEdges.contains(edgeInfo)) {
this.assignedEdges.remove(edgeInfo);
return true;
} else {
return false;
}
}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java

@ -16,5 +16,5 @@
package org.thingsboard.server.common.data.rule;
public enum RuleChainType {
SYSTEM, EDGE
CORE, EDGE
}

4
common/edge-api/pom.xml

@ -40,6 +40,10 @@
<groupId>org.thingsboard.common</groupId>
<artifactId>data</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>queue</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>message</artifactId>

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

@ -79,6 +79,7 @@ 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);
@ -89,6 +90,16 @@ public class EdgeGrpcClient implements EdgeRpcClient {
.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();
@ -130,7 +141,7 @@ public class EdgeGrpcClient implements EdgeRpcClient {
log.debug("[{}] Entity update message received {}", edgeKey, responseMsg.getEntityUpdateMsg());
onEntityUpdate.accept(responseMsg.getEntityUpdateMsg());
} else if (responseMsg.hasDownlinkMsg()) {
log.debug("[{}] Downlink message received for rule chain {}", edgeKey, responseMsg.getDownlinkMsg());
log.debug("[{}] Downlink message received {}", edgeKey, responseMsg.getDownlinkMsg());
onDownlink.accept(responseMsg.getDownlinkMsg());
}
}

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

@ -19,6 +19,8 @@ option java_package = "org.thingsboard.server.gen.edge";
option java_multiple_files = true;
option java_outer_classname = "EdgeProtos";
import "queue.proto";
package edge;
// Interface exported by the ThingsBoard Edge Transport.
@ -54,6 +56,7 @@ message EntityUpdateMsg {
AlarmUpdateMsg alarmUpdateMsg = 7;
UserUpdateMsg userUpdateMsg = 8;
CustomerUpdateMsg customerUpdateMsg = 9;
RelationUpdateMsg relationUpdateMsg = 10;
}
enum RequestMsgType {
@ -88,140 +91,165 @@ message EdgeConfiguration {
}
enum UpdateMsgType {
ENTITY_CREATED_RPC_MESSAGE = 0;
ENTITY_UPDATED_RPC_MESSAGE = 1;
ENTITY_DELETED_RPC_MESSAGE = 2;
ALARM_ACK_RPC_MESSAGE = 3;
ALARM_CLEAR_RPC_MESSAGE = 4;
RULE_CHAIN_CUSTOM_MESSAGE = 5;
ENTITY_CREATED_RPC_MESSAGE = 0;
ENTITY_UPDATED_RPC_MESSAGE = 1;
ENTITY_DELETED_RPC_MESSAGE = 2;
ALARM_ACK_RPC_MESSAGE = 3;
ALARM_CLEAR_RPC_MESSAGE = 4;
DEVICE_CONFLICT_RPC_MESSAGE = 5;
}
message EntityDataProto {
string entityName = 1;
string entityType = 2;
bytes tbMsg = 3;
int64 entityIdMSB = 1;
int64 entityIdLSB = 2;
string entityType = 3;
transport.PostTelemetryMsg postTelemetryMsg = 4;
transport.PostAttributeMsg postAttributesMsg = 5;
// transport.ToDeviceRpcRequestMsg ???
}
message RuleChainUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string name = 4;
int64 firstRuleNodeIdMSB = 5;
int64 firstRuleNodeIdLSB = 6;
bool root = 7;
bool debugMode = 8;
string configuration = 9;
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string name = 4;
int64 firstRuleNodeIdMSB = 5;
int64 firstRuleNodeIdLSB = 6;
bool root = 7;
bool debugMode = 8;
string configuration = 9;
}
message RuleChainMetadataUpdateMsg {
UpdateMsgType msgType = 1;
int64 ruleChainIdMSB = 2;
int64 ruleChainIdLSB = 3;
int32 firstNodeIndex = 4;
repeated RuleNodeProto nodes = 5;
repeated NodeConnectionInfoProto connections = 6;
repeated RuleChainConnectionInfoProto ruleChainConnections = 7;
UpdateMsgType msgType = 1;
int64 ruleChainIdMSB = 2;
int64 ruleChainIdLSB = 3;
int32 firstNodeIndex = 4;
repeated RuleNodeProto nodes = 5;
repeated NodeConnectionInfoProto connections = 6;
repeated RuleChainConnectionInfoProto ruleChainConnections = 7;
}
message RuleNodeProto {
int64 idMSB = 1;
int64 idLSB = 2;
string type = 3;
string name = 4;
bool debugMode = 5;
string configuration = 6;
string additionalInfo = 7;
int64 idMSB = 1;
int64 idLSB = 2;
string type = 3;
string name = 4;
bool debugMode = 5;
string configuration = 6;
string additionalInfo = 7;
}
message NodeConnectionInfoProto {
int32 fromIndex = 1;
int32 toIndex = 2;
string type = 3;
int32 fromIndex = 1;
int32 toIndex = 2;
string type = 3;
}
message RuleChainConnectionInfoProto {
int32 fromIndex = 1;
int64 targetRuleChainIdMSB = 2;
int64 targetRuleChainIdLSB = 3;
string type = 4;
string additionalInfo = 5;
int32 fromIndex = 1;
int64 targetRuleChainIdMSB = 2;
int64 targetRuleChainIdLSB = 3;
string type = 4;
string additionalInfo = 5;
}
message DashboardUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string title = 4;
string configuration = 5;
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string title = 4;
string configuration = 5;
}
message DeviceUpdateMsg {
UpdateMsgType msgType = 1;
string name = 2;
string type = 3;
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;
}
message AssetUpdateMsg {
UpdateMsgType msgType = 1;
string name = 2;
string type = 3;
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string name = 4;
string type = 5;
string label = 6;
}
message EntityViewUpdateMsg {
UpdateMsgType msgType = 1;
string name = 2;
string type = 3;
string relatedName = 4;
string relatedType = 5;
EntityType relatedEntityType = 6;
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string name = 4;
string type = 5;
int64 entityIdMSB = 6;
int64 entityIdLSB = 7;
EdgeEntityType entityType = 8;
}
message AlarmUpdateMsg {
UpdateMsgType msgType = 1;
string name = 2;
string type = 3;
string originatorType = 4;
string originatorName = 5;
string severity = 6;
string status = 7;
int64 startTs = 8;
int64 endTs = 9;
int64 ackTs = 10;
int64 clearTs = 11;
string details = 12;
bool propagate = 13;
UpdateMsgType msgType = 1;
string name = 2;
string type = 3;
string originatorType = 4;
string originatorName = 5;
string severity = 6;
string status = 7;
int64 startTs = 8;
int64 endTs = 9;
int64 ackTs = 10;
int64 clearTs = 11;
string details = 12;
bool propagate = 13;
}
message CustomerUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string title = 4;
string country = 5;
string state = 6;
string city = 7;
string address = 8;
string address2 = 9;
string zip = 10;
string phone = 11;
string email = 12;
string additionalInfo = 13;
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
string title = 4;
string country = 5;
string state = 6;
string city = 7;
string address = 8;
string address2 = 9;
string zip = 10;
string phone = 11;
string email = 12;
string additionalInfo = 13;
}
message RelationUpdateMsg {
UpdateMsgType msgType = 1;
int64 fromIdMSB = 2;
int64 fromIdLSB = 3;
string fromEntityType = 4;
int64 toIdMSB = 5;
int64 toIdLSB = 6;
string toEntityType = 7;
string type = 8;
string typeGroup = 9;
string additionalInfo = 10;
}
message UserUpdateMsg {
UpdateMsgType msgType = 1;
int64 idMSB = 2;
int64 idLSB = 3;
int64 customerIdMSB = 4;
int64 customerIdLSB = 5;
string email = 7;
string authority = 8;
string firstName = 9;
string lastName = 10;
string additionalInfo = 11;
bool enabled = 12;
string password = 13;
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;
}
message RuleChainMetadataRequestMsg {
@ -229,9 +257,9 @@ message RuleChainMetadataRequestMsg {
int64 ruleChainIdLSB = 2;
}
enum EntityType {
DEVICE = 0;
ASSET = 1;
enum EdgeEntityType {
DEVICE = 0;
ASSET = 1;
}
/**

2
common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java

@ -91,8 +91,6 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response
List<Response> responses = responseTemplate.poll(pollInterval);
if (responses.size() > 0) {
log.trace("Polling responses completed, consumer records count [{}]", responses.size());
} else {
continue;
}
responses.forEach(response -> {
byte[] requestIdHeader = response.getHeaders().get(REQUEST_ID_HEADER);

17
common/queue/src/main/proto/queue.proto

@ -352,6 +352,22 @@ message FromDeviceRPCResponseProto {
string response = 3;
int32 error = 4;
}
message EdgeNotificationMsgProto {
int64 tenantIdMSB = 1;
int64 tenantIdLSB = 2;
int64 edgeIdMSB = 3;
int64 edgeIdLSB = 4;
string edgeEventType = 5;
string edgeEventAction = 6;
int64 entityIdMSB = 7;
int64 entityIdLSB = 8;
string entityType = 9;
string entityBody = 10;
PostTelemetryMsg postTelemetryMsg = 11;
PostAttributeMsg postAttributesMsg = 12;
}
/**
* Main messages;
*/
@ -377,6 +393,7 @@ message ToCoreMsg {
DeviceStateServiceMsgProto deviceStateServiceMsg = 2;
SubscriptionMgrMsgProto toSubscriptionMgrMsg = 3;
bytes toDeviceActorNotificationMsg = 4;
EdgeNotificationMsgProto edgeNotificationMsg = 5;
}
/* High priority messages with low latency are handled by ThingsBoard Core Service separately */

58
dao/pom.xml

@ -91,26 +91,26 @@
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
@ -136,8 +136,8 @@
<groupId>io.takari.junit</groupId>
<artifactId>takari-cpsuite</artifactId>
<scope>test</scope>
</dependency>
<dependency>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
@ -219,18 +219,18 @@
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${jar-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

16
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -327,16 +327,6 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
}
private void deleteRelation(TenantId tenantId, EntityRelation alarmRelation) {
log.debug("Deleting Alarm relation: {}", alarmRelation);
relationService.deleteRelation(tenantId, alarmRelation);
}
private void createRelation(TenantId tenantId, EntityRelation alarmRelation) {
log.debug("Creating Alarm relation: {}", alarmRelation);
relationService.saveRelation(tenantId, alarmRelation);
}
private Alarm merge(Alarm existing, Alarm alarm) {
if (alarm.getStartTs() > existing.getEndTs()) {
existing.setEndTs(alarm.getStartTs());
@ -381,7 +371,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
}
}
private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) {
private void createAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) throws ExecutionException, InterruptedException {
if (createAnyRelation) {
createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM));
}
@ -390,13 +380,13 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
createRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
}
private void deleteAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status) {
private void deleteAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus status) throws ExecutionException, InterruptedException {
deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
deleteRelation(tenantId, new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
}
private void updateAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) {
private void updateAlarmRelation(TenantId tenantId, EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) throws ExecutionException, InterruptedException {
deleteAlarmRelation(tenantId, entityId, alarmId, oldStatus);
createAlarmRelation(tenantId, entityId, alarmId, newStatus, false);
}

16
dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java

@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.asset.AssetInfo;
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.dao.Dao;
import java.util.List;
@ -167,23 +168,12 @@ public interface AssetDao extends Dao<Asset> {
ListenableFuture<List<EntitySubtype>> findTenantAssetTypesAsync(UUID tenantId);
/**
* Find assets by tenantId, customerId and page link.
* Find assets by tenantId, edgeId and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param pageLink the page link
* @return the list of asset objects
*/
PageData<Asset> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink);
/**
* Find assets by tenantId, customerId, type and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param type the type
* @param pageLink the page link
* @return the list of asset objects
*/
PageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, PageLink pageLink);
ListenableFuture<PageData<Asset>> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
}

55
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.asset;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
@ -29,6 +30,7 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
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.EntityType;
import org.thingsboard.server.common.data.EntityView;
@ -36,6 +38,7 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.asset.AssetSearchQuery;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
@ -43,9 +46,12 @@ import org.thingsboard.server.common.data.id.EntityId;
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.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -53,6 +59,7 @@ import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -73,10 +80,8 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
public class BaseAssetService extends AbstractEntityService implements AssetService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_ASSET_ID = "Incorrect assetId ";
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
@Autowired
private AssetDao assetDao;
@ -90,6 +95,9 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
@Autowired
private EntityViewService entityViewService;
@Autowired
private EdgeService edgeService;
@Autowired
private CacheManager cacheManager;
@ -321,19 +329,40 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
@Override
public Asset assignAssetToEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId) {
Asset asset = findAssetById(tenantId, assetId);
asset.setEdgeId(edgeId);
return saveAsset(asset);
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't assign asset to non-existent edge!");
}
if (!edge.getTenantId().getId().equals(asset.getTenantId().getId())) {
throw new DataValidationException("Can't assign asset to edge from different tenant!");
}
try {
createRelation(tenantId, new EntityRelation(edgeId, assetId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create asset relation. Edge Id: [{}]", assetId, edgeId);
throw new RuntimeException(e);
}
return asset;
}
@Override
public Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId) {
public Asset unassignAssetFromEdge(TenantId tenantId, AssetId assetId, EdgeId edgeId) {
Asset asset = findAssetById(tenantId, assetId);
asset.setEdgeId(null);
return saveAsset(asset);
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't unassign asset from non-existent edge!");
}
try {
deleteRelation(tenantId, new EntityRelation(edgeId, assetId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete asset relation. Edge Id: [{}]", assetId, edgeId);
throw new RuntimeException(e);
}
return asset;
}
@Override
public PageData<Asset> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) {
public ListenableFuture<PageData<Asset>> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
@ -341,16 +370,6 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
return assetDao.findAssetsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
}
@Override
public PageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return assetDao.findAssetsByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
}
private DataValidator<Asset> assetValidator =
new DataValidator<Asset>() {

2
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java

@ -57,6 +57,6 @@ public interface DashboardInfoDao extends Dao<DashboardInfo> {
* @param pageLink the page link
* @return the list of dashboard objects
*/
PageData<DashboardInfo> findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink);
ListenableFuture<PageData<DashboardInfo>> findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
}

96
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java

@ -65,7 +65,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Autowired
private CustomerDao customerDao;
@Autowired
private EdgeDao edgeDao;
@ -156,16 +156,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
}
}
private void deleteRelation(TenantId tenantId, EntityRelation dashboardRelation) throws ExecutionException, InterruptedException {
log.debug("Deleting Dashboard relation: {}", dashboardRelation);
relationService.deleteRelationAsync(tenantId, dashboardRelation).get();
}
private void createRelation(TenantId tenantId, EntityRelation dashboardRelation) throws ExecutionException, InterruptedException {
log.debug("Creating Dashboard relation: {}", dashboardRelation);
relationService.saveRelationAsync(tenantId, dashboardRelation).get();
}
@Override
public void deleteDashboard(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing deleteDashboard [{}]", dashboardId);
@ -230,17 +220,13 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
if (!edge.getTenantId().getId().equals(dashboard.getTenantId().getId())) {
throw new DataValidationException("Can't assign dashboard to edge from different tenant!");
}
if (dashboard.addAssignedEdge(edge)) {
try {
createRelation(tenantId, new EntityRelation(edgeId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create dashboard relation. Edge Id: [{}]", dashboardId, edgeId);
throw new RuntimeException(e);
}
return saveDashboard(dashboard);
} else {
return dashboard;
try {
createRelation(tenantId, new EntityRelation(edgeId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create dashboard relation. Edge Id: [{}]", dashboardId, edgeId);
throw new RuntimeException(e);
}
return dashboard;
}
@Override
@ -250,17 +236,13 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
if (edge == null) {
throw new DataValidationException("Can't unassign dashboard from non-existent edge!");
}
if (dashboard.removeAssignedEdge(edge)) {
try {
deleteRelation(tenantId, new EntityRelation(edgeId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete dashboard relation. Edge Id: [{}]", dashboardId, edgeId);
throw new RuntimeException(e);
}
return saveDashboard(dashboard);
} else {
return dashboard;
try {
deleteRelation(tenantId, new EntityRelation(edgeId, dashboardId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete dashboard relation. Edge Id: [{}]", dashboardId, edgeId);
throw new RuntimeException(e);
}
return dashboard;
}
@Override
@ -275,18 +257,7 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
}
@Override
public void updateEdgeDashboards(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing updateEdgeDashboards, edgeId [{}]", edgeId);
Validator.validateId(edgeId, "Incorrect edgeId " + edgeId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't update dashboards for non-existent edge!");
}
new EdgeDashboardsUpdater(edge).removeEntities(tenantId, edge);
}
@Override
public PageData<DashboardInfo> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) {
public ListenableFuture<PageData<DashboardInfo>> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
log.trace("Executing findDashboardsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validateId(edgeId, "Incorrect customerId " + edgeId);
@ -294,15 +265,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
}
private Dashboard updateAssignedEdge(TenantId tenantId, DashboardId dashboardId, Edge edge) {
Dashboard dashboard = findDashboardById(tenantId, dashboardId);
if (dashboard.updateAssignedEdge(edge)) {
return saveDashboard(dashboard);
} else {
return dashboard;
}
}
private DataValidator<Dashboard> dashboardValidator =
new DataValidator<Dashboard>() {
@Override
@ -334,9 +296,9 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
deleteDashboard(tenantId, new DashboardId(entity.getUuidId()));
}
};
private class CustomerDashboardsUnassigner extends PaginatedRemover<Customer, DashboardInfo> {
private Customer customer;
CustomerDashboardsUnassigner(Customer customer) {
@ -385,32 +347,18 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
@Override
protected PageData<DashboardInfo> findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) {
return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink);
try {
return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink).get();
} catch (Exception e) {
log.error("[{}] Can't find dashboards by tenant id and edge id. Edge Id {}", edge.getId(), e);
throw new RuntimeException("[{}] Can't find dashboards by tenant id and edge id. Edge Id '" + edge.getId() + "'", e);
}
}
@Override
protected void removeEntity(TenantId tenantId, DashboardInfo entity) {
unassignDashboardFromEdge(edge.getTenantId(), new DashboardId(entity.getUuidId()), this.edge.getId());
}
}
private class EdgeDashboardsUpdater extends TimePaginatedRemover<Edge, DashboardInfo> {
private Edge edge;
EdgeDashboardsUpdater(Edge edge) {
this.edge = edge;
}
@Override
protected PageData<DashboardInfo> findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) {
return dashboardInfoDao.findDashboardsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink);
}
@Override
protected void removeEntity(TenantId tenantId, DashboardInfo entity) {
updateAssignedEdge(edge.getTenantId(), new DashboardId(entity.getUuidId()), this.edge);
}
}

14
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java

@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.EntitySubtype;
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.dao.Dao;
import java.util.List;
@ -175,16 +176,5 @@ public interface DeviceDao extends Dao<Device> {
* @param pageLink the page link
* @return the list of device objects
*/
PageData<Device> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink);
/**
* Find devices by tenantId, edgeId, type and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param type the type
* @param pageLink the page link
* @return the list of device objects
*/
PageData<Device> findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, PageLink pageLink);
ListenableFuture<PageData<Device>> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
}

50
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EdgeId;
@ -44,11 +45,14 @@ import org.thingsboard.server.common.data.id.EntityId;
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.EntitySearchDirection;
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.dao.customer.CustomerDao;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -98,6 +102,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Autowired
private EntityViewService entityViewService;
@Autowired
private EdgeService edgeService;
@Autowired
private CacheManager cacheManager;
@ -359,19 +366,40 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Override
public Device assignDeviceToEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId) {
Device device = findDeviceById(tenantId, deviceId);
device.setEdgeId(edgeId);
return saveDevice(device);
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't assign device to non-existent edge!");
}
if (!edge.getTenantId().getId().equals(device.getTenantId().getId())) {
throw new DataValidationException("Can't assign device to edge from different tenant!");
}
try {
createRelation(tenantId, new EntityRelation(edgeId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create device relation. Edge Id: [{}]", deviceId, edgeId);
throw new RuntimeException(e);
}
return device;
}
@Override
public Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId) {
public Device unassignDeviceFromEdge(TenantId tenantId, DeviceId deviceId, EdgeId edgeId) {
Device device = findDeviceById(tenantId, deviceId);
device.setEdgeId(null);
return saveDevice(device);
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't unassign device from non-existent edge!");
}
try {
deleteRelation(tenantId, new EntityRelation(edgeId, deviceId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete device relation. Edge Id: [{}]", deviceId, edgeId);
throw new RuntimeException(e);
}
return device;
}
@Override
public PageData<Device> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) {
public ListenableFuture<PageData<Device>> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
@ -379,16 +407,6 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
return deviceDao.findDevicesByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
}
@Override
public PageData<Device> findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}], type [{}], pageLink [{}]", tenantId, edgeId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return deviceDao.findDevicesByTenantIdAndEdgeIdAndType(tenantId.getId(), edgeId.getId(), type, pageLink);
}
private DataValidator<Device> deviceValidator =
new DataValidator<Device>() {

84
dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java

@ -0,0 +1,84 @@
/**
* 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.dao.edge;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.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;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
@Service
@Slf4j
public class BaseEdgeEventService implements EdgeEventService {
@Autowired
public EdgeEventDao edgeEventDao;
@Override
public EdgeEventType getEdgeEventTypeByEntityType(EntityType entityType) {
switch (entityType) {
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 ALARM:
return EdgeEventType.ALARM;
default:
log.warn("Failed to push notification to edge service. Unsupported entity type [{}]", entityType);
return null;
}
}
@Override
public ListenableFuture<EdgeEvent> saveAsync(EdgeEvent edgeEvent) {
edgeEventValidator.validate(edgeEvent, EdgeEvent::getTenantId);
return edgeEventDao.saveAsync(edgeEvent);
}
@Override
public PageData<EdgeEvent> findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
return edgeEventDao.findEdgeEvents(tenantId.getId(), edgeId, pageLink);
}
private DataValidator<EdgeEvent> edgeEventValidator =
new DataValidator<EdgeEvent>() {
@Override
protected void validateDataImpl(TenantId tenantId, EdgeEvent edgeEvent) {
if (edgeEvent.getEdgeId() == null) {
throw new DataValidationException("Edge id should be specified!");
}
if (StringUtils.isEmpty(edgeEvent.getEdgeEventAction())) {
throw new DataValidationException("Edge Event action should be specified!");
}
}
};
}

743
dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java

@ -1,743 +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.dao.edge;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Function;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeInfo;
import org.thingsboard.server.common.data.edge.EdgeQueueEntityType;
import org.thingsboard.server.common.data.edge.EdgeQueueEntry;
import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
import org.thingsboard.server.common.data.id.AssetId;
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.EntityViewId;
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.EntitySearchDirection;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.tenant.TenantDao;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.CacheConstants.EDGE_CACHE;
import static org.thingsboard.server.dao.DaoUtil.toUUIDs;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateIds;
import static org.thingsboard.server.dao.service.Validator.validatePageLink;
import static org.thingsboard.server.dao.service.Validator.validateString;
@Service
@Slf4j
public class BaseEdgeService extends AbstractEntityService implements EdgeService {
private static final ObjectMapper mapper = new ObjectMapper();
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
@Autowired
private EdgeDao edgeDao;
@Autowired
private TenantDao tenantDao;
@Autowired
private CustomerDao customerDao;
@Autowired
private CacheManager cacheManager;
@Autowired
private EventService eventService;
@Autowired
private DashboardService dashboardService;
@Autowired
private RuleChainService ruleChainService;
@Autowired
private DeviceService deviceService;
@Autowired
private AssetService assetService;
@Autowired
private EntityViewService entityViewService;
private ExecutorService tsCallBackExecutor;
@PostConstruct
public void initExecutor() {
tsCallBackExecutor = Executors.newSingleThreadExecutor();
}
@PreDestroy
public void shutdownExecutor() {
if (tsCallBackExecutor != null) {
tsCallBackExecutor.shutdownNow();
}
}
@Override
public Edge findEdgeById(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing findEdgeById [{}]", edgeId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
return edgeDao.findById(tenantId, edgeId.getId());
}
@Override
public ListenableFuture<Edge> findEdgeByIdAsync(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing findEdgeById [{}]", edgeId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
return edgeDao.findByIdAsync(tenantId, edgeId.getId());
}
@Cacheable(cacheNames = EDGE_CACHE, key = "{#tenantId, #name}")
@Override
public Edge findEdgeByTenantIdAndName(TenantId tenantId, String name) {
log.trace("Executing findEdgeByTenantIdAndName [{}][{}]", tenantId, name);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Optional<Edge> edgeOpt = edgeDao.findEdgeByTenantIdAndName(tenantId.getId(), name);
return edgeOpt.orElse(null);
}
@Override
public Optional<Edge> findEdgeByRoutingKey(TenantId tenantId, String routingKey) {
log.trace("Executing findEdgeByRoutingKey [{}]", routingKey);
Validator.validateString(routingKey, "Incorrect edge routingKey for search request.");
return edgeDao.findByRoutingKey(tenantId.getId(), routingKey);
}
@CacheEvict(cacheNames = EDGE_CACHE, key = "{#edge.tenantId, #edge.name}")
@Override
public Edge saveEdge(Edge edge) {
log.trace("Executing saveEdge [{}]", edge);
edgeValidator.validate(edge, Edge::getTenantId);
Edge savedEdge = edgeDao.save(edge.getTenantId(), edge);
dashboardService.updateEdgeDashboards(savedEdge.getTenantId(), savedEdge.getId());
return savedEdge;
}
@Override
public Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, CustomerId customerId) {
Edge edge = findEdgeById(tenantId, edgeId);
edge.setCustomerId(customerId);
return saveEdge(edge);
}
@Override
public Edge unassignEdgeFromCustomer(TenantId tenantId, EdgeId edgeId) {
Edge edge = findEdgeById(tenantId, edgeId);
edge.setCustomerId(null);
return saveEdge(edge);
}
@Override
public void deleteEdge(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing deleteEdge [{}]", edgeId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
dashboardService.unassignEdgeDashboards(tenantId, edgeId);
// TODO: validate that rule chains are removed by deleteEntityRelations(tenantId, edgeId); call
ruleChainService.unassignEdgeRuleChains(tenantId, edgeId);
List<Object> list = new ArrayList<>();
list.add(edge.getTenantId());
list.add(edge.getName());
Cache cache = cacheManager.getCache(EDGE_CACHE);
cache.evict(list);
deleteEntityRelations(tenantId, edgeId);
edgeDao.removeById(tenantId, edgeId.getId());
}
@Override
public PageData<Edge> findEdgesByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findEdgesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantId(tenantId.getId(), pageLink);
}
@Override
public PageData<Edge> findEdgesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@Override
public PageData<EdgeInfo> findEdgeInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findEdgeInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return edgeDao.findEdgeInfosByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@Override
public PageData<EdgeInfo> findEdgeInfosByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findEdgeInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validatePageLink(pageLink);
return edgeDao.findEdgeInfosByTenantId(tenantId.getId(), pageLink);
}
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdAndIdsAsync(TenantId tenantId, List<EdgeId> edgeIds) {
log.trace("Executing findEdgesByTenantIdAndIdsAsync, tenantId [{}], edgeIds [{}]", tenantId, edgeIds);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateIds(edgeIds, "Incorrect edgeIds " + edgeIds);
return edgeDao.findEdgesByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(edgeIds));
}
@Override
public void deleteEdgesByTenantId(TenantId tenantId) {
log.trace("Executing deleteEdgesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantEdgesRemover.removeEntities(tenantId, tenantId);
}
@Override
public PageData<Edge> findEdgesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
}
@Override
public PageData<Edge> findEdgesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
}
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<EdgeId> edgeIds) {
log.trace("Executing findEdgesByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], edgeIds [{}]", tenantId, customerId, edgeIds);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateIds(edgeIds, "Incorrect edgeIds " + edgeIds);
return edgeDao.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId.getId(),
customerId.getId(), toUUIDs(edgeIds));
}
@Override
public void unassignCustomerEdges(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerEdges, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
customerEdgeUnasigner.removeEntities(tenantId, customerId);
}
@Override
public ListenableFuture<List<Edge>> findEdgesByQuery(TenantId tenantId, EdgeSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(tenantId, query.toEntitySearchQuery());
ListenableFuture<List<Edge>> edges = Futures.transformAsync(relations, r -> {
EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
List<ListenableFuture<Edge>> futures = new ArrayList<>();
for (EntityRelation relation : r) {
EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
if (entityId.getEntityType() == EntityType.EDGE) {
futures.add(findEdgeByIdAsync(tenantId, new EdgeId(entityId.getId())));
}
}
return Futures.successfulAsList(futures);
}, MoreExecutors.directExecutor());
edges = Futures.transform(edges, new Function<List<Edge>, List<Edge>>() {
@Nullable
@Override
public List<Edge> apply(@Nullable List<Edge> edgeList) {
return edgeList == null ? Collections.emptyList() : edgeList.stream().filter(edge -> query.getEdgeTypes().contains(edge.getType())).collect(Collectors.toList());
}
}, MoreExecutors.directExecutor());
return edges;
}
@Override
public ListenableFuture<List<EntitySubtype>> findEdgeTypesByTenantId(TenantId tenantId) {
log.trace("Executing findEdgeTypesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
ListenableFuture<List<EntitySubtype>> tenantEdgeTypes = edgeDao.findTenantEdgeTypesAsync(tenantId.getId());
return Futures.transform(tenantEdgeTypes,
edgeTypes -> {
edgeTypes.sort(Comparator.comparing(EntitySubtype::getType));
return edgeTypes;
}, MoreExecutors.directExecutor());
}
@Override
public void pushEventToEdge(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback) {
if (tbMsg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) ||
tbMsg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) ||
tbMsg.getType().equals(DataConstants.ATTRIBUTES_UPDATED) ||
tbMsg.getType().equals(DataConstants.ATTRIBUTES_DELETED)) {
processCustomTbMsg(tenantId, tbMsg, callback);
} else {
try {
switch (tbMsg.getOriginator().getEntityType()) {
case EDGE:
processEdge(tenantId, tbMsg, callback);
break;
case ASSET:
processAsset(tenantId, tbMsg, callback);
break;
case DEVICE:
processDevice(tenantId, tbMsg, callback);
break;
case DASHBOARD:
processDashboard(tenantId, tbMsg, callback);
break;
case RULE_CHAIN:
processRuleChain(tenantId, tbMsg, callback);
break;
case ENTITY_VIEW:
processEntityView(tenantId, tbMsg, callback);
break;
case ALARM:
processAlarm(tenantId, tbMsg, callback);
break;
default:
log.debug("Entity type [{}] is not designed to be pushed to edge", tbMsg.getOriginator().getEntityType());
}
} catch (IOException e) {
log.error("Can't push to edge updates, entity type [{}], data [{}]", tbMsg.getOriginator().getEntityType(), tbMsg.getData(), e);
}
}
}
private void processCustomTbMsg(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback) {
EdgeId edgeId = getEdgeIdByOriginatorId(tenantId, tbMsg.getOriginator());
EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(tbMsg.getOriginator().getEntityType());
if (edgeId != null && edgeQueueEntityType != null) {
try {
saveEventToEdgeQueue(tenantId, edgeId, edgeQueueEntityType, tbMsg.getType(), mapper.writeValueAsString(tbMsg), callback);
} catch (IOException e) {
log.error("Error while saving custom tbMsg into Edge Queue", e);
}
}
}
private EdgeQueueEntityType getEdgeQueueTypeByEntityType(EntityType entityType) {
switch (entityType) {
case DEVICE:
return EdgeQueueEntityType.DEVICE;
case ASSET:
return EdgeQueueEntityType.ASSET;
case ENTITY_VIEW:
return EdgeQueueEntityType.ENTITY_VIEW;
default:
log.info("Unsupported entity type: [{}]", entityType);
return null;
}
}
private EdgeId getEdgeIdByOriginatorId(TenantId tenantId, EntityId originatorId) {
switch (originatorId.getEntityType()) {
case DEVICE:
Device device = deviceService.findDeviceById(tenantId, new DeviceId(originatorId.getId()));
return device.getEdgeId();
case ASSET:
Asset asset = assetService.findAssetById(tenantId, new AssetId(originatorId.getId()));
return asset.getEdgeId();
case ENTITY_VIEW:
EntityView entityView = entityViewService.findEntityViewById(tenantId, new EntityViewId(originatorId.getId()));
return entityView.getEdgeId();
default:
log.info("Unsupported entity type: [{}]", originatorId.getEntityType());
return null;
}
}
private void processDevice(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback) throws IOException {
switch (tbMsg.getType()) {
case DataConstants.ENTITY_ASSIGNED_TO_EDGE:
case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE:
processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.DEVICE, callback);
break;
case DataConstants.ENTITY_DELETED:
case DataConstants.ENTITY_CREATED:
case DataConstants.ENTITY_UPDATED:
Device device = mapper.readValue(tbMsg.getData(), Device.class);
if (device.getEdgeId() != null) {
pushEventToEdge(tenantId, device.getEdgeId(), EdgeQueueEntityType.DEVICE, tbMsg, callback);
}
break;
default:
log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg);
}
}
private void processEdge(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback) throws IOException {
switch (tbMsg.getType()) {
case DataConstants.ENTITY_DELETED:
case DataConstants.ENTITY_CREATED:
case DataConstants.ENTITY_UPDATED:
Edge edge = mapper.readValue(tbMsg.getData(), Edge.class);
if (edge != null) {
pushEventToEdge(tenantId, edge.getId(), EdgeQueueEntityType.EDGE, tbMsg, callback);
}
break;
default:
log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg);
}
}
private void processAsset(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback) throws IOException {
switch (tbMsg.getType()) {
case DataConstants.ENTITY_ASSIGNED_TO_EDGE:
case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE:
processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.ASSET, callback);
break;
case DataConstants.ENTITY_DELETED:
case DataConstants.ENTITY_CREATED:
case DataConstants.ENTITY_UPDATED:
Asset asset = mapper.readValue(tbMsg.getData(), Asset.class);
if (asset.getEdgeId() != null) {
pushEventToEdge(tenantId, asset.getEdgeId(), EdgeQueueEntityType.ASSET, tbMsg, callback);
}
break;
default:
log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg);
}
}
private void processEntityView(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback) throws IOException {
switch (tbMsg.getType()) {
case DataConstants.ENTITY_ASSIGNED_TO_EDGE:
case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE:
processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.ENTITY_VIEW, callback);
break;
case DataConstants.ENTITY_DELETED:
case DataConstants.ENTITY_CREATED:
case DataConstants.ENTITY_UPDATED:
EntityView entityView = mapper.readValue(tbMsg.getData(), EntityView.class);
if (entityView.getEdgeId() != null) {
pushEventToEdge(tenantId, entityView.getEdgeId(), EdgeQueueEntityType.ENTITY_VIEW, tbMsg, callback);
}
break;
default:
log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg);
}
}
private void processAlarm(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback) throws IOException {
switch (tbMsg.getType()) {
case DataConstants.ENTITY_DELETED:
case DataConstants.ENTITY_CREATED:
case DataConstants.ENTITY_UPDATED:
case DataConstants.ALARM_ACK:
case DataConstants.ALARM_CLEAR:
Alarm alarm = mapper.readValue(tbMsg.getData(), Alarm.class);
EdgeId edgeId = getEdgeIdByOriginatorId(tenantId, alarm.getOriginator());
EdgeQueueEntityType edgeQueueEntityType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType());
if (edgeId != null && edgeQueueEntityType != null) {
pushEventToEdge(tenantId, edgeId, EdgeQueueEntityType.ALARM, tbMsg, callback);
}
break;
default:
log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg);
}
}
private void processDashboard(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback) throws IOException {
processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.DASHBOARD, callback);
}
private void processRuleChain(TenantId tenantId, TbMsg tbMsg, FutureCallback<Void> callback) throws IOException {
switch (tbMsg.getType()) {
case DataConstants.ENTITY_ASSIGNED_TO_EDGE:
case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE:
processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.RULE_CHAIN, callback);
break;
case DataConstants.ENTITY_DELETED:
case DataConstants.ENTITY_CREATED:
case DataConstants.ENTITY_UPDATED:
RuleChain ruleChain = mapper.readValue(tbMsg.getData(), RuleChain.class);
if (ruleChain.getAssignedEdges() != null && !ruleChain.getAssignedEdges().isEmpty()) {
for (ShortEdgeInfo assignedEdge : ruleChain.getAssignedEdges()) {
pushEventToEdge(tenantId, assignedEdge.getEdgeId(), EdgeQueueEntityType.RULE_CHAIN, tbMsg, callback);
}
}
break;
default:
log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg);
}
}
private void processAssignedEntity(TenantId tenantId, TbMsg tbMsg, EdgeQueueEntityType entityType, FutureCallback<Void> callback) throws IOException {
EdgeId edgeId;
switch (tbMsg.getType()) {
case DataConstants.ENTITY_ASSIGNED_TO_EDGE:
edgeId = new EdgeId(UUID.fromString(tbMsg.getMetaData().getValue("assignedEdgeId")));
pushEventToEdge(tenantId, edgeId, entityType, tbMsg, callback);
break;
case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE:
edgeId = new EdgeId(UUID.fromString(tbMsg.getMetaData().getValue("unassignedEdgeId")));
pushEventToEdge(tenantId, edgeId, entityType, tbMsg, callback);
break;
case DataConstants.ENTITY_DELETED:
case DataConstants.ENTITY_CREATED:
case DataConstants.ENTITY_UPDATED:
Dashboard dashboard = mapper.readValue(tbMsg.getData(), Dashboard.class);
if (dashboard.getAssignedEdges() != null && !dashboard.getAssignedEdges().isEmpty()) {
for (ShortEdgeInfo assignedEdge : dashboard.getAssignedEdges()) {
pushEventToEdge(tenantId, assignedEdge.getEdgeId(), EdgeQueueEntityType.DASHBOARD, tbMsg, callback);
}
}
break;
}
}
private void pushEventToEdge(TenantId tenantId, EdgeId edgeId, EdgeQueueEntityType entityType, TbMsg tbMsg, FutureCallback<Void> callback) throws IOException {
log.debug("Pushing event(s) to edge queue. tenantId [{}], edgeId [{}], entityType [{}], tbMsg [{}]", tenantId, edgeId, entityType, tbMsg);
saveEventToEdgeQueue(tenantId, edgeId, entityType, tbMsg.getType(), tbMsg.getData(), callback);
if (entityType.equals(EdgeQueueEntityType.RULE_CHAIN)) {
pushRuleChainMetadataToEdge(tenantId, edgeId, tbMsg, callback);
}
}
private void saveEventToEdgeQueue(TenantId tenantId, EdgeId edgeId, EdgeQueueEntityType entityType, String type, String data, FutureCallback<Void> callback) throws IOException {
log.debug("Pushing single event to edge queue. tenantId [{}], edgeId [{}], entityType [{}], type[{}], data [{}]", tenantId, edgeId, entityType, type, data);
EdgeQueueEntry queueEntry = new EdgeQueueEntry();
queueEntry.setEntityType(entityType);
queueEntry.setType(type);
queueEntry.setData(data);
Event event = new Event();
event.setEntityId(edgeId);
event.setTenantId(tenantId);
event.setType(DataConstants.EDGE_QUEUE_EVENT_TYPE);
event.setBody(mapper.valueToTree(queueEntry));
ListenableFuture<Event> saveFuture = eventService.saveAsync(event);
addMainCallback(saveFuture, callback);
}
private void addMainCallback(ListenableFuture<Event> saveFuture, final FutureCallback<Void> callback) {
Futures.addCallback(saveFuture, new FutureCallback<Event>() {
@Override
public void onSuccess(@Nullable Event result) {
callback.onSuccess(null);
}
@Override
public void onFailure(Throwable t) {
callback.onFailure(t);
}
}, tsCallBackExecutor);
}
private void pushRuleChainMetadataToEdge(TenantId tenantId, EdgeId edgeId, TbMsg tbMsg, FutureCallback<Void> callback) throws IOException {
RuleChain ruleChain = mapper.readValue(tbMsg.getData(), RuleChain.class);
switch (tbMsg.getType()) {
case DataConstants.ENTITY_ASSIGNED_TO_EDGE:
case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE:
case DataConstants.ENTITY_UPDATED:
RuleChainMetaData ruleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChain.getId());
saveEventToEdgeQueue(tenantId, edgeId, EdgeQueueEntityType.RULE_CHAIN_METADATA, tbMsg.getType(), mapper.writeValueAsString(ruleChainMetaData), callback);
break;
default:
log.warn("Unsupported msgType [{}], tbMsg [{}]", tbMsg.getType(), tbMsg);
}
}
@Override
public PageData<Event> findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
return eventService.findEvents(tenantId, edgeId, DataConstants.EDGE_QUEUE_EVENT_TYPE, pageLink);
}
@Override
public Edge setRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException {
edge.setRootRuleChainId(ruleChainId);
Edge savedEdge = saveEdge(edge);
ruleChainService.updateEdgeRuleChains(tenantId, savedEdge.getId());
RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, ruleChainId);
saveEventToEdgeQueue(tenantId, edge.getId(), EdgeQueueEntityType.RULE_CHAIN, DataConstants.ENTITY_UPDATED, mapper.writeValueAsString(ruleChain), new FutureCallback<Void>() {
@Override
public void onSuccess(@Nullable Void aVoid) {
log.debug("Event saved successfully!");
}
@Override
public void onFailure(Throwable t) {
log.debug("Failure during event save", t);
}
});
return savedEdge;
}
private DataValidator<Edge> edgeValidator =
new DataValidator<Edge>() {
@Override
protected void validateCreate(TenantId tenantId, Edge edge) {
edgeDao.findEdgeByTenantIdAndName(edge.getTenantId().getId(), edge.getName()).ifPresent(
d -> {
throw new DataValidationException("Edge with such name already exists!");
}
);
}
@Override
protected void validateUpdate(TenantId tenantId, Edge edge) {
edgeDao.findEdgeByTenantIdAndName(edge.getTenantId().getId(), edge.getName()).ifPresent(
e -> {
if (!e.getUuidId().equals(edge.getUuidId())) {
throw new DataValidationException("Edge with such name already exists!");
}
}
);
}
@Override
protected void validateDataImpl(TenantId tenantId, Edge edge) {
if (StringUtils.isEmpty(edge.getType())) {
throw new DataValidationException("Edge type should be specified!");
}
if (StringUtils.isEmpty(edge.getName())) {
throw new DataValidationException("Edge name should be specified!");
}
if (edge.getTenantId() == null) {
throw new DataValidationException("Edge should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(edge.getTenantId(), edge.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Edge is referencing to non-existent tenant!");
}
}
if (edge.getCustomerId() == null) {
edge.setCustomerId(new CustomerId(NULL_UUID));
} else if (!edge.getCustomerId().getId().equals(NULL_UUID)) {
Customer customer = customerDao.findById(edge.getTenantId(), edge.getCustomerId().getId());
if (customer == null) {
throw new DataValidationException("Can't assign edge to non-existent customer!");
}
if (!customer.getTenantId().getId().equals(edge.getTenantId().getId())) {
throw new DataValidationException("Can't assign edge to customer from different tenant!");
}
}
}
};
private PaginatedRemover<TenantId, Edge> tenantEdgesRemover =
new PaginatedRemover<TenantId, Edge>() {
@Override
protected PageData<Edge> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {
return edgeDao.findEdgesByTenantId(id.getId(), pageLink);
}
@Override
protected void removeEntity(TenantId tenantId, Edge entity) {
deleteEdge(tenantId, new EdgeId(entity.getUuidId()));
}
};
private PaginatedRemover<CustomerId, Edge> customerEdgeUnasigner = new PaginatedRemover<CustomerId, Edge>() {
@Override
protected PageData<Edge> findEntities(TenantId tenantId, CustomerId id, PageLink pageLink) {
return edgeDao.findEdgesByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
}
@Override
protected void removeEntity(TenantId tenantId, Edge entity) {
unassignEdgeFromCustomer(tenantId, new EdgeId(entity.getUuidId()));
}
};
}

17
dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java

@ -130,4 +130,21 @@ public interface EdgeDao extends Dao<Edge> {
PageData<EdgeInfo> findEdgeInfosByTenantId(UUID tenantId, PageLink pageLink);
/**
* Find edges by tenantId and ruleChainId.
*
* @param tenantId the tenantId
* @param ruleChainId the ruleChainId
* @return the list of rule chain objects
*/
ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId);
/**
* Find edges by tenantId and dashboardId.
*
* @param tenantId the tenantId
* @param dashboardId the dashboardId
* @return the list of rule chain objects
*/
ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId);
}

51
dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java

@ -0,0 +1,51 @@
/**
* 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.dao.edge;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.Dao;
import java.util.UUID;
/**
* The Interface EdgeEventDao.
*/
public interface EdgeEventDao extends Dao<EdgeEvent> {
/**
* Save or update edge event object async
*
* @param edgeEvent the event object
* @return saved edge event object future
*/
ListenableFuture<EdgeEvent> saveAsync(EdgeEvent edgeEvent);
/**
* Find edge events by tenantId, edgeId and pageLink.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param pageLink the pageLink
* @return the event list
*/
PageData<EdgeEvent> findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink);
}

407
dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java

@ -0,0 +1,407 @@
/**
* 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.dao.edge;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
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.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;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.tenant.TenantDao;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.CacheConstants.EDGE_CACHE;
import static org.thingsboard.server.dao.DaoUtil.toUUIDs;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateIds;
import static org.thingsboard.server.dao.service.Validator.validatePageLink;
import static org.thingsboard.server.dao.service.Validator.validateString;
@Service
@Slf4j
public class EdgeServiceImpl extends AbstractEntityService implements EdgeService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
@Autowired
private EdgeDao edgeDao;
@Autowired
private TenantDao tenantDao;
@Autowired
private CustomerDao customerDao;
@Autowired
private CacheManager cacheManager;
@Autowired
private DashboardService dashboardService;
@Autowired
private RuleChainService ruleChainService;
@Autowired
private RelationService relationService;
@Override
public Edge findEdgeById(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing findEdgeById [{}]", edgeId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
return edgeDao.findById(tenantId, edgeId.getId());
}
@Override
public ListenableFuture<Edge> findEdgeByIdAsync(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing findEdgeById [{}]", edgeId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
return edgeDao.findByIdAsync(tenantId, edgeId.getId());
}
@Cacheable(cacheNames = EDGE_CACHE, key = "{#tenantId, #name}")
@Override
public Edge findEdgeByTenantIdAndName(TenantId tenantId, String name) {
log.trace("Executing findEdgeByTenantIdAndName [{}][{}]", tenantId, name);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Optional<Edge> edgeOpt = edgeDao.findEdgeByTenantIdAndName(tenantId.getId(), name);
return edgeOpt.orElse(null);
}
@Override
public Optional<Edge> findEdgeByRoutingKey(TenantId tenantId, String routingKey) {
log.trace("Executing findEdgeByRoutingKey [{}]", routingKey);
Validator.validateString(routingKey, "Incorrect edge routingKey for search request.");
return edgeDao.findByRoutingKey(tenantId.getId(), routingKey);
}
@CacheEvict(cacheNames = EDGE_CACHE, key = "{#edge.tenantId, #edge.name}")
@Override
public Edge saveEdge(Edge edge) {
log.trace("Executing saveEdge [{}]", edge);
edgeValidator.validate(edge, Edge::getTenantId);
return edgeDao.save(edge.getTenantId(), edge);
}
@Override
public Edge assignEdgeToCustomer(TenantId tenantId, EdgeId edgeId, CustomerId customerId) {
Edge edge = findEdgeById(tenantId, edgeId);
edge.setCustomerId(customerId);
return saveEdge(edge);
}
@Override
public Edge unassignEdgeFromCustomer(TenantId tenantId, EdgeId edgeId) {
Edge edge = findEdgeById(tenantId, edgeId);
edge.setCustomerId(null);
return saveEdge(edge);
}
@Override
public void deleteEdge(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing deleteEdge [{}]", edgeId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
dashboardService.unassignEdgeDashboards(tenantId, edgeId);
// TODO: validate that rule chains are removed by deleteEntityRelations(tenantId, edgeId); call
ruleChainService.unassignEdgeRuleChains(tenantId, edgeId);
List<Object> list = new ArrayList<>();
list.add(edge.getTenantId());
list.add(edge.getName());
Cache cache = cacheManager.getCache(EDGE_CACHE);
cache.evict(list);
deleteEntityRelations(tenantId, edgeId);
edgeDao.removeById(tenantId, edgeId.getId());
}
@Override
public PageData<Edge> findEdgesByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findEdgesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantId(tenantId.getId(), pageLink);
}
@Override
public PageData<Edge> findEdgesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@Override
public PageData<EdgeInfo> findEdgeInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) {
log.trace("Executing findEdgeInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return edgeDao.findEdgeInfosByTenantIdAndType(tenantId.getId(), type, pageLink);
}
@Override
public PageData<EdgeInfo> findEdgeInfosByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findEdgeInfosByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validatePageLink(pageLink);
return edgeDao.findEdgeInfosByTenantId(tenantId.getId(), pageLink);
}
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdAndIdsAsync(TenantId tenantId, List<EdgeId> edgeIds) {
log.trace("Executing findEdgesByTenantIdAndIdsAsync, tenantId [{}], edgeIds [{}]", tenantId, edgeIds);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateIds(edgeIds, "Incorrect edgeIds " + edgeIds);
return edgeDao.findEdgesByTenantIdAndIdsAsync(tenantId.getId(), toUUIDs(edgeIds));
}
@Override
public void deleteEdgesByTenantId(TenantId tenantId) {
log.trace("Executing deleteEdgesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
tenantEdgesRemover.removeEntities(tenantId, tenantId);
}
@Override
public PageData<Edge> findEdgesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndCustomerId, tenantId [{}], customerId [{}], pageLink [{}]", tenantId, customerId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
}
@Override
public PageData<Edge> findEdgesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink) {
log.trace("Executing findEdgesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink);
return edgeDao.findEdgesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
}
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<EdgeId> edgeIds) {
log.trace("Executing findEdgesByTenantIdCustomerIdAndIdsAsync, tenantId [{}], customerId [{}], edgeIds [{}]", tenantId, customerId, edgeIds);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
validateIds(edgeIds, "Incorrect edgeIds " + edgeIds);
return edgeDao.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId.getId(),
customerId.getId(), toUUIDs(edgeIds));
}
@Override
public void unassignCustomerEdges(TenantId tenantId, CustomerId customerId) {
log.trace("Executing unassignCustomerEdges, tenantId [{}], customerId [{}]", tenantId, customerId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
customerEdgeUnassigner.removeEntities(tenantId, customerId);
}
@Override
public ListenableFuture<List<Edge>> findEdgesByQuery(TenantId tenantId, EdgeSearchQuery query) {
ListenableFuture<List<EntityRelation>> relations = relationService.findByQuery(tenantId, query.toEntitySearchQuery());
ListenableFuture<List<Edge>> edges = Futures.transformAsync(relations, r -> {
EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection();
List<ListenableFuture<Edge>> futures = new ArrayList<>();
for (EntityRelation relation : r) {
EntityId entityId = direction == EntitySearchDirection.FROM ? relation.getTo() : relation.getFrom();
if (entityId.getEntityType() == EntityType.EDGE) {
futures.add(findEdgeByIdAsync(tenantId, new EdgeId(entityId.getId())));
}
}
return Futures.successfulAsList(futures);
}, MoreExecutors.directExecutor());
edges = Futures.transform(edges, new Function<List<Edge>, List<Edge>>() {
@Nullable
@Override
public List<Edge> apply(@Nullable List<Edge> edgeList) {
return edgeList == null ? Collections.emptyList() : edgeList.stream().filter(edge -> query.getEdgeTypes().contains(edge.getType())).collect(Collectors.toList());
}
}, MoreExecutors.directExecutor());
return edges;
}
@Override
public ListenableFuture<List<EntitySubtype>> findEdgeTypesByTenantId(TenantId tenantId) {
log.trace("Executing findEdgeTypesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
ListenableFuture<List<EntitySubtype>> tenantEdgeTypes = edgeDao.findTenantEdgeTypesAsync(tenantId.getId());
return Futures.transform(tenantEdgeTypes,
edgeTypes -> {
edgeTypes.sort(Comparator.comparing(EntitySubtype::getType));
return edgeTypes;
}, MoreExecutors.directExecutor());
}
@Override
public void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing assignDefaultRuleChainsToEdge, tenantId [{}], edgeId [{}]", tenantId, edgeId);
ListenableFuture<List<RuleChain>> future = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId);
Futures.transform(future, ruleChains -> {
if (ruleChains != null && !ruleChains.isEmpty()) {
for (RuleChain ruleChain : ruleChains) {
ruleChainService.assignRuleChainToEdge(tenantId, ruleChain.getId(), edgeId);
}
}
return null;
}, MoreExecutors.directExecutor());
}
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId) {
log.trace("Executing findEdgesByTenantIdAndRuleChainId, tenantId [{}], ruleChainId [{}]", tenantId, ruleChainId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(ruleChainId, "Incorrect ruleChainId " + ruleChainId);
return edgeDao.findEdgesByTenantIdAndRuleChainId(tenantId.getId(), ruleChainId.getId());
}
@Override
public ListenableFuture<List<Edge>> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId) {
log.trace("Executing findEdgesByTenantIdAndDashboardId, tenantId [{}], dashboardId [{}]", tenantId, dashboardId);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
return edgeDao.findEdgesByTenantIdAndDashboardId(tenantId.getId(), dashboardId.getId());
}
private DataValidator<Edge> edgeValidator =
new DataValidator<Edge>() {
@Override
protected void validateCreate(TenantId tenantId, Edge edge) {
}
@Override
protected void validateUpdate(TenantId tenantId, Edge edge) {
}
@Override
protected void validateDataImpl(TenantId tenantId, Edge edge) {
if (StringUtils.isEmpty(edge.getType())) {
throw new DataValidationException("Edge type should be specified!");
}
if (StringUtils.isEmpty(edge.getName())) {
throw new DataValidationException("Edge name should be specified!");
}
if (StringUtils.isEmpty(edge.getSecret())) {
throw new DataValidationException("Edge secret should be specified!");
}
if (StringUtils.isEmpty(edge.getRoutingKey())) {
throw new DataValidationException("Edge routing key should be specified!");
}
if (edge.getTenantId() == null) {
throw new DataValidationException("Edge should be assigned to tenant!");
} else {
Tenant tenant = tenantDao.findById(edge.getTenantId(), edge.getTenantId().getId());
if (tenant == null) {
throw new DataValidationException("Edge is referencing to non-existent tenant!");
}
}
if (edge.getCustomerId() == null) {
edge.setCustomerId(new CustomerId(NULL_UUID));
} else if (!edge.getCustomerId().getId().equals(NULL_UUID)) {
Customer customer = customerDao.findById(edge.getTenantId(), edge.getCustomerId().getId());
if (customer == null) {
throw new DataValidationException("Can't assign edge to non-existent customer!");
}
if (!customer.getTenantId().getId().equals(edge.getTenantId().getId())) {
throw new DataValidationException("Can't assign edge to customer from different tenant!");
}
}
}
};
private PaginatedRemover<TenantId, Edge> tenantEdgesRemover =
new PaginatedRemover<TenantId, Edge>() {
@Override
protected PageData<Edge> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) {
return edgeDao.findEdgesByTenantId(id.getId(), pageLink);
}
@Override
protected void removeEntity(TenantId tenantId, Edge entity) {
deleteEdge(tenantId, new EdgeId(entity.getUuidId()));
}
};
private PaginatedRemover<CustomerId, Edge> customerEdgeUnassigner = new PaginatedRemover<CustomerId, Edge>() {
@Override
protected PageData<Edge> findEntities(TenantId tenantId, CustomerId id, PageLink pageLink) {
return edgeDao.findEdgesByTenantIdAndCustomerId(tenantId.getId(), id.getId(), pageLink);
}
@Override
protected void removeEntity(TenantId tenantId, Edge entity) {
unassignEdgeFromCustomer(tenantId, new EdgeId(entity.getUuidId()));
}
};
}

15
dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java

@ -20,16 +20,31 @@ import org.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
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.dao.relation.RelationService;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
@Slf4j
public abstract class AbstractEntityService {
public static final String INCORRECT_EDGE_ID = "Incorrect edgeId ";
public static final String INCORRECT_PAGE_LINK = "Incorrect page link ";
@Autowired
protected RelationService relationService;
protected void createRelation(TenantId tenantId, EntityRelation relation) throws ExecutionException, InterruptedException {
log.debug("Creating relation: {}", relation);
relationService.saveRelation(tenantId, relation);
}
protected void deleteRelation(TenantId tenantId, EntityRelation relation) throws ExecutionException, InterruptedException {
log.debug("Deleting relation: {}", relation);
relationService.deleteRelation(tenantId, relation);
}
protected void deleteEntityRelations(TenantId tenantId, EntityId entityId) {
log.trace("Executing deleteEntityRelations [{}]", entityId);
relationService.deleteEntityRelations(tenantId, entityId);

21
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java

@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.EntityViewInfo;
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.dao.Dao;
import java.util.List;
@ -161,22 +162,8 @@ public interface EntityViewDao extends Dao<EntityView> {
* @param pageLink the page link
* @return the list of entity view objects
*/
PageData<EntityView> findEntityViewsByTenantIdAndEdgeId(UUID tenantId,
UUID edgeId,
PageLink pageLink);
/**
* Find entity views by tenantId, edgeId, type and page link.
*
* @param tenantId the tenantId
* @param edgeId the edgeId
* @param type the type
* @param pageLink the page link
* @return the list of entity view objects
*/
PageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId,
UUID edgeId,
String type,
PageLink pageLink);
ListenableFuture<PageData<EntityView>> findEntityViewsByTenantIdAndEdgeId(UUID tenantId,
UUID edgeId,
TimePageLink pageLink);
}

62
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java

@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.EntityType;
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.edge.Edge;
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
@ -43,9 +44,12 @@ 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 org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
@ -59,6 +63,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE;
@ -89,6 +94,9 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
@Autowired
private CustomerDao customerDao;
@Autowired
private EdgeService edgeService;
@Autowired
private CacheManager cacheManager;
@ -333,40 +341,46 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti
@Override
public EntityView assignEntityViewToEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId) {
EntityView entityView = findEntityViewById(tenantId, entityViewId);
entityView.setEdgeId(edgeId);
return saveEntityView(entityView);
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't assign entityView to non-existent edge!");
}
if (!edge.getTenantId().getId().equals(entityView.getTenantId().getId())) {
throw new DataValidationException("Can't assign entityView to edge from different tenant!");
}
try {
createRelation(tenantId, new EntityRelation(edgeId, entityViewId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create entityView relation. Edge Id: [{}]", entityViewId, edgeId);
throw new RuntimeException(e);
}
return entityView;
}
@CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityViewId}")
@Override
public EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId) {
public EntityView unassignEntityViewFromEdge(TenantId tenantId, EntityViewId entityViewId, EdgeId edgeId) {
EntityView entityView = findEntityViewById(tenantId, entityViewId);
entityView.setEdgeId(null);
return saveEntityView(entityView);
}
@Override
public PageData<EntityView> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId,
PageLink pageLink) {
log.trace("Executing findEntityViewsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}]," +
" pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validatePageLink(pageLink);
return entityViewDao.findEntityViewsByTenantIdAndEdgeId(tenantId.getId(),
edgeId.getId(), pageLink);
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't unassign entityView from non-existent edge!");
}
try {
deleteRelation(tenantId, new EntityRelation(edgeId, entityViewId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete entityView relation. Edge Id: [{}]", entityViewId, edgeId);
throw new RuntimeException(e);
}
return entityView;
}
@Override
public PageData<EntityView> findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) {
log.trace("Executing findEntityViewsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}]," +
" pageLink [{}], type [{}]", tenantId, edgeId, pageLink, type);
public ListenableFuture<PageData<EntityView>> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId,
TimePageLink pageLink) {
log.trace("Executing findEntityViewsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateId(edgeId, INCORRECT_EDGE_ID + edgeId);
validatePageLink(pageLink);
validateString(type, "Incorrect type " + type);
return entityViewDao.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId.getId(),
edgeId.getId(), type, pageLink);
return entityViewDao.findEntityViewsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
}
private DataValidator<EntityView> entityViewValidator =

16
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -142,7 +142,6 @@ public class ModelConstants {
public static final String DEVICE_TYPE_PROPERTY = "type";
public static final String DEVICE_LABEL_PROPERTY = "label";
public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String DEVICE_EDGE_ID_PROPERTY = "edge_id";
public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text";
@ -158,7 +157,6 @@ public class ModelConstants {
public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY;
public static final String ENTITY_VIEW_EDGE_ID_PROPERTY = "edge_id";
public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF = "entity_view_by_tenant_and_customer";
public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_TYPE_CF = "entity_view_by_tenant_and_customer_and_type";
public static final String ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF = "entity_view_by_tenant_and_entity_id";
@ -206,7 +204,6 @@ public class ModelConstants {
public static final String ASSET_TYPE_PROPERTY = "type";
public static final String ASSET_LABEL_PROPERTY = "label";
public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String ASSET_EDGE_ID_PROPERTY = "edge_id";
public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text";
public static final String ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_by_type_and_search_text";
@ -302,7 +299,6 @@ public class ModelConstants {
public static final String DASHBOARD_TITLE_PROPERTY = TITLE_PROPERTY;
public static final String DASHBOARD_CONFIGURATION_PROPERTY = "configuration";
public static final String DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY = "assigned_customers";
public static final String DASHBOARD_ASSIGNED_EDGES_PROPERTY = "assigned_edges";
public static final String DASHBOARD_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "dashboard_by_tenant_and_search_text";
@ -347,7 +343,6 @@ public class ModelConstants {
public static final String RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY = "first_rule_node_id";
public static final String RULE_CHAIN_ROOT_PROPERTY = "root";
public static final String RULE_CHAIN_CONFIGURATION_PROPERTY = "configuration";
public static final String RULE_CHAIN_ASSIGNED_EDGES_PROPERTY = "assigned_edges";
public static final String RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_and_search_text";
public static final String RULE_CHAIN_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_by_type_and_search_text";
@ -377,6 +372,17 @@ public class ModelConstants {
public static final String EDGE_ROUTING_KEY_PROPERTY = "routing_key";
public static final String EDGE_SECRET_PROPERTY = "secret";
/**
* Cassandra edge queue constants.
*/
public static final String EDGE_EVENT_COLUMN_FAMILY_NAME = "edge_event";
public static final String EDGE_EVENT_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String EDGE_EVENT_EDGE_ID_PROPERTY = "edge_id";
public static final String EDGE_EVENT_TYPE_PROPERTY = "edge_event_type";
public static final String EDGE_EVENT_ACTION_PROPERTY = "edge_event_action";
public static final String EDGE_EVENT_ENTITY_ID_PROPERTY = "entity_id";
public static final String EDGE_EVENT_ENTITY_BODY_PROPERTY = "entity_body";
/**
* Cassandra attributes and timeseries constants.
*/

11
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java

@ -25,7 +25,6 @@ import org.thingsboard.server.common.data.UUIDConverter;
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.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
@ -36,7 +35,6 @@ import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_EDGE_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_LABEL_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY;
@ -55,9 +53,6 @@ public abstract class AbstractAssetEntity<T extends Asset> extends BaseSqlEntity
@Column(name = ASSET_CUSTOMER_ID_PROPERTY)
private String customerId;
@Column(name = ASSET_EDGE_ID_PROPERTY)
private String edgeId;
@Column(name = ASSET_NAME_PROPERTY)
private String name;
@ -88,9 +83,6 @@ public abstract class AbstractAssetEntity<T extends Asset> extends BaseSqlEntity
if (asset.getCustomerId() != null) {
this.customerId = UUIDConverter.fromTimeUUID(asset.getCustomerId().getId());
}
if (asset.getEdgeId() != null) {
this.edgeId = UUIDConverter.fromTimeUUID(asset.getEdgeId().getId());
}
this.name = asset.getName();
this.type = asset.getType();
this.label = asset.getLabel();
@ -131,9 +123,6 @@ public abstract class AbstractAssetEntity<T extends Asset> extends BaseSqlEntity
if (customerId != null) {
asset.setCustomerId(new CustomerId(UUIDConverter.fromString(customerId)));
}
if (edgeId != null) {
asset.setEdgeId(new EdgeId(UUIDConverter.fromString(edgeId)));
}
asset.setName(name);
asset.setType(type);
asset.setLabel(label);

10
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java

@ -24,7 +24,6 @@ import org.hibernate.annotations.TypeDef;
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.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
@ -46,9 +45,6 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
@Column(name = ModelConstants.DEVICE_CUSTOMER_ID_PROPERTY)
private String customerId;
@Column(name = ModelConstants.DEVICE_EDGE_ID_PROPERTY)
private String edgeId;
@Column(name = ModelConstants.DEVICE_TYPE_PROPERTY)
private String type;
@ -79,9 +75,6 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
if (device.getCustomerId() != null) {
this.customerId = toString(device.getCustomerId().getId());
}
if (device.getEdgeId() != null) {
this.edgeId = toString(device.getEdgeId().getId());
}
this.name = device.getName();
this.type = device.getType();
this.label = device.getLabel();
@ -118,9 +111,6 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti
if (customerId != null) {
device.setCustomerId(new CustomerId(toUUID(customerId)));
}
if (edgeId != null) {
device.setEdgeId(new EdgeId(toUUID(edgeId)));
}
device.setName(name);
device.setType(type);
device.setLabel(label);

9
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java

@ -66,9 +66,6 @@ public abstract class AbstractEntityViewEntity<T extends EntityView> extends Bas
@Column(name = ModelConstants.ENTITY_VIEW_CUSTOMER_ID_PROPERTY)
private String customerId;
@Column(name = ModelConstants.ENTITY_VIEW_EDGE_ID_PROPERTY)
private String edgeId;
@Column(name = ModelConstants.DEVICE_TYPE_PROPERTY)
private String type;
@ -111,9 +108,6 @@ public abstract class AbstractEntityViewEntity<T extends EntityView> extends Bas
if (entityView.getCustomerId() != null) {
this.customerId = toString(entityView.getCustomerId().getId());
}
if (entityView.getEdgeId() != null) {
this.edgeId = toString(entityView.getEdgeId().getId());
}
this.type = entityView.getType();
this.name = entityView.getName();
try {
@ -165,9 +159,6 @@ public abstract class AbstractEntityViewEntity<T extends EntityView> extends Bas
if (customerId != null) {
entityView.setCustomerId(new CustomerId(toUUID(customerId)));
}
if (edgeId != null) {
entityView.setEdgeId(new EdgeId(toUUID(edgeId)));
}
entityView.setType(type);
entityView.setName(name);
try {

20
dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java

@ -28,7 +28,6 @@ import org.hibernate.annotations.TypeDef;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
@ -53,8 +52,6 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedCustomersType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
private static final JavaType assignedEdgesType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class);
@Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY)
private String tenantId;
@ -68,9 +65,6 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
@Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private String assignedCustomers;
@Column(name = ModelConstants.DASHBOARD_ASSIGNED_EDGES_PROPERTY)
private String assignedEdges;
@Type(type = "json")
@Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY)
private JsonNode configuration;
@ -94,13 +88,6 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
log.error("Unable to serialize assigned customers to string!", e);
}
}
if (dashboard.getAssignedEdges() != null) {
try {
this.assignedEdges = objectMapper.writeValueAsString(dashboard.getAssignedEdges());
} catch (JsonProcessingException e) {
log.error("Unable to serialize assigned edges to string!", e);
}
}
this.configuration = dashboard.getConfiguration();
}
@ -129,13 +116,6 @@ public final class DashboardEntity extends BaseSqlEntity<Dashboard> implements S
log.warn("Unable to parse assigned customers!", e);
}
}
if (!StringUtils.isEmpty(assignedEdges)) {
try {
dashboard.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType));
} catch (IOException e) {
log.warn("Unable to parse assigned edges!", e);
}
}
dashboard.setConfiguration(configuration);
return dashboard;
}

20
dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java

@ -25,7 +25,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.ShortCustomerInfo;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseSqlEntity;
@ -48,8 +47,6 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final JavaType assignedCustomersType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortCustomerInfo.class);
private static final JavaType assignedEdgesType =
objectMapper.getTypeFactory().constructCollectionType(HashSet.class, ShortEdgeInfo.class);
@Column(name = ModelConstants.DASHBOARD_TENANT_ID_PROPERTY)
private String tenantId;
@ -63,9 +60,6 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
@Column(name = ModelConstants.DASHBOARD_ASSIGNED_CUSTOMERS_PROPERTY)
private String assignedCustomers;
@Column(name = ModelConstants.DASHBOARD_ASSIGNED_EDGES_PROPERTY)
private String assignedEdges;
public DashboardInfoEntity() {
super();
}
@ -85,13 +79,6 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
log.error("Unable to serialize assigned customers to string!", e);
}
}
if (dashboardInfo.getAssignedEdges() != null) {
try {
this.assignedEdges = objectMapper.writeValueAsString(dashboardInfo.getAssignedEdges());
} catch (JsonProcessingException e) {
log.error("Unable to serialize assigned edges to string!", e);
}
}
}
@Override
@ -123,13 +110,6 @@ public class DashboardInfoEntity extends BaseSqlEntity<DashboardInfo> implements
log.warn("Unable to parse assigned customers!", e);
}
}
if (!StringUtils.isEmpty(assignedEdges)) {
try {
dashboardInfo.setAssignedEdges(objectMapper.readValue(assignedEdges, assignedEdgesType));
} catch (IOException e) {
log.warn("Unable to parse assigned edges!", e);
}
}
return dashboardInfo;
}

121
dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java

@ -0,0 +1,121 @@
/**
* 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.dao.model.sql;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.EdgeEventId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.model.BaseEntity;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ACTION_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_EDGE_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ENTITY_BODY_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_ENTITY_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EDGE_EVENT_TYPE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.EPOCH_DIFF;
import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN;
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@TypeDef(name = "json", typeClass = JsonStringType.class)
@Table(name = EDGE_EVENT_COLUMN_FAMILY_NAME)
@NoArgsConstructor
public class EdgeEventEntity extends BaseSqlEntity<EdgeEvent> implements BaseEntity<EdgeEvent> {
@Column(name = EDGE_EVENT_TENANT_ID_PROPERTY)
private String tenantId;
@Column(name = EDGE_EVENT_EDGE_ID_PROPERTY)
private String edgeId;
@Column(name = EDGE_EVENT_ENTITY_ID_PROPERTY)
private String entityId;
@Enumerated(EnumType.STRING)
@Column(name = EDGE_EVENT_TYPE_PROPERTY)
private EdgeEventType edgeEventType;
@Column(name = EDGE_EVENT_ACTION_PROPERTY)
private String edgeEventAction;
@Type(type = "json")
@Column(name = EDGE_EVENT_ENTITY_BODY_PROPERTY)
private JsonNode entityBody;
@Column(name = TS_COLUMN)
private long ts;
public EdgeEventEntity(EdgeEvent edgeEvent) {
if (edgeEvent.getId() != null) {
this.setUuid(edgeEvent.getId().getId());
this.ts = getTs(edgeEvent.getId().getId());
} else {
this.ts = System.currentTimeMillis();
}
if (edgeEvent.getTenantId() != null) {
this.tenantId = toString(edgeEvent.getTenantId().getId());
}
if (edgeEvent.getEdgeId() != null) {
this.edgeId = toString(edgeEvent.getEdgeId().getId());
}
if (edgeEvent.getEntityId() != null) {
this.entityId = toString(edgeEvent.getEntityId());
}
this.edgeEventType = edgeEvent.getEdgeEventType();
this.edgeEventAction = edgeEvent.getEdgeEventAction();
this.entityBody = edgeEvent.getEntityBody();
}
@Override
public EdgeEvent toData() {
EdgeEvent edgeEvent = new EdgeEvent(new EdgeEventId(this.getUuid()));
edgeEvent.setCreatedTime(Uuids.unixTimestamp(this.getUuid()));
edgeEvent.setTenantId(new TenantId(toUUID(tenantId)));
edgeEvent.setEdgeId(new EdgeId(toUUID(edgeId)));
if (entityId != null) {
edgeEvent.setEntityId(toUUID(entityId));
}
edgeEvent.setEdgeEventType(edgeEventType);
edgeEvent.setEdgeEventAction(edgeEventAction);
edgeEvent.setEntityBody(entityBody);
return edgeEvent;
}
private static long getTs(UUID uuid) {
return (uuid.timestamp() - EPOCH_DIFF) / 10000;
}
}

177
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -23,7 +23,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ShortEdgeInfo;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.EdgeId;
@ -33,6 +32,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.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
@ -41,12 +41,12 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.edge.EdgeDao;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.TimePaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.tenant.TenantDao;
@ -56,6 +56,8 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import static org.thingsboard.server.dao.service.Validator.validateId;
/**
* Created by igor on 3/12/18.
*/
@ -65,6 +67,8 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
private static final ObjectMapper objectMapper = new ObjectMapper();
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
@Autowired
private RuleChainDao ruleChainDao;
@ -74,9 +78,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Autowired
private TenantDao tenantDao;
@Autowired
private EdgeDao edgeDao;
@Autowired
private EdgeService edgeService;
@ -281,7 +282,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override
public RuleChain getRootTenantRuleChain(TenantId tenantId) {
return getRootRuleChainByType(tenantId, RuleChainType.SYSTEM);
return getRootRuleChainByType(tenantId, RuleChainType.CORE);
}
private RuleChain getRootRuleChainByType(TenantId tenantId, RuleChainType type) {
@ -374,11 +375,18 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
if (ruleChain.isRoot()) {
throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!");
}
if (ruleChain.getAssignedEdges() != null && !ruleChain.getAssignedEdges().isEmpty()) {
for (ShortEdgeInfo assignedEdge : ruleChain.getAssignedEdges()) {
if (assignedEdge.getRootRuleChainId() != null && assignedEdge.getRootRuleChainId().equals(ruleChainId)) {
throw new DataValidationException("Can't delete rule chain that is root for edge [" + assignedEdge.getTitle() + "]. Please assign another root rule chain first to the edge!");
if (RuleChainType.EDGE.equals(ruleChain.getType())) {
try {
List<Edge> edges = edgeService.findEdgesByTenantIdAndRuleChainId(tenantId, ruleChainId).get();
if (edges != null) {
for (Edge edge : edges) {
if (edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) {
throw new DataValidationException("Can't delete rule chain that is root for edge [" + edge.getName() + "]. Please assign another root rule chain first to the edge!");
}
}
}
} catch (InterruptedException | ExecutionException e) {
log.error("Can't get edges by tenant id [{}] and rule chain id [{}]", tenantId.getId(), ruleChainId.getId(), e);
}
}
}
@ -394,21 +402,18 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override
public RuleChain assignRuleChainToEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId) {
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't assign ruleChain to non-existent edge!");
}
if (!edge.getTenantId().getId().equals(ruleChain.getTenantId().getId())) {
throw new DataValidationException("Can't assign ruleChain to edge from different tenant!");
}
if (ruleChain.addAssignedEdge(edge)) {
try {
createRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create ruleChain relation. Edge Id: [{}]", ruleChainId, edgeId);
throw new RuntimeException(e);
}
ruleChain = saveRuleChain(ruleChain);
try {
createRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to create ruleChain relation. Edge Id: [{}]", ruleChainId, edgeId);
throw new RuntimeException(e);
}
return ruleChain;
}
@ -416,31 +421,27 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override
public RuleChain unassignRuleChainFromEdge(TenantId tenantId, RuleChainId ruleChainId, EdgeId edgeId, boolean remove) {
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't unassign rule chain from non-existent edge!");
}
if (!remove && edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) {
throw new DataValidationException("Can't unassign root rule chain from edge [" + edge.getName() + "]. Please assign another root rule chain first!");
}
if (ruleChain.removeAssignedEdge(edge)) {
try {
deleteRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete rule chain relation. Edge Id: [{}]", ruleChainId, edgeId);
throw new RuntimeException(e);
}
return saveRuleChain(ruleChain);
} else {
return ruleChain;
try {
deleteRelation(tenantId, new EntityRelation(edgeId, ruleChainId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE));
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to delete rule chain relation. Edge Id: [{}]", ruleChainId, edgeId);
throw new RuntimeException(e);
}
return ruleChain;
}
@Override
public void unassignEdgeRuleChains(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing unassignEdgeRuleChains, edgeId [{}]", edgeId);
Validator.validateId(edgeId, "Incorrect edgeId " + edgeId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
Edge edge = edgeService.findEdgeById(tenantId, edgeId);
if (edge == null) {
throw new DataValidationException("Can't unassign ruleChains from non-existent edge!");
}
@ -448,21 +449,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
@Override
public void updateEdgeRuleChains(TenantId tenantId, EdgeId edgeId) {
log.trace("Executing updateEdgeRuleChains, edgeId [{}]", edgeId);
Validator.validateId(edgeId, "Incorrect edgeId " + edgeId);
Edge edge = edgeDao.findById(tenantId, edgeId.getId());
if (edge == null) {
throw new DataValidationException("Can't update ruleChains for non-existent edge!");
}
new EdgeRuleChainsUpdater(edge).removeEntities(tenantId, edge);
}
@Override
public PageData<RuleChain> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) {
public ListenableFuture<PageData<RuleChain>> findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) {
log.trace("Executing findRuleChainsByTenantIdAndEdgeId, tenantId [{}], edgeId [{}], pageLink [{}]", tenantId, edgeId, pageLink);
Validator.validateId(tenantId, "Incorrect tenantId " + tenantId);
Validator.validateId(edgeId, "Incorrect customerId " + edgeId);
Validator.validateId(edgeId, "Incorrect edgeId " + edgeId);
Validator.validatePageLink(pageLink);
return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(tenantId.getId(), edgeId.getId(), pageLink);
}
@ -476,25 +466,59 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
public boolean setDefaultRootEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId) {
RuleChain ruleChain = ruleChainDao.findById(tenantId, ruleChainId.getId());
RuleChain previousDefaultRootEdgeRuleChain = getDefaultRootEdgeRuleChain(ruleChain.getTenantId());
if (!previousDefaultRootEdgeRuleChain.getId().equals(ruleChain.getId())) {
if (previousDefaultRootEdgeRuleChain == null || !previousDefaultRootEdgeRuleChain.getId().equals(ruleChain.getId())) {
try {
deleteRelation(tenantId, new EntityRelation(previousDefaultRootEdgeRuleChain.getTenantId(), previousDefaultRootEdgeRuleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
previousDefaultRootEdgeRuleChain.setRoot(false);
ruleChainDao.save(tenantId, previousDefaultRootEdgeRuleChain);
if (previousDefaultRootEdgeRuleChain != null) {
deleteRelation(tenantId, new EntityRelation(previousDefaultRootEdgeRuleChain.getTenantId(), previousDefaultRootEdgeRuleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
previousDefaultRootEdgeRuleChain.setRoot(false);
ruleChainDao.save(tenantId, previousDefaultRootEdgeRuleChain);
}
createRelation(tenantId, new EntityRelation(ruleChain.getTenantId(), ruleChain.getId(),
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.RULE_CHAIN));
ruleChain.setRoot(true);
ruleChainDao.save(tenantId, ruleChain);
return true;
} catch (ExecutionException | InterruptedException e) {
log.warn("[{}] Failed to set default root edge rule chain, ruleChainId: [{}]", ruleChainId);
log.warn("Failed to set default root edge rule chain, ruleChainId: [{}]", ruleChainId, e);
throw new RuntimeException(e);
}
}
return false;
}
@Override
public boolean addDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId) {
try {
createRelation(tenantId, new EntityRelation(tenantId, ruleChainId,
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE_DEFAULT_RULE_CHAIN));
return true;
} catch (ExecutionException | InterruptedException e) {
log.warn("Failed to add default edge rule chain, ruleChainId: [{}]", ruleChainId, e);
throw new RuntimeException(e);
}
}
@Override
public boolean removeDefaultEdgeRuleChain(TenantId tenantId, RuleChainId ruleChainId) {
try {
deleteRelation(tenantId, new EntityRelation(tenantId, ruleChainId,
EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE_DEFAULT_RULE_CHAIN));
return true;
} catch (ExecutionException | InterruptedException e) {
log.warn("Failed to remove default edge rule chain, ruleChainId: [{}]", ruleChainId, e);
throw new RuntimeException(e);
}
}
@Override
public ListenableFuture<List<RuleChain>> findDefaultEdgeRuleChainsByTenantId(TenantId tenantId) {
log.trace("Executing findDefaultEdgeRuleChainsByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
return ruleChainDao.findDefaultEdgeRuleChainsByTenantId(tenantId.getId());
}
private void checkRuleNodesAndDelete(TenantId tenantId, RuleChainId ruleChainId) {
List<EntityRelation> nodeRelations = getRuleChainToNodeRelations(tenantId, ruleChainId);
for (EntityRelation relation : nodeRelations) {
@ -517,24 +541,6 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
ruleNodeDao.removeById(tenantId, entityId.getId());
}
private void createRelation(TenantId tenantId, EntityRelation relation) throws ExecutionException, InterruptedException {
log.debug("Creating relation: {}", relation);
relationService.saveRelation(tenantId, relation);
}
private void deleteRelation(TenantId tenantId, EntityRelation relation) throws ExecutionException, InterruptedException {
log.debug("Deleting relation: {}", relation);
relationService.deleteRelation(tenantId, relation);
}
private RuleChain updateAssignedEdge(TenantId tenantId, RuleChainId ruleChainId, Edge edge) {
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainId);
if (ruleChain.updateAssignedEdge(edge)) {
return saveRuleChain(ruleChain);
} else {
return ruleChain;
}
}
private DataValidator<RuleChain> ruleChainValidator =
new DataValidator<RuleChain>() {
@ -544,7 +550,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
throw new DataValidationException("Rule chain name should be specified!");
}
if (ruleChain.getType() == null) {
ruleChain.setType(RuleChainType.SYSTEM);
ruleChain.setType(RuleChainType.CORE);
}
if (ruleChain.getTenantId() == null || ruleChain.getTenantId().isNullUid()) {
throw new DataValidationException("Rule chain should be assigned to tenant!");
@ -553,7 +559,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
if (tenant == null) {
throw new DataValidationException("Rule chain is referencing to non-existent tenant!");
}
if (ruleChain.isRoot() && RuleChainType.SYSTEM.equals(ruleChain.getType())) {
if (ruleChain.isRoot() && RuleChainType.CORE.equals(ruleChain.getType())) {
RuleChain rootRuleChain = getRootTenantRuleChain(ruleChain.getTenantId());
if (rootRuleChain != null && !rootRuleChain.getId().equals(ruleChain.getId())) {
throw new DataValidationException("Another root rule chain is present in scope of current tenant!");
@ -582,7 +588,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
};
private class EdgeRuleChainsUnassigner extends PaginatedRemover<Edge, RuleChain> {
private class EdgeRuleChainsUnassigner extends TimePaginatedRemover<Edge, RuleChain> {
private Edge edge;
@ -591,8 +597,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
@Override
protected PageData<RuleChain> findEntities(TenantId tenantId, Edge edge, PageLink pageLink) {
return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink);
protected PageData<RuleChain> findEntities(TenantId tenantId, Edge edge, TimePageLink pageLink) {
try {
return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink).get();
} catch (Exception e) {
log.error("[{}] Can't find rule chains by tenant id and edge id. Edge Id {}", edge.getId(), e);
throw new RuntimeException("[{}] Can't find rule chains by tenant id and edge id. Edge Id '" + edge.getId() + "'", e);
}
}
@Override
@ -601,24 +612,4 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
}
private class EdgeRuleChainsUpdater extends PaginatedRemover<Edge, RuleChain> {
private Edge edge;
EdgeRuleChainsUpdater(Edge edge) {
this.edge = edge;
}
@Override
protected PageData<RuleChain> findEntities(TenantId tenantId, Edge edge, PageLink pageLink) {
return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink);
}
@Override
protected void removeEntity(TenantId tenantId, RuleChain entity) {
updateAssignedEdge(edge.getTenantId(), new RuleChainId(entity.getUuidId()), this.edge);
}
}
}

9
dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java

@ -58,6 +58,13 @@ public interface RuleChainDao extends Dao<RuleChain> {
* @param pageLink the page link
* @return the list of rule chain objects
*/
PageData<RuleChain> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink);
ListenableFuture<PageData<RuleChain>> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink);
/**
* Find default edge rule chains by tenantId.
*
* @param tenantId the tenantId
* @return the list of rule chain objects
*/
ListenableFuture<List<RuleChain>> findDefaultEdgeRuleChainsByTenantId(UUID tenantId);
}

18
dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java

@ -17,7 +17,6 @@ package org.thingsboard.server.dao.service;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.validator.routines.EmailValidator;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -26,11 +25,13 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
public abstract class DataValidator<D extends BaseData<?>> {
private static EmailValidator emailValidator = EmailValidator.getInstance();
private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);
public void validate(D data, Function<D, TenantId> tenantIdFunction) {
try {
@ -64,11 +65,20 @@ public abstract class DataValidator<D extends BaseData<?>> {
}
protected static void validateEmail(String email) {
if (!emailValidator.isValid(email)) {
if (!doValidateEmail(email)) {
throw new DataValidationException("Invalid email address format '" + email + "'!");
}
}
private static boolean doValidateEmail(String email) {
if (email == null) {
return false;
}
Matcher emailMatcher = EMAIL_PATTERN.matcher(email);
return emailMatcher.matches();
}
protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) {
Set<String> expectedFields = new HashSet<>();
Iterator<String> fieldsIterator = expectedNode.fieldNames();

1
dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java

@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
public abstract class TimePaginatedRemover<I, D extends IdBased<?>> {

18
dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java

@ -123,22 +123,4 @@ public interface AssetRepository extends PagingAndSortingRepository<AssetEntity,
@Query("SELECT DISTINCT a.type FROM AssetEntity a WHERE a.tenantId = :tenantId")
List<String> findTenantAssetTypes(@Param("tenantId") String tenantId);
@Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " +
"AND a.edgeId = :edgeId " +
"AND LOWER(a.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
Page<AssetEntity> findByTenantIdAndEdgeId(@Param("tenantId") String tenantId,
@Param("edgeId") String edgeId,
@Param("textSearch") String textSearch,
Pageable pageable);
@Query("SELECT a FROM AssetEntity a WHERE a.tenantId = :tenantId " +
"AND a.edgeId = :edgeId AND a.type = :type " +
"AND LOWER(a.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
Page<AssetEntity> findByTenantIdAndEdgeIdAndType(@Param("tenantId") String tenantId,
@Param("edgeId") String edgeId,
@Param("type") String type,
@Param("textSearch") String textSearch,
Pageable pageable);
}

48
dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java

@ -15,22 +15,31 @@
*/
package org.thingsboard.server.dao.sql.asset;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo;
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.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.dao.DaoUtil;
import org.thingsboard.server.dao.asset.AssetDao;
import org.thingsboard.server.dao.model.sql.AssetEntity;
import org.thingsboard.server.dao.model.sql.AssetInfoEntity;
import org.thingsboard.server.dao.relation.RelationDao;
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
import org.thingsboard.server.dao.util.SqlDao;
@ -49,11 +58,15 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUIDs;
*/
@Component
@SqlDao
@Slf4j
public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> implements AssetDao {
@Autowired
private AssetRepository assetRepository;
@Autowired
private RelationDao relationDao;
@Override
protected Class<AssetEntity> getEntityClass() {
return AssetEntity.class;
@ -184,23 +197,22 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im
}
@Override
public PageData<Asset> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) {
return DaoUtil.toPageData(assetRepository
.findByTenantIdAndEdgeId(
fromTimeUUID(tenantId),
fromTimeUUID(edgeId),
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
@Override
public PageData<Asset> findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, PageLink pageLink) {
return DaoUtil.toPageData(assetRepository
.findByTenantIdAndEdgeIdAndType(
fromTimeUUID(tenantId),
fromTimeUUID(edgeId),
type,
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
public ListenableFuture<PageData<Asset>> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) {
log.debug("Try to find assets by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink);
ListenableFuture<PageData<EntityRelation>> relations =
relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.ASSET, pageLink);
return Futures.transformAsync(relations, relationsData -> {
if (relationsData != null && relationsData.getData() != null && !relationsData.getData().isEmpty()) {
List<ListenableFuture<Asset>> assetFutures = new ArrayList<>(relationsData.getData().size());
for (EntityRelation relation : relationsData.getData()) {
assetFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId()));
}
return Futures.transform(Futures.successfulAsList(assetFutures),
assets -> new PageData<>(assets, relationsData.getTotalPages(), relationsData.getTotalElements(),
relationsData.hasNext()), MoreExecutors.directExecutor());
} else {
return Futures.immediateFuture(new PageData<>());
}
}, MoreExecutors.directExecutor());
}
}

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

Loading…
Cancel
Save