diff --git a/application/src/main/data/json/demo/rule_chains/edge_root_rule_chain.json b/application/src/main/data/json/demo/rule_chains/edge_root_rule_chain.json new file mode 100644 index 0000000000..0d777f029f --- /dev/null +++ b/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 + } +} \ No newline at end of file diff --git a/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json b/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json index d67052cbc5..e3ba2e3192 100644 --- a/application/src/main/data/json/demo/rule_chains/thermostat_alarms.json +++ b/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, diff --git a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json b/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json index e7591a05aa..cec15f3643 100644 --- a/application/src/main/data/json/tenant/rule_chains/root_rule_chain.json +++ b/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, diff --git a/application/src/main/data/upgrade/2.4.x/schema_update.cql b/application/src/main/data/upgrade/2.4.x/schema_update.cql deleted file mode 100644 index 28bbeec7c0..0000000000 --- a/application/src/main/data/upgrade/2.4.x/schema_update.cql +++ /dev/null @@ -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 \ No newline at end of file diff --git a/application/src/main/data/upgrade/2.6.0/schema_update.cql b/application/src/main/data/upgrade/2.6.0/schema_update.cql new file mode 100644 index 0000000000..7e1a99f8db --- /dev/null +++ b/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 \ No newline at end of file diff --git a/application/src/main/data/upgrade/2.4.x/schema_update.sql b/application/src/main/data/upgrade/2.6.0/schema_update.sql similarity index 100% rename from application/src/main/data/upgrade/2.4.x/schema_update.sql rename to application/src/main/data/upgrade/2.6.0/schema_update.sql diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index d8a212b131..3dfe6d657e 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/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; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 79f53c7f7f..fd55098e2c 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/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(); diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java index cafcf992a8..f676de2d59 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java @@ -97,7 +97,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor 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 ruleNodeList = service.getRuleChainNodes(tenantId, entityId); log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java index 73fa49c3a8..3432950511 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java +++ b/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; } diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java index b069a5b198..7ce5fb3206 100644 --- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java +++ b/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 { diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 9192bc4440..0c77e02d3c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/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); } diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index f3a0f9a921..73d48fe093 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/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 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); } diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 87c15ae28d..1124c4b825 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/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); + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java index b7062254c3..acda94e9cc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java +++ b/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 getComponentDescriptorsByType(@PathVariable(value = "ruleChainType", required = false) String strRuleChainType, - @PathVariable("componentType") String strComponentType) throws ThingsboardException { + public List 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 getComponentDescriptorsByTypes(@PathVariable(value = "ruleChainType", required = false) String strRuleChainType, - @RequestParam("componentTypes") String[] strComponentTypes) throws ThingsboardException { + public List getComponentDescriptorsByTypes(@RequestParam("componentTypes") String[] strComponentTypes, + @RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException { checkArrayParameter("componentTypes", strComponentTypes); try { Set 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); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 1fab656a4a..fac08c0f92 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/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 edgeIds = new HashSet<>(); - if (strEdgeIds != null) { - for (String strEdgeId : strEdgeIds) { - edgeIds.add(new EdgeId(toUUID(strEdgeId))); - } - } - - Set addedEdgeIds = new HashSet<>(); - Set 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 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 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 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); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 1577831a7e..2a95d332de 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/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 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); } diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index ad258f02d4..51c14283c2 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/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); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 88aa9ce850..38060c7731 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/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); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 48526cb8ff..9861b070ca 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/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 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); } diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index d3c72c5a8f..0d4b73be61 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/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 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 edgeIds = new HashSet<>(); - if (strEdgeIds != null) { - for (String strEdgeId : strEdgeIds) { - edgeIds.add(new EdgeId(toUUID(strEdgeId))); - } - } - - Set addedEdgeIds = new HashSet<>(); - Set removedEdgeIds = new HashSet<>(); - for (EdgeId edgeId : edgeIds) { - if (!ruleChain.isAssignedToEdge(edgeId)) { - addedEdgeIds.add(edgeId); - } - } - - Set 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 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 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 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 getDefaultEdgeRuleChains() throws ThingsboardException { + try { + TenantId tenantId = getCurrentUser().getTenantId(); + return checkNotNull(ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId)).get(); + } catch (Exception e) { + throw handleException(e); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java index a5bc097b36..77e2afcd88 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java +++ b/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 components = new HashMap<>(); - private Map> systemComponentsMap = new HashMap<>(); + private Map> coreComponentsMap = new HashMap<>(); private Map> 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 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 getComponents(Set 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 { diff --git a/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java index b571651a5a..643c82f04f 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java @@ -35,5 +35,4 @@ public interface ComponentDiscoveryService { List getComponents(Set types, RuleChainType ruleChainType); Optional getComponent(String clazz); - } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java new file mode 100644 index 0000000000..a13d5ed79c --- /dev/null +++ b/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 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> 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 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 alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId); + Futures.transform(alarmFuture, alarm -> { + if (alarm != null) { + EdgeEventType edgeEventType = getEdgeQueueTypeByEntityType(alarm.getOriginator().getEntityType()); + if (edgeEventType != null) { + ListenableFuture> 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>> futures = new ArrayList<>(); + futures.add(findRelatedEdgeIdsByEntityId(tenantId, entityRelation.getTo())); + futures.add(findRelatedEdgeIdsByEntityId(tenantId, entityRelation.getFrom())); + ListenableFuture>> combinedFuture = Futures.allAsList(futures); + Futures.transform(combinedFuture, listOfListsEdgeIds -> { + Set uniqueEdgeIds = new HashSet<>(); + if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) { + for (List 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> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId) { + switch (entityId.getEntityType()) { + case DEVICE: + case ASSET: + case ENTITY_VIEW: + ListenableFuture> 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> convertToEdgeIds(ListenableFuture> 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; + } + } +} + + diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java index d7ce0b4965..32f38d2621 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java +++ b/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; } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeNotificationService.java new file mode 100644 index 0000000000..07ed8d1f8c --- /dev/null +++ b/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 findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); + + Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; + + void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback); +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index bf2c8fee2d..892523ac06 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -19,52 +19,61 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; -import com.google.protobuf.ByteString; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import io.grpc.stub.StreamObserver; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.Customer; +import org.checkerframework.checker.nullness.qual.Nullable; 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.EntityType; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.Event; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.edge.EdgeQueueEntry; +import org.thingsboard.server.common.data.edge.EdgeEvent; +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.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.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.SortOrder; 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.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.common.transport.util.JsonUtils; import org.thingsboard.server.gen.edge.AlarmUpdateMsg; import org.thingsboard.server.gen.edge.ConnectRequestMsg; import org.thingsboard.server.gen.edge.ConnectResponseCode; import org.thingsboard.server.gen.edge.ConnectResponseMsg; -import org.thingsboard.server.gen.edge.CustomerUpdateMsg; import org.thingsboard.server.gen.edge.DeviceUpdateMsg; import org.thingsboard.server.gen.edge.DownlinkMsg; import org.thingsboard.server.gen.edge.EdgeConfiguration; @@ -78,9 +87,10 @@ import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg; import org.thingsboard.server.gen.edge.UpdateMsgType; import org.thingsboard.server.gen.edge.UplinkMsg; import org.thingsboard.server.gen.edge.UplinkResponseMsg; -import org.thingsboard.server.gen.edge.UserUpdateMsg; +import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.service.edge.EdgeContextComponent; +import java.io.Closeable; import java.io.IOException; import java.util.Collections; import java.util.List; @@ -95,9 +105,11 @@ import static org.thingsboard.server.gen.edge.UpdateMsgType.ENTITY_CREATED_RPC_M @Slf4j @Data -public final class EdgeGrpcSession implements Cloneable { +public final class EdgeGrpcSession implements Closeable { - private static final ReentrantLock entityCreationLock = new ReentrantLock(); + private static final ReentrantLock deviceCreationLock = new ReentrantLock(); + + private final Gson gson = new Gson(); private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs"; @@ -136,7 +148,7 @@ public final class EdgeGrpcSession implements Cloneable { outputStream.onError(new RuntimeException(responseMsg.getErrorMsg())); } if (ConnectResponseCode.ACCEPTED == responseMsg.getResponseCode()) { - ctx.getInitEdgeService().init(edge, outputStream); + ctx.getSyncEdgeService().sync(ctx, edge, outputStream); } } if (connected) { @@ -163,40 +175,32 @@ public final class EdgeGrpcSession implements Cloneable { void processHandleMessages() throws ExecutionException, InterruptedException { Long queueStartTs = getQueueStartTs().get(); - TimePageLink pageLink = new TimePageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(), 0, "", null, queueStartTs, null); - PageData pageData; + TimePageLink pageLink = new TimePageLink(ctx.getEdgeEventStorageSettings().getMaxReadRecordsCount(), 0, "", new SortOrder("createdTime"), queueStartTs, null); + PageData pageData; UUID ifOffset = null; do { - pageData = ctx.getEdgeService().findQueueEvents(edge.getTenantId(), edge.getId(), pageLink); - if (!pageData.getData().isEmpty()) { + pageData = ctx.getEdgeNotificationService().findEdgeEvents(edge.getTenantId(), edge.getId(), pageLink); + if (isConnected() && !pageData.getData().isEmpty()) { log.trace("[{}] [{}] event(s) are going to be processed.", this.sessionId, pageData.getData().size()); - for (Event event : pageData.getData()) { - log.trace("[{}] Processing event [{}]", this.sessionId, event); + for (EdgeEvent edgeEvent : pageData.getData()) { + log.trace("[{}] Processing edge event [{}]", this.sessionId, edgeEvent); try { - EdgeQueueEntry entry = objectMapper.treeToValue(event.getBody(), EdgeQueueEntry.class); - UpdateMsgType msgType = getResponseMsgType(entry.getType()); - switch (msgType) { - case ENTITY_DELETED_RPC_MESSAGE: - case ENTITY_UPDATED_RPC_MESSAGE: - case ENTITY_CREATED_RPC_MESSAGE: - case ALARM_ACK_RPC_MESSAGE: - case ALARM_CLEAR_RPC_MESSAGE: - processEntityCRUDMessage(entry, msgType); - break; - case RULE_CHAIN_CUSTOM_MESSAGE: - processCustomDownlinkMessage(entry); - break; - } - if (ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { - pushEntityAttributesToEdge(entry); + UpdateMsgType msgType = getResponseMsgType(ActionType.valueOf(edgeEvent.getEdgeEventAction())); + if (msgType == null) { + processTelemetryMessage(edgeEvent); + } else { + processEntityCRUDMessage(edgeEvent, msgType); + if (ENTITY_CREATED_RPC_MESSAGE.equals(msgType)) { + pushEntityAttributesToEdge(edgeEvent); + } } } catch (Exception e) { log.error("Exception during processing records from queue", e); } - ifOffset = event.getUuidId(); + ifOffset = edgeEvent.getUuidId(); } } - if (pageData.hasNext()) { + if (isConnected() && pageData.hasNext()) { pageLink = pageLink.nextPageLink(); try { Thread.sleep(ctx.getEdgeEventStorageSettings().getSleepIntervalBetweenBatches()); @@ -204,7 +208,7 @@ public final class EdgeGrpcSession implements Cloneable { log.error("Error during sleep between batches", e); } } - } while (pageData.hasNext()); + } while (isConnected() && pageData.hasNext()); if (ifOffset != null) { Long newStartTs = Uuids.unixTimestamp(ifOffset); @@ -217,24 +221,42 @@ public final class EdgeGrpcSession implements Cloneable { } } - private void pushEntityAttributesToEdge(EdgeQueueEntry entry) throws IOException { + private ListenableFuture getQueueStartTs() { + ListenableFuture> future = + ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, QUEUE_START_TS_ATTR_KEY); + return Futures.transform(future, attributeKvEntryOpt -> { + if (attributeKvEntryOpt != null && attributeKvEntryOpt.isPresent()) { + AttributeKvEntry attributeKvEntry = attributeKvEntryOpt.get(); + return attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L; + } else { + return 0L; + } + }, MoreExecutors.directExecutor()); + } + + private void updateQueueStartTs(Long newStartTs) { + newStartTs = ++newStartTs; // increments ts by 1 - next edge event search starts from current offset + 1 + List attributes = Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, newStartTs), System.currentTimeMillis())); + ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes); + } + + private void pushEntityAttributesToEdge(EdgeEvent edgeEvent) throws IOException { EntityId entityId = null; - String entityName = null; - switch (entry.getEntityType()) { + switch (edgeEvent.getEdgeEventType()) { case EDGE: - entityId = objectMapper.readValue(entry.getData(), Edge.class).getId(); + entityId = edge.getId(); break; case DEVICE: - entityId = objectMapper.readValue(entry.getData(), Device.class).getId(); + entityId = new DeviceId(edgeEvent.getEntityId()); break; case ASSET: - entityId = objectMapper.readValue(entry.getData(), Asset.class).getId(); + entityId = new AssetId(edgeEvent.getEntityId()); break; case ENTITY_VIEW: - entityId = objectMapper.readValue(entry.getData(), EntityView.class).getId(); + entityId = new EntityViewId(edgeEvent.getEntityId()); break; case DASHBOARD: - entityId = objectMapper.readValue(entry.getData(), Dashboard.class).getId(); + entityId = new DashboardId(edgeEvent.getEntityId()); break; } if (entityId != null) { @@ -243,26 +265,22 @@ public final class EdgeGrpcSession implements Cloneable { Futures.transform(ssAttrFuture, ssAttributes -> { if (ssAttributes != null && !ssAttributes.isEmpty()) { try { - TbMsgMetaData metaData = new TbMsgMetaData(); ObjectNode entityNode = objectMapper.createObjectNode(); - metaData.putValue("scope", DataConstants.SERVER_SCOPE); for (AttributeKvEntry attr : ssAttributes) { - if (attr.getDataType() == DataType.BOOLEAN) { + if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) { entityNode.put(attr.getKey(), attr.getBooleanValue().get()); - } else if (attr.getDataType() == DataType.DOUBLE) { + } else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) { entityNode.put(attr.getKey(), attr.getDoubleValue().get()); - } else if (attr.getDataType() == DataType.LONG) { + } else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) { entityNode.put(attr.getKey(), attr.getLongValue().get()); } else { entityNode.put(attr.getKey(), attr.getValueAsString()); } } - TbMsg tbMsg = TbMsg.newMsg(DataConstants.ATTRIBUTES_UPDATED, finalEntityId, metaData, TbMsgDataType.JSON - , objectMapper.writeValueAsString(entityNode)); - log.debug("Sending donwlink entity data msg, entityName [{}], tbMsg [{}]", entityName, tbMsg); + log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", finalEntityId, entityNode); + DownlinkMsg value = constructEntityDataProtoMsg(finalEntityId, ActionType.ATTRIBUTES_UPDATED, JsonUtils.parse(objectMapper.writeValueAsString(entityNode))); outputStream.onNext(ResponseMsg.newBuilder() - .setDownlinkMsg(constructDownlinkEntityDataMsg(entityName, tbMsg)) - .build()); + .setDownlinkMsg(value).build()); } catch (Exception e) { log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e); } @@ -274,208 +292,392 @@ public final class EdgeGrpcSession implements Cloneable { } } - private void processCustomDownlinkMessage(EdgeQueueEntry entry) throws IOException { - log.trace("Executing processCustomDownlinkMessage, entry [{}]", entry); - TbMsg tbMsg = objectMapper.readValue(entry.getData(), TbMsg.class); - String entityName = null; - switch (entry.getEntityType()) { + private void processTelemetryMessage(EdgeEvent edgeEvent) throws IOException { + log.trace("Executing processTelemetryMessage, edgeEvent [{}]", edgeEvent); + EntityId entityId = null; + switch (edgeEvent.getEdgeEventType()) { case DEVICE: - Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), new DeviceId(tbMsg.getOriginator().getId())); - entityName = device.getName(); + entityId = new DeviceId(edgeEvent.getEntityId()); break; case ASSET: - Asset asset = ctx.getAssetService().findAssetById(edge.getTenantId(), new AssetId(tbMsg.getOriginator().getId())); - entityName = asset.getName(); + entityId = new AssetId(edgeEvent.getEntityId()); break; case ENTITY_VIEW: - EntityView entityView = ctx.getEntityViewService().findEntityViewById(edge.getTenantId(), new EntityViewId(tbMsg.getOriginator().getId())); - entityName = entityView.getName(); + entityId = new EntityViewId(edgeEvent.getEntityId()); break; } - if (entityName != null) { - log.debug("Sending donwlink entity data msg, entityName [{}], tbMsg [{}]", entityName, tbMsg); - outputStream.onNext(ResponseMsg.newBuilder() - .setDownlinkMsg(constructDownlinkEntityDataMsg(entityName, tbMsg)) - .build()); + if (entityId != null) { + log.debug("Sending telemetry data msg, entityId [{}], body [{}]", edgeEvent.getEntityId(), edgeEvent.getEntityBody()); + DownlinkMsg downlinkMsg; + try { + ActionType actionType = ActionType.valueOf(edgeEvent.getEdgeEventAction()); + downlinkMsg = constructEntityDataProtoMsg(entityId, actionType, JsonUtils.parse(objectMapper.writeValueAsString(edgeEvent.getEntityBody()))); + outputStream.onNext(ResponseMsg.newBuilder() + .setDownlinkMsg(downlinkMsg) + .build()); + } catch (Exception e) { + log.warn("Can't send telemetry data msg, entityId [{}], body [{}]", edgeEvent.getEntityId(), edgeEvent.getEntityBody(), e); + } + } } - private void processEntityCRUDMessage(EdgeQueueEntry entry, UpdateMsgType msgType) throws java.io.IOException { - log.trace("Executing processEntityCRUDMessage, entry [{}], msgType [{}]", entry, msgType); - switch (entry.getEntityType()) { + private void processEntityCRUDMessage(EdgeEvent edgeEvent, UpdateMsgType msgType) { + log.trace("Executing processEntityCRUDMessage, edgeEvent [{}], msgType [{}]", edgeEvent, msgType); + switch (edgeEvent.getEdgeEventType()) { case EDGE: - Edge edge = objectMapper.readValue(entry.getData(), Edge.class); - onEdgeUpdated(msgType, edge); + // TODO: voba - add edge update logic break; case DEVICE: - Device device = objectMapper.readValue(entry.getData(), Device.class); - onDeviceUpdated(msgType, device); + processDeviceCRUD(edgeEvent, msgType); break; case ASSET: - Asset asset = objectMapper.readValue(entry.getData(), Asset.class); - onAssetUpdated(msgType, asset); + processAssetCRUD(edgeEvent, msgType); break; case ENTITY_VIEW: - EntityView entityView = objectMapper.readValue(entry.getData(), EntityView.class); - onEntityViewUpdated(msgType, entityView); + processEntityViewCRUD(edgeEvent, msgType); break; case DASHBOARD: - Dashboard dashboard = objectMapper.readValue(entry.getData(), Dashboard.class); - onDashboardUpdated(msgType, dashboard); + processDashboardCRUD(edgeEvent, msgType); break; case RULE_CHAIN: - RuleChain ruleChain = objectMapper.readValue(entry.getData(), RuleChain.class); - onRuleChainUpdated(msgType, ruleChain); + processRuleChainCRUD(edgeEvent, msgType); break; case RULE_CHAIN_METADATA: - RuleChainMetaData ruleChainMetaData = objectMapper.readValue(entry.getData(), RuleChainMetaData.class); - onRuleChainMetadataUpdated(msgType, ruleChainMetaData); + processRuleChainMetadataCRUD(edgeEvent, msgType); break; case ALARM: - Alarm alarm = objectMapper.readValue(entry.getData(), Alarm.class); - onAlarmUpdated(msgType, alarm); + processAlarmCRUD(edgeEvent, msgType); + break; + case USER: + processUserCRUD(edgeEvent, msgType); + break; + case RELATION: + processRelationCRUD(edgeEvent, msgType); break; } } - private void updateQueueStartTs(Long newStartTs) { - newStartTs = ++newStartTs; // increments ts by 1 - next edge event search starts from current offset + 1 - List attributes = Collections.singletonList(new BaseAttributeKvEntry(new LongDataEntry(QUEUE_START_TS_ATTR_KEY, newStartTs), System.currentTimeMillis())); - ctx.getAttributesService().save(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, attributes); - } + private void processDeviceCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + DeviceId deviceId = new DeviceId(edgeEvent.getEntityId()); + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture deviceFuture = ctx.getDeviceService().findDeviceByIdAsync(edgeEvent.getTenantId(), deviceId); + Futures.addCallback(deviceFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Device device) { + if (device != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(msgType, device)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } + + @Override + public void onFailure(Throwable t) { + log.warn("Can't processDeviceCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceDeleteMsg(deviceId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + - private ListenableFuture getQueueStartTs() { - ListenableFuture> future = - ctx.getAttributesService().find(edge.getTenantId(), edge.getId(), DataConstants.SERVER_SCOPE, QUEUE_START_TS_ATTR_KEY); - return Futures.transform(future, attributeKvEntryOpt -> { - if (attributeKvEntryOpt != null && attributeKvEntryOpt.isPresent()) { - AttributeKvEntry attributeKvEntry = attributeKvEntryOpt.get(); - return attributeKvEntry.getLongValue().isPresent() ? attributeKvEntry.getLongValue().get() : 0L; - } else { - return 0L; - } - }, MoreExecutors.directExecutor()); } - private void onEdgeUpdated(UpdateMsgType msgType, Edge edge) { - // TODO: voba add configuration update to edge - this.edge = edge; + private void processAssetCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + AssetId assetId = new AssetId(edgeEvent.getEntityId()); + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture assetFuture = ctx.getAssetService().findAssetByIdAsync(edgeEvent.getTenantId(), assetId); + Futures.addCallback(assetFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Asset asset) { + if (asset != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setAssetUpdateMsg(ctx.getAssetUpdateMsgConstructor().constructAssetUpdatedMsg(msgType, asset)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } + + @Override + public void onFailure(Throwable t) { + log.warn("Can't processAssetCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setAssetUpdateMsg(ctx.getAssetUpdateMsgConstructor().constructAssetDeleteMsg(assetId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + break; + } } - private void onDeviceUpdated(UpdateMsgType msgType, Device device) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(msgType, device)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); + private void processEntityViewCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + EntityViewId entityViewId = new EntityViewId(edgeEvent.getEntityId()); + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture entityViewFuture = ctx.getEntityViewService().findEntityViewByIdAsync(edgeEvent.getTenantId(), entityViewId); + Futures.addCallback(entityViewFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable EntityView entityView) { + if (entityView != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setEntityViewUpdateMsg(ctx.getEntityViewUpdateMsgConstructor().constructEntityViewUpdatedMsg(msgType, entityView)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } + + @Override + public void onFailure(Throwable t) { + log.warn("Can't processEntityViewCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setEntityViewUpdateMsg(ctx.getEntityViewUpdateMsgConstructor().constructEntityViewDeleteMsg(entityViewId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + break; + } } - private void onAssetUpdated(UpdateMsgType msgType, Asset asset) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setAssetUpdateMsg(ctx.getAssetUpdateMsgConstructor().constructAssetUpdatedMsg(msgType, asset)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); + private void processDashboardCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + DashboardId dashboardId = new DashboardId(edgeEvent.getEntityId()); + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture dashboardFuture = ctx.getDashboardService().findDashboardByIdAsync(edgeEvent.getTenantId(), dashboardId); + Futures.addCallback(dashboardFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Dashboard dashboard) { + if (dashboard != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDashboardUpdateMsg(ctx.getDashboardUpdateMsgConstructor().constructDashboardUpdatedMsg(msgType, dashboard)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } + + @Override + public void onFailure(Throwable t) { + log.warn("Can't processDashboardCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDashboardUpdateMsg(ctx.getDashboardUpdateMsgConstructor().constructDashboardDeleteMsg(dashboardId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + break; + } } - private void onEntityViewUpdated(UpdateMsgType msgType, EntityView entityView) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setEntityViewUpdateMsg(ctx.getEntityViewUpdateMsgConstructor().constructEntityViewUpdatedMsg(msgType, entityView)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); + private void processRuleChainCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId()); + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture ruleChainFuture = ctx.getRuleChainService().findRuleChainByIdAsync(edgeEvent.getTenantId(), ruleChainId); + Futures.addCallback(ruleChainFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable RuleChain ruleChain) { + if (ruleChain != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainUpdatedMsg(edge.getRootRuleChainId(), msgType, ruleChain)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } + + @Override + public void onFailure(Throwable t) { + log.warn("Can't processRuleChainCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainDeleteMsg(ruleChainId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + break; + } } - private void onRuleChainUpdated(UpdateMsgType msgType, RuleChain ruleChain) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRuleChainUpdateMsg(ctx.getRuleChainUpdateMsgConstructor().constructRuleChainUpdatedMsg(edge, msgType, ruleChain)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); + private void processRuleChainMetadataCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + RuleChainId ruleChainId = new RuleChainId(edgeEvent.getEntityId()); + ListenableFuture ruleChainFuture = ctx.getRuleChainService().findRuleChainByIdAsync(edgeEvent.getTenantId(), ruleChainId); + Futures.addCallback(ruleChainFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable RuleChain ruleChain) { + if (ruleChain != null) { + RuleChainMetaData ruleChainMetaData = ctx.getRuleChainService().loadRuleChainMetaData(edgeEvent.getTenantId(), ruleChainId); + RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = + ctx.getRuleChainUpdateMsgConstructor().constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData); + if (ruleChainMetadataUpdateMsg != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setRuleChainMetadataUpdateMsg(ruleChainMetadataUpdateMsg) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } + } + + @Override + public void onFailure(Throwable t) { + log.warn("Can't processRuleChainMetadataCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); } - private void onRuleChainMetadataUpdated(UpdateMsgType msgType, RuleChainMetaData ruleChainMetaData) { - RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg = - ctx.getRuleChainUpdateMsgConstructor().constructRuleChainMetadataUpdatedMsg(msgType, ruleChainMetaData); - if (ruleChainMetadataUpdateMsg != null) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setRuleChainMetadataUpdateMsg(ruleChainMetadataUpdateMsg) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); + private void processUserCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + UserId userId = new UserId(edgeEvent.getEntityId()); + switch (msgType) { + case ENTITY_CREATED_RPC_MESSAGE: + case ENTITY_UPDATED_RPC_MESSAGE: + case DEVICE_CONFLICT_RPC_MESSAGE: + ListenableFuture userFuture = ctx.getUserService().findUserByIdAsync(edgeEvent.getTenantId(), userId); + Futures.addCallback(userFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable User user) { + if (user != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserUpdatedMsg(msgType, user)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } + + @Override + public void onFailure(Throwable t) { + log.warn("Can't processUserCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); + break; + case ENTITY_DELETED_RPC_MESSAGE: + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setUserUpdateMsg(ctx.getUserUpdateMsgConstructor().constructUserDeleteMsg(userId)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + break; } } - private void onDashboardUpdated(UpdateMsgType msgType, Dashboard dashboard) { + private void processRelationCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + EntityRelation entityRelation = objectMapper.convertValue(edgeEvent.getEntityBody(), EntityRelation.class); EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setDashboardUpdateMsg(ctx.getDashboardUpdateMsgConstructor().constructDashboardUpdatedMsg(msgType, dashboard)) + .setRelationUpdateMsg(ctx.getRelationUpdateMsgConstructor().constructRelationUpdatedMsg(msgType, entityRelation)) .build(); outputStream.onNext(ResponseMsg.newBuilder() .setEntityUpdateMsg(entityUpdateMsg) .build()); } - private void onAlarmUpdated(UpdateMsgType msgType, Alarm alarm) { - EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() - .setAlarmUpdateMsg(ctx.getAlarmUpdateMsgConstructor().constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm)) - .build(); - outputStream.onNext(ResponseMsg.newBuilder() - .setEntityUpdateMsg(entityUpdateMsg) - .build()); + private void processAlarmCRUD(EdgeEvent edgeEvent, UpdateMsgType msgType) { + AlarmId alarmId = new AlarmId(edgeEvent.getEntityId()); + ListenableFuture alarmFuture = ctx.getAlarmService().findAlarmByIdAsync(edgeEvent.getTenantId(), alarmId); + Futures.addCallback(alarmFuture, + new FutureCallback() { + @Override + public void onSuccess(@Nullable Alarm alarm) { + if (alarm != null) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setAlarmUpdateMsg(ctx.getAlarmUpdateMsgConstructor().constructAlarmUpdatedMsg(edge.getTenantId(), msgType, alarm)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } + + @Override + public void onFailure(Throwable t) { + log.warn("Can't processAlarmCRUD, edgeEvent [{}]", edgeEvent, t); + } + }, ctx.getDbCallbackExecutor()); } - private UpdateMsgType getResponseMsgType(String msgType) { - if (msgType.equals(SessionMsgType.POST_TELEMETRY_REQUEST.name()) || - msgType.equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name()) || - msgType.equals(DataConstants.ATTRIBUTES_UPDATED) || - msgType.equals(DataConstants.ATTRIBUTES_DELETED)) { - return UpdateMsgType.RULE_CHAIN_CUSTOM_MESSAGE; - } else { - switch (msgType) { - case DataConstants.ENTITY_UPDATED: - return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; - case DataConstants.ENTITY_CREATED: - case DataConstants.ENTITY_ASSIGNED_TO_EDGE: - return ENTITY_CREATED_RPC_MESSAGE; - case DataConstants.ENTITY_DELETED: - case DataConstants.ENTITY_UNASSIGNED_FROM_EDGE: - return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; - case DataConstants.ALARM_ACK: - return UpdateMsgType.ALARM_ACK_RPC_MESSAGE; - case DataConstants.ALARM_CLEAR: - return UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE; - default: - throw new RuntimeException("Unsupported msgType [" + msgType + "]"); - } + private UpdateMsgType getResponseMsgType(ActionType actionType) { + switch (actionType) { + case UPDATED: + return UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE; + case ADDED: + case ASSIGNED_TO_EDGE: + return ENTITY_CREATED_RPC_MESSAGE; + case DELETED: + case UNASSIGNED_FROM_EDGE: + return UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; + case ALARM_ACK: + return UpdateMsgType.ALARM_ACK_RPC_MESSAGE; + case ALARM_CLEAR: + return UpdateMsgType.ALARM_CLEAR_RPC_MESSAGE; + case ATTRIBUTES_UPDATED: + case ATTRIBUTES_DELETED: + case TIMESERIES_DELETED: + return null; + default: + throw new RuntimeException("Unsupported actionType [" + actionType + "]"); } } - private DownlinkMsg constructDownlinkEntityDataMsg(String entityName, TbMsg tbMsg) { - EntityDataProto entityData = EntityDataProto.newBuilder() - .setEntityName(entityName) - .setTbMsg(ByteString.copyFrom(TbMsg.toByteArray(tbMsg))).build(); - + private DownlinkMsg constructEntityDataProtoMsg(EntityId entityId, ActionType actionType, JsonElement entityData) { + EntityDataProto entityDataProto = ctx.getEntityDataMsgConstructor().constructEntityDataMsg(entityId, actionType, entityData); DownlinkMsg.Builder builder = DownlinkMsg.newBuilder() - .addAllEntityData(Collections.singletonList(entityData)); - - return builder.build(); - } - - private CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { - CustomerUpdateMsg.Builder builder = CustomerUpdateMsg.newBuilder() - .setMsgType(msgType); - return builder.build(); - } - - private UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user) { - UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder() - .setMsgType(msgType); + .addAllEntityData(Collections.singletonList(entityDataProto)); return builder.build(); } @@ -483,46 +685,27 @@ public final class EdgeGrpcSession implements Cloneable { try { if (uplinkMsg.getEntityDataList() != null && !uplinkMsg.getEntityDataList().isEmpty()) { for (EntityDataProto entityData : uplinkMsg.getEntityDataList()) { - TbMsg tbMsg = null; - TbMsg originalTbMsg = TbMsg.fromBytes(null, entityData.getTbMsg().toByteArray(), null); - switch (originalTbMsg.getOriginator().getEntityType()) { - case DEVICE: - String deviceName = entityData.getEntityName(); - String deviceType = entityData.getEntityType(); - Device device = getOrCreateDevice(deviceName, deviceType); - if (device != null) { - tbMsg = TbMsg.newMsg(originalTbMsg.getType(), device.getId(), originalTbMsg.getMetaData().copy(), - originalTbMsg.getDataType(), originalTbMsg.getData()); - } - break; - case ASSET: - String assetName = entityData.getEntityName(); - Asset asset = ctx.getAssetService().findAssetByTenantIdAndName(edge.getTenantId(), assetName); - if (asset != null) { - tbMsg = TbMsg.newMsg(originalTbMsg.getType(), asset.getId(), originalTbMsg.getMetaData().copy(), - originalTbMsg.getDataType(), originalTbMsg.getData()); - } - break; - case ENTITY_VIEW: - String entityViewName = entityData.getEntityName(); - EntityView entityView = ctx.getEntityViewService().findEntityViewByTenantIdAndName(edge.getTenantId(), entityViewName); - if (entityView != null) { - tbMsg = TbMsg.newMsg(originalTbMsg.getType(), entityView.getId(), originalTbMsg.getMetaData().copy(), - originalTbMsg.getDataType(), originalTbMsg.getData()); + EntityId entityId = constructEntityId(entityData); + if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg()) && entityId != null) { + ListenableFuture metaDataFuture = constructBaseMsgMetadata(entityId); + Futures.transform(metaDataFuture, metaData -> { + if (metaData != null) { + metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE); + if (entityData.hasPostAttributesMsg()) { + processPostAttributes(entityId, entityData.getPostAttributesMsg(), metaData); + } + if (entityData.hasPostTelemetryMsg()) { + processPostTelemetry(entityId, entityData.getPostTelemetryMsg(), metaData); + } } - break; + return null; + }, ctx.getDbCallbackExecutor()); } } } if (uplinkMsg.getDeviceUpdateMsgList() != null && !uplinkMsg.getDeviceUpdateMsgList().isEmpty()) { for (DeviceUpdateMsg deviceUpdateMsg : uplinkMsg.getDeviceUpdateMsgList()) { - String deviceName = deviceUpdateMsg.getName(); - String deviceType = deviceUpdateMsg.getType(); - switch (deviceUpdateMsg.getMsgType()) { - case ENTITY_CREATED_RPC_MESSAGE: - getOrCreateDevice(deviceName, deviceType); - break; - } + onDeviceUpdate(deviceUpdateMsg); } } if (uplinkMsg.getAlarmUpdateMsgList() != null && !uplinkMsg.getAlarmUpdateMsgList().isEmpty()) { @@ -532,7 +715,7 @@ public final class EdgeGrpcSession implements Cloneable { } if (uplinkMsg.getRuleChainMetadataRequestMsgList() != null && !uplinkMsg.getRuleChainMetadataRequestMsgList().isEmpty()) { for (RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg : uplinkMsg.getRuleChainMetadataRequestMsgList()) { - ctx.getInitEdgeService().initRuleChainMetadata(edge, ruleChainMetadataRequestMsg, outputStream); + ctx.getSyncEdgeService().syncRuleChainMetadata(edge, ruleChainMetadataRequestMsg, outputStream); } } } catch (Exception e) { @@ -542,6 +725,173 @@ public final class EdgeGrpcSession implements Cloneable { return UplinkResponseMsg.newBuilder().setSuccess(true).build(); } + private ListenableFuture constructBaseMsgMetadata(EntityId entityId) { + switch (entityId.getEntityType()) { + case DEVICE: + ListenableFuture deviceFuture = ctx.getDeviceService().findDeviceByIdAsync(edge.getTenantId(), new DeviceId(entityId.getId())); + return Futures.transform(deviceFuture, device -> { + TbMsgMetaData metaData = new TbMsgMetaData(); + if (device != null) { + metaData.putValue("deviceName", device.getName()); + metaData.putValue("deviceType", device.getType()); + } + return metaData; + }, ctx.getDbCallbackExecutor()); + case ASSET: + ListenableFuture assetFuture = ctx.getAssetService().findAssetByIdAsync(edge.getTenantId(), new AssetId(entityId.getId())); + return Futures.transform(assetFuture, asset -> { + TbMsgMetaData metaData = new TbMsgMetaData(); + if (asset != null) { + metaData.putValue("assetName", asset.getName()); + metaData.putValue("assetType", asset.getType()); + } + return metaData; + }, ctx.getDbCallbackExecutor()); + case ENTITY_VIEW: + ListenableFuture entityViewFuture = ctx.getEntityViewService().findEntityViewByIdAsync(edge.getTenantId(), new EntityViewId(entityId.getId())); + return Futures.transform(entityViewFuture, entityView -> { + TbMsgMetaData metaData = new TbMsgMetaData(); + if (entityView != null) { + metaData.putValue("entityViewName", entityView.getName()); + metaData.putValue("entityViewType", entityView.getType()); + } + return metaData; + }, ctx.getDbCallbackExecutor()); + default: + log.debug("Constructing empty metadata for entityId [{}]", entityId); + return Futures.immediateFuture(new TbMsgMetaData()); + } + } + + private EntityId constructEntityId(EntityDataProto entityData) { + EntityType entityType = EntityType.valueOf(entityData.getEntityType()); + switch (entityType) { + case DEVICE: + return new DeviceId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + case ASSET: + return new AssetId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + case ENTITY_VIEW: + return new EntityViewId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + case DASHBOARD: + return new DashboardId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); + default: + log.warn("Unsupported entity type [{}] during construct of entity id. EntityDataProto [{}]", entityData.getEntityType(), entityData); + return null; + } + } + + private void processPostTelemetry(EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) { + for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) { + JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); + metaData.putValue("ts", tsKv.getTs() + ""); + TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, metaData, gson.toJson(json)); + // TODO: voba - verify that null callback is OK + ctx.getTbClusterService().pushMsgToRuleEngine(edge.getTenantId(), tbMsg.getOriginator(), tbMsg, null); + } + } + + private void processPostAttributes(EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { + JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); + TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json)); + // TODO: voba - verify that null callback is OK + ctx.getTbClusterService().pushMsgToRuleEngine(edge.getTenantId(), tbMsg.getOriginator(), tbMsg, null); + } + + private void onDeviceUpdate(DeviceUpdateMsg deviceUpdateMsg) { + log.info("onDeviceUpdate {}", deviceUpdateMsg); + DeviceId edgeDeviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); + switch (deviceUpdateMsg.getMsgType()) { + case ENTITY_CREATED_RPC_MESSAGE: + String deviceName = deviceUpdateMsg.getName(); + Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); + if (device != null) { + // device with this name already exists on the cloud - update ID on the edge + if (!device.getId().equals(edgeDeviceId)) { + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE, device)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } + } else { + Device deviceById = ctx.getDeviceService().findDeviceById(edge.getTenantId(), edgeDeviceId); + if (deviceById != null) { + // this ID already used by other device - create new device and update ID on the edge + Device savedDevice = createDevice(deviceUpdateMsg); + EntityUpdateMsg entityUpdateMsg = EntityUpdateMsg.newBuilder() + .setDeviceUpdateMsg(ctx.getDeviceUpdateMsgConstructor().constructDeviceUpdatedMsg(UpdateMsgType.DEVICE_CONFLICT_RPC_MESSAGE, savedDevice)) + .build(); + outputStream.onNext(ResponseMsg.newBuilder() + .setEntityUpdateMsg(entityUpdateMsg) + .build()); + } else { + createDevice(deviceUpdateMsg); + } + } + break; + case ENTITY_UPDATED_RPC_MESSAGE: + updateDevice(deviceUpdateMsg); + break; + case ENTITY_DELETED_RPC_MESSAGE: + Device deviceToDelete = ctx.getDeviceService().findDeviceById(edge.getTenantId(), edgeDeviceId); + if (deviceToDelete != null) { + ctx.getDeviceService().unassignDeviceFromEdge(edge.getTenantId(), edgeDeviceId, edge.getId()); + } + break; + case UNRECOGNIZED: + log.error("Unsupported msg type"); + } + } + + private void updateDevice(DeviceUpdateMsg deviceUpdateMsg) { + DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); + Device device = ctx.getDeviceService().findDeviceById(edge.getTenantId(), deviceId); + device.setName(deviceUpdateMsg.getName()); + device.setType(deviceUpdateMsg.getType()); + device.setLabel(deviceUpdateMsg.getLabel()); + device = ctx.getDeviceService().saveDevice(device); + updateDeviceCredentials(deviceUpdateMsg, device); + } + + private void updateDeviceCredentials(DeviceUpdateMsg deviceUpdateMsg, Device device) { + log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", + device.getName(), deviceUpdateMsg.getCredentialsId(), deviceUpdateMsg.getCredentialsValue()); + + DeviceCredentials deviceCredentials = ctx.getDeviceCredentialsService().findDeviceCredentialsByDeviceId(edge.getTenantId(), device.getId()); + deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceUpdateMsg.getCredentialsType())); + deviceCredentials.setCredentialsId(deviceUpdateMsg.getCredentialsId()); + deviceCredentials.setCredentialsValue(deviceUpdateMsg.getCredentialsValue()); + ctx.getDeviceCredentialsService().updateDeviceCredentials(edge.getTenantId(), deviceCredentials); + log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", + device.getName(), deviceUpdateMsg.getCredentialsId(), deviceUpdateMsg.getCredentialsValue()); + + } + + private Device createDevice(DeviceUpdateMsg deviceUpdateMsg) { + Device device; + try { + deviceCreationLock.lock(); + DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); + device = new Device(); + device.setTenantId(edge.getTenantId()); + device.setCustomerId(edge.getCustomerId()); + device.setId(deviceId); + device.setName(deviceUpdateMsg.getName()); + device.setType(deviceUpdateMsg.getType()); + device.setLabel(deviceUpdateMsg.getLabel()); + device = ctx.getDeviceService().saveDevice(device); + device = ctx.getDeviceService().assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId()); + createRelationFromEdge(device.getId()); + ctx.getRelationService().saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(edge.getId(), device.getId(), "Created")); + ctx.getDeviceStateService().onDeviceAdded(device); + updateDeviceCredentials(deviceUpdateMsg, device); + } finally { + deviceCreationLock.unlock(); + } + return device; + } + private EntityId getAlarmOriginator(String entityName, org.thingsboard.server.common.data.EntityType entityType) { switch (entityType) { case DEVICE: @@ -601,62 +951,6 @@ public final class EdgeGrpcSession implements Cloneable { } } - private Device getOrCreateDevice(String deviceName, String deviceType) { - Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); - if (device == null) { - entityCreationLock.lock(); - try { - return processGetOrCreateDevice(deviceName, deviceType); - } finally { - entityCreationLock.unlock(); - } - } - return device; - } - - private Device processGetOrCreateDevice(String deviceName, String deviceType) { - Device device = ctx.getDeviceService().findDeviceByTenantIdAndName(edge.getTenantId(), deviceName); - if (device == null) { - device = new Device(); - device.setName(deviceName); - device.setType(deviceType); - device.setTenantId(edge.getTenantId()); - device.setCustomerId(edge.getCustomerId()); - device = ctx.getDeviceService().saveDevice(device); - device = ctx.getDeviceService().assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId()); - createRelationFromEdge(device.getId()); -// ctx.getActorService().onDeviceAdded(device); - pushDeviceCreatedEventToRuleEngine(device); - } - return device; - } - - private void pushDeviceCreatedEventToRuleEngine(Device device) { - try { - ObjectNode entityNode = objectMapper.valueToTree(device); - TbMsg msg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, device.getId(), deviceActionTbMsgMetaData(device), objectMapper.writeValueAsString(entityNode)); -// ctx.getActorService().onMsg(new SendToClusterMsg(device.getId(), new ServiceToRuleEngineMsg(edge.getTenantId(), msg))); - } catch (JsonProcessingException | IllegalArgumentException e) { - log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); - } - } - - private TbMsgMetaData deviceActionTbMsgMetaData(Device device) { - TbMsgMetaData metaData = getTbMsgMetaData(); - CustomerId customerId = device.getCustomerId(); - if (customerId != null && !customerId.isNullUid()) { - metaData.putValue("customerId", customerId.toString()); - } - return metaData; - } - - private TbMsgMetaData getTbMsgMetaData() { - TbMsgMetaData metaData = new TbMsgMetaData(); - metaData.putValue("edgeId", edge.getId().toString()); - metaData.putValue("edgeName", edge.getName()); - return metaData; - } - private ConnectResponseMsg processConnect(ConnectRequestMsg request) { Optional optional = ctx.getEdgeService().findEdgeByRoutingKey(TenantId.SYS_TENANT_ID, request.getEdgeRoutingKey()); if (optional.isPresent()) { @@ -703,7 +997,17 @@ public final class EdgeGrpcSession implements Cloneable { .setTenantIdLSB(edge.getTenantId().getId().getLeastSignificantBits()) .setName(edge.getName()) .setRoutingKey(edge.getRoutingKey()) - .setType(edge.getType().toString()) + .setType(edge.getType()) .build(); } + + @Override + public void close() { + connected = false; + try { + outputStream.onCompleted(); + } catch (Exception e) { + log.debug("[{}] Failed to close output stream: {}", sessionId, e.getMessage()); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java index 3d59cd3d44..c404fe5c41 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/AssetUpdateMsgConstructor.java +++ b/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(); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java index 3826cc25f0..6bd24f123b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DashboardUpdateMsgConstructor.java +++ b/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(); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java index ff4835d609..8d7fbc4c25 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/DeviceUpdateMsgConstructor.java +++ b/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(); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityDataMsgConstructor.java new file mode 100644 index 0000000000..d9a06655c5 --- /dev/null +++ b/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(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java index ef5445ac81..75301c7ed9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/EntityViewUpdateMsgConstructor.java +++ b/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(); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RelationUpdateMsgConstructor.java new file mode 100644 index 0000000000..e09b9f2e8b --- /dev/null +++ b/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(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java index 92828c9e7e..52e487d973 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/RuleChainUpdateMsgConstructor.java +++ b/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(); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/UserUpdateMsgConstructor.java new file mode 100644 index 0000000000..b994d8fdc4 --- /dev/null +++ b/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(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java deleted file mode 100644 index e324c085af..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultInitEdgeService.java +++ /dev/null @@ -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 outputStream) { - initRuleChains(edge, outputStream); - initDevices(edge, outputStream); - initAssets(edge, outputStream); - initEntityViews(edge, outputStream); - initDashboards(edge, outputStream); - } - - private void initDevices(Edge edge, StreamObserver outputStream) { - try { - PageLink pageLink = new PageLink(100); - PageData 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 outputStream) { - try { - PageLink pageLink = new PageLink(100); - PageData 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 outputStream) { - try { - PageLink pageLink = new PageLink(100); - PageData 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 outputStream) { - try { - TimePageLink pageLink = new TimePageLink(100); - PageData 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 outputStream) { - try { - TimePageLink pageLink = new TimePageLink(100); - PageData 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 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()); - } - } - } -} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/DefaultSyncEdgeService.java new file mode 100644 index 0000000000..4679153d0d --- /dev/null +++ b/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 outputStream) { + Set pushedEntityIds = new HashSet<>(); + syncUsers(ctx, edge, pushedEntityIds, outputStream); + List> 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> joinFuture = Futures.allAsList(futures); + Futures.transform(joinFuture, result -> { + syncRelations(ctx, edge, pushedEntityIds, outputStream); + return null; + }, MoreExecutors.directExecutor()); + } + + private ListenableFuture syncRuleChains(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + try { + ListenableFuture> 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 syncDevices(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + try { + ListenableFuture> 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 syncAssets(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + try { + ListenableFuture> 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 syncEntityViews(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + try { + ListenableFuture> 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 syncDashboards(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + try { + ListenableFuture> 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 pushedEntityIds, StreamObserver outputStream) { + try { + PageData 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 pageData, Edge edge, Set pushedEntityIds, StreamObserver 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 syncRelations(EdgeContextComponent ctx, Edge edge, Set pushedEntityIds, StreamObserver outputStream) { + if (!pushedEntityIds.isEmpty()) { + List>> futures = new ArrayList<>(); + for (EntityId entityId : pushedEntityIds) { + futures.add(syncRelations(edge, entityId, EntitySearchDirection.FROM)); + futures.add(syncRelations(edge, entityId, EntitySearchDirection.TO)); + } + ListenableFuture>> relationsListFuture = Futures.allAsList(futures); + return Futures.transform(relationsListFuture, relationsList -> { + try { + Set uniqueEntityRelations = new HashSet<>(); + if (!relationsList.isEmpty()) { + for (List 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> 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 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()); + } + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/InitEdgeService.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java similarity index 78% rename from application/src/main/java/org/thingsboard/server/service/edge/rpc/init/InitEdgeService.java rename to application/src/main/java/org/thingsboard/server/service/edge/rpc/init/SyncEdgeService.java index 8aeb89bf23..84b4d9ffff 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/init/InitEdgeService.java +++ b/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 outputStream); + void sync(EdgeContextComponent ctx, Edge edge, StreamObserver outputStream); - void initRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver outputStream); + void syncRuleChainMetadata(Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg, StreamObserver outputStream); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 5926253555..ea36a61b48 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/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 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); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index 89775f413c..cccb7c17ef 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/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."); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index ff88f012ee..16125133f8 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/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 actorMsg = encodingService.decode(toCoreMsg.getToDeviceActorNotificationMsg().toByteArray()); if (actorMsg.isPresent()) { @@ -274,6 +281,13 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService descriptors = readResponse( - doGet("/api/components/SYSTEM?componentTypes={componentTypes}", ComponentType.FILTER).andExpect(status().isOk()), new TypeReference>() { + doGet("/api/components?componentTypes={componentTypes}&ruleChainType={ruleChainType}", ComponentType.FILTER, RuleChainType.CORE).andExpect(status().isOk()), new TypeReference>() { }); 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()); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java index a0d3428a77..b9537775ed 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java +++ b/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 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 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 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 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 loadedEdges = new ArrayList<>(); @@ -284,23 +263,19 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest { String title1 = "Edge title 1"; List 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 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 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 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 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 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 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 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 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; + } } \ No newline at end of file diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java index 71b03860c2..9c3615cbdc 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java +++ b/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 findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); - - PageData findAssetsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink); + ListenableFuture> findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java index 8f3ade6c9c..51ac8cac0f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java +++ b/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 findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); + ListenableFuture> findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index 525127257f..820734cffe 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/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 findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); - - PageData findDevicesByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink); + ListenableFuture> findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeEventService.java new file mode 100644 index 0000000000..32720491c3 --- /dev/null +++ b/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 saveAsync(EdgeEvent edgeEvent); + + PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java index 0da49698c6..7e85626a74 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/edge/EdgeService.java +++ b/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> findEdgeTypesByTenantId(TenantId tenantId); - void pushEventToEdge(TenantId tenantId, TbMsg tbMsg, FutureCallback callback); + void assignDefaultRuleChainsToEdge(TenantId tenantId, EdgeId edgeId); - PageData findQueueEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); + ListenableFuture> findEdgesByTenantIdAndRuleChainId(TenantId tenantId, RuleChainId ruleChainId); - Edge setRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; + ListenableFuture> findEdgesByTenantIdAndDashboardId(TenantId tenantId, DashboardId dashboardId); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java index 4da40f9cf6..e780dde977 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/entityview/EntityViewService.java +++ b/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 findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); - - PageData findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink); + ListenableFuture> findEntityViewsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java index d80ca1d0df..de4af7f9b2 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java +++ b/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 findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); + ListenableFuture> 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> findDefaultEdgeRuleChainsByTenantId(TenantId tenantId); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index 817c9d1857..97889a488e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -32,9 +32,6 @@ public class DashboardInfo extends SearchTextBased implements HasNa private String title; private Set assignedCustomers; - @Getter @Setter - private Set assignedEdges; - public DashboardInfo() { super(); } @@ -123,55 +120,6 @@ public class DashboardInfo extends SearchTextBased 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() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index 4dd9bb1076..ccb3b8765f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/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"; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 201bbc8b07..cd617ec345 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/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 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 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 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 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="); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java b/common/data/src/main/java/org/thingsboard/server/common/data/EdgeUtils.java new file mode 100644 index 0000000000..31449a7801 --- /dev/null +++ b/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 assignedEdges, EdgeId edgeId) { + return assignedEdges != null && assignedEdges.contains(new ShortEdgeInfo(edgeId, null, null)); + } + + public static ShortEdgeInfo getAssignedEdgeInfo(Set assignedEdges, EdgeId edgeId) { + if (assignedEdges != null) { + for (ShortEdgeInfo edgeInfo : assignedEdges) { + if (edgeInfo.getEdgeId().equals(edgeId)) { + return edgeInfo; + } + } + } + return null; + } + + public static boolean addAssignedEdge(Set 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 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 assignedEdges, ShortEdgeInfo edgeInfo) { + if (assignedEdges != null && assignedEdges.contains(edgeInfo)) { + assignedEdges.remove(edgeInfo); + return true; + } else { + return false; + } + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java index 6a19368aa4..0bf080899e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java +++ b/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 private EntityId entityId; private TenantId tenantId; private CustomerId customerId; - private EdgeId edgeId; private String name; private String type; private TelemetryEntityView keys; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index 74f8c52584..e0f587dda4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/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 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 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 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 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="); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java index 7e78a03bb4..48de5fedb6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java +++ b/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 diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEvent.java new file mode 100644 index 0000000000..1c9ea9343c --- /dev/null +++ b/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 { + + 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); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java similarity index 90% rename from common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java rename to common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java index accae613c1..ada50c9998 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntityType.java +++ b/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 } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EdgeEventId.java similarity index 55% rename from common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntry.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/EdgeEventId.java index 3299ab07be..705c46e2fa 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeQueueEntry.java +++ b/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)); + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index 583b3b8e67..17d86e26ec 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/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!"); + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java b/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java index 611c4777a3..07315428e3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationTypeGroup.java +++ b/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 } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index d4bb5f4c20..50cd23ac3f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/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 im private boolean root; private boolean debugMode; private transient JsonNode configuration; - private Set assignedEdges; + @JsonIgnore private byte[] configurationBytes; - public RuleChain() { super(); } @@ -67,7 +60,6 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo 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 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; - } - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java index d6a806f95f..9dc3dc4e5e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainType.java +++ b/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 } diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index 8421c0ec92..1171de7372 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -40,6 +40,10 @@ org.thingsboard.common data + + org.thingsboard.common + queue + org.thingsboard.common message diff --git a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java b/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java index 5ee2f3341f..38b377c4a8 100644 --- a/common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java +++ b/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()); } } diff --git a/common/edge-api/src/main/proto/edge.proto b/common/edge-api/src/main/proto/edge.proto index dbc30d1a06..38cbe7092c 100644 --- a/common/edge-api/src/main/proto/edge.proto +++ b/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; } /** diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index f02ae63441..ae7760638f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -91,8 +91,6 @@ public class DefaultTbQueueRequestTemplate 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); diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index d3e850b39c..921a096df0 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/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 */ diff --git a/dao/pom.xml b/dao/pom.xml index fc991101ab..5a90f4d676 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -91,26 +91,26 @@ mockito-all test - - org.apache.commons - commons-lang3 - - - commons-validator - commons-validator - - - com.fasterxml.jackson.core - jackson-databind - + + org.apache.commons + commons-lang3 + + + commons-collections + commons-collections + + + com.fasterxml.jackson.core + jackson-databind + org.springframework spring-context - - org.springframework - spring-tx - + + org.springframework + spring-tx + org.springframework spring-web @@ -136,8 +136,8 @@ io.takari.junit takari-cpsuite test - - + + com.google.guava guava @@ -219,18 +219,18 @@ - - org.apache.maven.plugins - maven-jar-plugin + + org.apache.maven.plugins + maven-jar-plugin ${jar-plugin.version} - - - - test-jar - - - - + + + + test-jar + + + + diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 49926ad5c1..bc25960d6b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/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); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java index 27cc7fab41..c6adfa64bd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java +++ b/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 { ListenableFuture> 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 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 findAssetsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, PageLink pageLink); + ListenableFuture> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index aefea88130..bbba320b1b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/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 findAssetsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { + public ListenableFuture> 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 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 assetValidator = new DataValidator() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java index 632c8a2d2d..5d2e4d53d9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java @@ -57,6 +57,6 @@ public interface DashboardInfoDao extends Dao { * @param pageLink the page link * @return the list of dashboard objects */ - PageData findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink); + ListenableFuture> findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index 51ff47b93f..faca4ff172 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/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 findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { + public ListenableFuture> 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 dashboardValidator = new DataValidator() { @Override @@ -334,9 +296,9 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb deleteDashboard(tenantId, new DashboardId(entity.getUuidId())); } }; - + private class CustomerDashboardsUnassigner extends PaginatedRemover { - + private Customer customer; CustomerDashboardsUnassigner(Customer customer) { @@ -385,32 +347,18 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb @Override protected PageData 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 { - - private Edge edge; - - EdgeDashboardsUpdater(Edge edge) { - this.edge = edge; - } - - @Override - protected PageData 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); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index 90a44b2438..783bddc762 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/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 { * @param pageLink the page link * @return the list of device objects */ - PageData 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 findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, PageLink pageLink); + ListenableFuture> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index ae97532e45..530ac59248 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/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 findDevicesByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { + public ListenableFuture> 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 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 deviceValidator = new DataValidator() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeEventService.java new file mode 100644 index 0000000000..eeeb3a3fe9 --- /dev/null +++ b/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 saveAsync(EdgeEvent edgeEvent) { + edgeEventValidator.validate(edgeEvent, EdgeEvent::getTenantId); + return edgeEventDao.saveAsync(edgeEvent); + } + + @Override + public PageData findEdgeEvents(TenantId tenantId, EdgeId edgeId, TimePageLink pageLink) { + return edgeEventDao.findEdgeEvents(tenantId.getId(), edgeId, pageLink); + } + + private DataValidator edgeEventValidator = + new DataValidator() { + @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!"); + } + } + }; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java b/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java deleted file mode 100644 index d6f62488cf..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/BaseEdgeService.java +++ /dev/null @@ -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 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 edgeOpt = edgeDao.findEdgeByTenantIdAndName(tenantId.getId(), name); - return edgeOpt.orElse(null); - } - - @Override - public Optional 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 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 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 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 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 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> findEdgesByTenantIdAndIdsAsync(TenantId tenantId, List 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 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 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> findEdgesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List 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> findEdgesByQuery(TenantId tenantId, EdgeSearchQuery query) { - ListenableFuture> relations = relationService.findByQuery(tenantId, query.toEntitySearchQuery()); - ListenableFuture> edges = Futures.transformAsync(relations, r -> { - EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection(); - List> 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>() { - @Nullable - @Override - public List apply(@Nullable List 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> findEdgeTypesByTenantId(TenantId tenantId) { - log.trace("Executing findEdgeTypesByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - ListenableFuture> 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 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 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 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 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 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 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 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 callback) throws IOException { - processAssignedEntity(tenantId, tbMsg, EdgeQueueEntityType.DASHBOARD, callback); - } - - private void processRuleChain(TenantId tenantId, TbMsg tbMsg, FutureCallback 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 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 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 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 saveFuture = eventService.saveAsync(event); - - addMainCallback(saveFuture, callback); - } - - private void addMainCallback(ListenableFuture saveFuture, final FutureCallback callback) { - Futures.addCallback(saveFuture, new FutureCallback() { - @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 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 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() { - @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 edgeValidator = - new DataValidator() { - - @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 tenantEdgesRemover = - new PaginatedRemover() { - - @Override - protected PageData 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 customerEdgeUnasigner = new PaginatedRemover() { - - @Override - protected PageData 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())); - } - }; - -} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java index 9f94ce212e..737707ad7f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeDao.java @@ -130,4 +130,21 @@ public interface EdgeDao extends Dao { PageData 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> 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> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId); } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeEventDao.java new file mode 100644 index 0000000000..002fd2d8ca --- /dev/null +++ b/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 { + + /** + * Save or update edge event object async + * + * @param edgeEvent the event object + * @return saved edge event object future + */ + ListenableFuture 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 findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/edge/EdgeServiceImpl.java new file mode 100644 index 0000000000..d63e5295b2 --- /dev/null +++ b/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 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 edgeOpt = edgeDao.findEdgeByTenantIdAndName(tenantId.getId(), name); + return edgeOpt.orElse(null); + } + + @Override + public Optional 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 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 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 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 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 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> findEdgesByTenantIdAndIdsAsync(TenantId tenantId, List 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 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 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> findEdgesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List 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> findEdgesByQuery(TenantId tenantId, EdgeSearchQuery query) { + ListenableFuture> relations = relationService.findByQuery(tenantId, query.toEntitySearchQuery()); + ListenableFuture> edges = Futures.transformAsync(relations, r -> { + EntitySearchDirection direction = query.toEntitySearchQuery().getParameters().getDirection(); + List> 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>() { + @Nullable + @Override + public List apply(@Nullable List 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> findEdgeTypesByTenantId(TenantId tenantId) { + log.trace("Executing findEdgeTypesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + ListenableFuture> 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> 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> 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> 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 edgeValidator = + new DataValidator() { + + @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 tenantEdgesRemover = + new PaginatedRemover() { + + @Override + protected PageData 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 customerEdgeUnassigner = new PaginatedRemover() { + + @Override + protected PageData 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())); + } + }; + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java index a0f03b2a32..1b0ebd7979 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java +++ b/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); diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java index c12a0d37b3..7a3bc88f4f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewDao.java +++ b/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 { * @param pageLink the page link * @return the list of entity view objects */ - PageData 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 findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId, - UUID edgeId, - String type, - PageLink pageLink); + ListenableFuture> findEntityViewsByTenantIdAndEdgeId(UUID tenantId, + UUID edgeId, + TimePageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 53d4e77a75..7ee8299a7c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/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 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 findEntityViewsByTenantIdAndEdgeIdAndType(TenantId tenantId, EdgeId edgeId, String type, PageLink pageLink) { - log.trace("Executing findEntityViewsByTenantIdAndEdgeIdAndType, tenantId [{}], edgeId [{}]," + - " pageLink [{}], type [{}]", tenantId, edgeId, pageLink, type); + public ListenableFuture> 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 entityViewValidator = diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index abb7b8022b..3febd2686c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/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. */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java index d4188b69f7..367864db6b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java +++ b/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 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 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 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); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java index 0e5ad8a236..6b4d8d8ba5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java +++ b/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 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 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 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); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java index f24597a98b..34bc9797e3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractEntityViewEntity.java @@ -66,9 +66,6 @@ public abstract class AbstractEntityViewEntity 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 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 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 { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java index 07498507d9..622f03cbad 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java +++ b/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 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 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 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 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; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java index c529c9103a..7e5aabd328 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardInfoEntity.java +++ b/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 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 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 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 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; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EdgeEventEntity.java new file mode 100644 index 0000000000..e257ec2fc9 --- /dev/null +++ b/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 implements BaseEntity { + + @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; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 6cd3166814..2a98a83180 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/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 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 findRuleChainsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink) { + public ListenableFuture> 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> 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 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 ruleChainValidator = new DataValidator() { @@ -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 { + private class EdgeRuleChainsUnassigner extends TimePaginatedRemover { private Edge edge; @@ -591,8 +597,13 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC } @Override - protected PageData findEntities(TenantId tenantId, Edge edge, PageLink pageLink) { - return ruleChainDao.findRuleChainsByTenantIdAndEdgeId(edge.getTenantId().getId(), edge.getId().getId(), pageLink); + protected PageData 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 { - - private Edge edge; - - EdgeRuleChainsUpdater(Edge edge) { - this.edge = edge; - } - - @Override - protected PageData 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); - } - - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index 69e1737a45..1e3874a797 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -58,6 +58,13 @@ public interface RuleChainDao extends Dao { * @param pageLink the page link * @return the list of rule chain objects */ - PageData findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink); + ListenableFuture> 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> findDefaultEdgeRuleChainsByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index 05969874ed..7f04ddf2aa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/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> { - - 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 tenantIdFunction) { try { @@ -64,11 +65,20 @@ public abstract class DataValidator> { } 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 expectedFields = new HashSet<>(); Iterator fieldsIterator = expectedNode.fieldNames(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java b/dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java index e2ea5be619..cd3561d3ca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/TimePaginatedRemover.java +++ b/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> { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java index 04e746738b..ed0507238f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java @@ -123,22 +123,4 @@ public interface AssetRepository extends PagingAndSortingRepository 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 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 findByTenantIdAndEdgeIdAndType(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("type") String type, - @Param("textSearch") String textSearch, - Pageable pageable); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index 6c44dd392d..bee40a3c64 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/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 implements AssetDao { @Autowired private AssetRepository assetRepository; + @Autowired + private RelationDao relationDao; + @Override protected Class getEntityClass() { return AssetEntity.class; @@ -184,23 +197,22 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im } @Override - public PageData 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 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> findAssetsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find assets by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> 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> 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()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java index 52c3e0ed0a..32afcfd3a7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardInfoRepository.java @@ -47,12 +47,4 @@ public interface DashboardInfoRepository extends PagingAndSortingRepository findByTenantIdAndEdgeId(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("searchText") String searchText, - Pageable pageable); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java index 7219d6b9e5..2e07e97107 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardInfoDao.java @@ -15,20 +15,33 @@ */ package org.thingsboard.server.dao.sql.dashboard; +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.repository.CrudRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DashboardInfo; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.UUIDConverter; +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.dashboard.DashboardInfoDao; import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.UUID; @@ -43,6 +56,9 @@ public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) { + public ListenableFuture> findDashboardsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { log.debug("Try to find dashboards by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); - return DaoUtil.toPageData(dashboardInfoRepository - .findByTenantIdAndEdgeId( - UUIDConverter.fromTimeUUID(tenantId), - UUIDConverter.fromTimeUUID(edgeId), - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); + + ListenableFuture> relations = + relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DASHBOARD, pageLink); + + return Futures.transformAsync(relations, relationsData -> { + if (relationsData != null && relationsData.getData() != null && relationsData.getData().isEmpty()) { + List> dashboardFutures = new ArrayList<>(relationsData.getData().size()); + for (EntityRelation relation : relationsData.getData()) { + dashboardFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.transform(Futures.successfulAsList(dashboardFutures), + dashboards -> new PageData<>(dashboards, relationsData.getTotalPages(), relationsData.getTotalElements(), + relationsData.hasNext()), MoreExecutors.directExecutor()); + } else { + return Futures.immediateFuture(new PageData<>()); + } + }, MoreExecutors.directExecutor()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java index 3ef2a30dcf..084eb20731 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java @@ -126,22 +126,4 @@ public interface DeviceRepository extends PagingAndSortingRepository findDevicesByTenantIdAndCustomerIdAndIdIn(String tenantId, String customerId, List deviceIds); List findDevicesByTenantIdAndIdIn(String tenantId, List deviceIds); - - @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + - "AND d.edgeId = :edgeId " + - "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") - Page findByTenantIdAndEdgeId(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("searchText") String searchText, - Pageable pageable); - - @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + - "AND d.edgeId = :edgeId " + - "AND d.type = :type " + - "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") - Page findByTenantIdAndEdgeIdAndType(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("type") String type, - @Param("textSearch") String textSearch, - Pageable pageable); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index b501caa0a7..448fab993e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -15,7 +15,10 @@ */ package org.thingsboard.server.dao.sql.device; +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.repository.CrudRepository; import org.springframework.stereotype.Component; @@ -25,13 +28,19 @@ import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.common.data.asset.Asset; +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.device.DeviceDao; import org.thingsboard.server.dao.model.sql.DeviceEntity; import org.thingsboard.server.dao.model.sql.DeviceInfoEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; @@ -50,11 +59,15 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUIDs; */ @Component @SqlDao +@Slf4j public class JpaDeviceDao extends JpaAbstractSearchTextDao implements DeviceDao { @Autowired private DeviceRepository deviceRepository; + @Autowired + private RelationDao relationDao; + @Override protected Class getEntityClass() { return DeviceEntity.class; @@ -191,23 +204,22 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao } @Override - public PageData findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) { - return DaoUtil.toPageData( - deviceRepository.findByTenantIdAndEdgeId( - fromTimeUUID(tenantId), - fromTimeUUID(edgeId), - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); - } - - @Override - public PageData findDevicesByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, PageLink pageLink) { - return DaoUtil.toPageData( - deviceRepository.findByTenantIdAndEdgeIdAndType( - fromTimeUUID(tenantId), - fromTimeUUID(edgeId), - type, - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); + public ListenableFuture> findDevicesByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find devices by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> relations = + relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.DEVICE, pageLink); + return Futures.transformAsync(relations, relationsData -> { + if (relationsData != null && relationsData.getData() != null && !relationsData.getData().isEmpty()) { + List> deviceFutures = new ArrayList<>(relationsData.getData().size()); + for (EntityRelation relation : relationsData.getData()) { + deviceFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.transform(Futures.successfulAsList(deviceFutures), + devices -> new PageData<>(devices, relationsData.getTotalPages(), relationsData.getTotalElements(), + relationsData.hasNext()), MoreExecutors.directExecutor()); + } else { + return Futures.immediateFuture(new PageData<>()); + } + }, MoreExecutors.directExecutor()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java new file mode 100644 index 0000000000..65da23db11 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/EdgeEventRepository.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2020 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql.edge; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.thingsboard.server.dao.model.sql.EdgeEventEntity; +import org.thingsboard.server.dao.util.SqlDao; + +@SqlDao +public interface EdgeEventRepository extends CrudRepository, JpaSpecificationExecutor { + + @Query("SELECT e FROM EdgeEventEntity e WHERE " + + "e.tenantId = :tenantId " + + "AND e.edgeId = :edgeId " + + "AND (:startId IS NULL OR e.id >= :startId) " + + "AND (:endId IS NULL OR e.id <= :endId)" + ) + Page findEdgeEventsByTenantIdAndEdgeId(@Param("tenantId") String tenantId, + @Param("edgeId") String edgeId, + @Param("startId") String startId, + @Param("endId") String endId, + Pageable pageable); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.java new file mode 100644 index 0000000000..b84ee5d8ec --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaBaseEdgeEventDao.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.sql.edge; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.UUIDConverter; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.id.EdgeEventId; +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.DaoUtil; +import org.thingsboard.server.dao.edge.EdgeEventDao; +import org.thingsboard.server.dao.model.sql.EdgeEventEntity; +import org.thingsboard.server.dao.sql.JpaAbstractSearchTimeDao; +import org.thingsboard.server.dao.util.SqlDao; + +import javax.persistence.criteria.Predicate; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + +import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; +import static org.thingsboard.server.dao.DaoUtil.endTimeToId; +import static org.thingsboard.server.dao.DaoUtil.startTimeToId; +import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; + +@Slf4j +@Component +@SqlDao +public class JpaBaseEdgeEventDao extends JpaAbstractSearchTimeDao implements EdgeEventDao { + + private final UUID systemTenantId = NULL_UUID; + + @Autowired + private EdgeEventRepository edgeEventRepository; + + @Override + protected Class getEntityClass() { + return EdgeEventEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return edgeEventRepository; + } + + @Override + public ListenableFuture saveAsync(EdgeEvent edgeEvent) { + log.debug("Save edge event [{}] ", edgeEvent); + if (edgeEvent.getId() == null) { + edgeEvent.setId(new EdgeEventId(Uuids.timeBased())); + } + return service.submit(() -> save(new EdgeEventEntity(edgeEvent)).orElse(null)); + } + + @Override + public PageData findEdgeEvents(UUID tenantId, EdgeId edgeId, TimePageLink pageLink) { + return DaoUtil.toPageData( + edgeEventRepository + .findEdgeEventsByTenantIdAndEdgeId( + fromTimeUUID(tenantId), + fromTimeUUID(edgeId.getId()), + startTimeToId(pageLink.getStartTime()), + endTimeToId(pageLink.getEndTime()), + DaoUtil.toPageable(pageLink))); + + } + + public Optional save(EdgeEventEntity entity) { + log.debug("Save edge event [{}] ", entity); + if (entity.getTenantId() == null) { + log.trace("Save system edge event with predefined id {}", systemTenantId); + entity.setTenantId(UUIDConverter.fromTimeUUID(systemTenantId)); + } + if (entity.getUuid() == null) { + entity.setUuid(Uuids.timeBased()); + } + return Optional.of(DaoUtil.getData(edgeEventRepository.save(entity))); + } + + private Specification getEntityFieldsSpec(UUID tenantId, EdgeId edgeId) { + return (root, criteriaQuery, criteriaBuilder) -> { + List predicates = new ArrayList<>(); + if (tenantId != null) { + Predicate tenantIdPredicate = criteriaBuilder.equal(root.get("tenantId"), UUIDConverter.fromTimeUUID(tenantId)); + predicates.add(tenantIdPredicate); + } + if (edgeId != null) { + Predicate entityIdPredicate = criteriaBuilder.equal(root.get("edgeId"), UUIDConverter.fromTimeUUID(edgeId.getId())); + predicates.add(entityIdPredicate); + } + return criteriaBuilder.and(predicates.toArray(new Predicate[]{})); + }; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index a5a8f830ba..8a6b4517ed 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -15,7 +15,10 @@ */ package org.thingsboard.server.dao.sql.edge; +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.repository.CrudRepository; import org.springframework.stereotype.Component; @@ -24,13 +27,18 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.UUIDConverter; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeInfo; +import org.thingsboard.server.common.data.id.DashboardId; +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.RelationTypeGroup; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.edge.EdgeDao; import org.thingsboard.server.dao.model.sql.EdgeEntity; import org.thingsboard.server.dao.model.sql.EdgeInfoEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; @@ -46,11 +54,15 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUIDs; @Component @SqlDao +@Slf4j public class JpaEdgeDao extends JpaAbstractSearchTextDao implements EdgeDao { @Autowired private EdgeRepository edgeRepository; + @Autowired + private RelationDao relationDao; + @Override protected Class getEntityClass() { return EdgeEntity.class; @@ -148,6 +160,32 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple return Optional.ofNullable(edge); } + @Override + public ListenableFuture> findEdgesByTenantIdAndRuleChainId(UUID tenantId, UUID ruleChainId) { + log.debug("Try to find edges by tenantId [{}], ruleChainId [{}]", tenantId, ruleChainId); + ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new RuleChainId(ruleChainId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transformAsync(relations, input -> { + List> edgeFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getFrom().getId())); + } + return Futures.successfulAsList(edgeFutures); + }, MoreExecutors.directExecutor()); + } + + @Override + public ListenableFuture> findEdgesByTenantIdAndDashboardId(UUID tenantId, UUID dashboardId) { + log.debug("Try to find edges by tenantId [{}], dashboardId [{}]", tenantId, dashboardId); + ListenableFuture> relations = relationDao.findAllByToAndType(new TenantId(tenantId), new DashboardId(dashboardId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transformAsync(relations, input -> { + List> edgeFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + edgeFutures.add(findByIdAsync(new TenantId(tenantId), relation.getFrom().getId())); + } + return Futures.successfulAsList(edgeFutures); + }, MoreExecutors.directExecutor()); + } + private List convertTenantEdgeTypesToDto(UUID tenantId, List types) { List list = Collections.emptyList(); if (types != null && !types.isEmpty()) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java index 103ad3e8a8..794c11a032 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/EntityViewRepository.java @@ -121,22 +121,4 @@ public interface EntityViewRepository extends PagingAndSortingRepository findTenantEntityViewTypes(@Param("tenantId") String tenantId); - - @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + - "AND e.edgeId = :edgeId " + - "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") - Page findByTenantIdAndEdgeId(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("searchText") String searchText, - Pageable pageable); - - @Query("SELECT e FROM EntityViewEntity e WHERE e.tenantId = :tenantId " + - "AND e.edgeId = :edgeId " + - "AND e.type = :type " + - "AND LOWER(e.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") - Page findByTenantIdAndEdgeIdAndType(@Param("tenantId") String tenantId, - @Param("edgeId") String edgeId, - @Param("type") String type, - @Param("searchText") String searchText, - Pageable pageable); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java index d7a90c3679..499c94755a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -15,18 +15,30 @@ */ package org.thingsboard.server.dao.sql.entityview; +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.repository.CrudRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.*; +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.UUIDConverter; +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.entityview.EntityViewDao; import org.thingsboard.server.dao.model.sql.EntityViewEntity; import org.thingsboard.server.dao.model.sql.EntityViewInfoEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; @@ -44,12 +56,16 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; */ @Component @SqlDao +@Slf4j public class JpaEntityViewDao extends JpaAbstractSearchTextDao implements EntityViewDao { @Autowired private EntityViewRepository entityViewRepository; + @Autowired + private RelationDao relationDao; + @Override protected Class getEntityClass() { return EntityViewEntity.class; @@ -178,27 +194,22 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao findEntityViewsByTenantIdAndEdgeId(UUID tenantId, - UUID edgeId, - PageLink pageLink) { - return DaoUtil.toPageData( - entityViewRepository.findByTenantIdAndEdgeId( - fromTimeUUID(tenantId), - fromTimeUUID(edgeId), - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink) - )); - } - - @Override - public PageData findEntityViewsByTenantIdAndEdgeIdAndType(UUID tenantId, UUID edgeId, String type, PageLink pageLink) { - return DaoUtil.toPageData( - entityViewRepository.findByTenantIdAndEdgeIdAndType( - fromTimeUUID(tenantId), - fromTimeUUID(edgeId), - type, - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink) - )); + public ListenableFuture> findEntityViewsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { + log.debug("Try to find entity views by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); + ListenableFuture> relations = + relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.ENTITY_VIEW, pageLink); + return Futures.transformAsync(relations, relationsData -> { + if (relationsData != null && relationsData.getData() != null && !relationsData.getData().isEmpty()) { + List> entityViewFutures = new ArrayList<>(relationsData.getData().size()); + for (EntityRelation relation : relationsData.getData()) { + entityViewFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.transform(Futures.successfulAsList(entityViewFutures), + entityViews -> new PageData<>(entityViews, relationsData.getTotalPages(), relationsData.getTotalElements(), + relationsData.hasNext()), MoreExecutors.directExecutor()); + } else { + return Futures.immediateFuture(new PageData<>()); + } + }, MoreExecutors.directExecutor()); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index 12dd16ebf7..90583f9ea8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -15,21 +15,34 @@ */ package org.thingsboard.server.dao.sql.rule; +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.repository.CrudRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.UUIDConverter; +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.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.RuleChainEntity; +import org.thingsboard.server.dao.relation.RelationDao; import org.thingsboard.server.dao.rule.RuleChainDao; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import org.thingsboard.server.dao.util.SqlDao; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.UUID; @@ -41,6 +54,9 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, PageLink pageLink) { + public ListenableFuture> findRuleChainsByTenantIdAndEdgeId(UUID tenantId, UUID edgeId, TimePageLink pageLink) { log.debug("Try to find rule chains by tenantId [{}], edgeId [{}] and pageLink [{}]", tenantId, edgeId, pageLink); - return DaoUtil.toPageData(ruleChainRepository - .findByTenantIdAndEdgeId( - UUIDConverter.fromTimeUUID(tenantId), - UUIDConverter.fromTimeUUID(edgeId), - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); + ListenableFuture> relations = + relationDao.findRelations(new TenantId(tenantId), new EdgeId(edgeId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE, EntityType.RULE_CHAIN, pageLink); + return Futures.transformAsync(relations, relationsData -> { + if (relationsData != null && relationsData.getData() != null && !relationsData.getData().isEmpty()) { + List> ruleChainFutures = new ArrayList<>(relationsData.getData().size()); + for (EntityRelation relation : relationsData.getData()) { + ruleChainFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.transform(Futures.successfulAsList(ruleChainFutures), + ruleChains -> new PageData<>(ruleChains, relationsData.getTotalPages(), relationsData.getTotalElements(), + relationsData.hasNext()), MoreExecutors.directExecutor()); + } else { + return Futures.immediateFuture(new PageData<>()); + } + }, MoreExecutors.directExecutor()); } + @Override + public ListenableFuture> findDefaultEdgeRuleChainsByTenantId(UUID tenantId) { + log.debug("Try to find default edge rule chains by tenantId [{}]", tenantId); + ListenableFuture> relations = + relationDao.findAllByFromAndType(new TenantId(tenantId), new TenantId(tenantId), EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE_DEFAULT_RULE_CHAIN); + return Futures.transformAsync(relations, input -> { + if (input != null && !input.isEmpty()) { + List> ruleChainsFutures = new ArrayList<>(input.size()); + for (EntityRelation relation : input) { + ruleChainsFutures.add(findByIdAsync(new TenantId(tenantId), relation.getTo().getId())); + } + return Futures.successfulAsList(ruleChainsFutures); + } else { + return Futures.immediateFuture(Collections.emptyList()); + } + }, MoreExecutors.directExecutor()); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java index 5bf7b0db31..100eb976d7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.sql.user; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; @@ -40,6 +41,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR; */ @Component @SqlDao +@Slf4j public class JpaUserDao extends JpaAbstractSearchTextDao implements UserDao { @Autowired diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java index 1ab1037613..b13b41c6c2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java @@ -41,7 +41,7 @@ public interface UserDao extends Dao { * @return the user entity */ User findByEmail(TenantId tenantId, String email); - + /** * Find tenant admin users by tenantId and page link. * diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java index 1c69e222fb..cc07bce640 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; 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.exception.IncorrectParameterException; @@ -85,6 +86,9 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @Autowired private CustomerDao customerDao; + @Autowired + private EdgeService edgeService; + @Override public User findUserByEmail(TenantId tenantId, String email) { log.trace("Executing findUserByEmail [{}]", email); diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 42f52a2184..53042215cd 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -266,5 +266,18 @@ CREATE TABLE IF NOT EXISTS edge ( routing_key varchar(255), secret varchar(255), search_text varchar(255), - tenant_id varchar(31) + tenant_id varchar(31), + CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name), + CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key) ); + +CREATE TABLE IF NOT EXISTS edge_event ( + id varchar(31) NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY, + edge_id varchar(31), + edge_event_type varchar(255), + entity_id varchar(31), + edge_event_action varchar(255), + entity_body varchar(10000000), + tenant_id varchar(31), + ts bigint NOT NULL +); \ No newline at end of file diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index ffea8bdf39..c37641320b 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -266,7 +266,21 @@ CREATE TABLE IF NOT EXISTS edge ( routing_key varchar(255), secret varchar(255), search_text varchar(255), - tenant_id varchar(31) + tenant_id varchar(31), + CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name), + CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key) +); + + +CREATE TABLE IF NOT EXISTS edge_event ( + id varchar(31) NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY, + edge_id varchar(31), + edge_event_type varchar(255), + entity_id varchar(31), + edge_event_action varchar(255), + entity_body varchar(10000000), + tenant_id varchar(31), + ts bigint NOT NULL ); CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index e7a26ab53c..b5cdbb16e7 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -45,6 +45,7 @@ import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.edge.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; @@ -125,6 +126,9 @@ public abstract class AbstractServiceTest { @Autowired protected EdgeService edgeService; + @Autowired + protected EdgeEventService edgeEventService; + @Autowired private ComponentDescriptorService componentDescriptorService; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java index 7f06532a14..5b1d588f47 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDashboardServiceTest.java @@ -356,6 +356,8 @@ public abstract class BaseDashboardServiceTest extends AbstractServiceTest { edge.setType("default"); edge.setName("Test different edge"); edge.setType("default"); + edge.setSecret(RandomStringUtils.randomAlphanumeric(20)); + edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20)); edge = edgeService.saveEdge(edge); try { dashboardService.assignDashboardToEdge(tenantId, dashboard.getId(), edge.getId()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java new file mode 100644 index 0000000000..7aec1cbd32 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeEventServiceTest.java @@ -0,0 +1,109 @@ +/** + * 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.service; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.junit.Assert; +import org.junit.Test; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.DeviceId; +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 org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.SortOrder; +import org.thingsboard.server.common.data.page.TimePageLink; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneOffset; + +public abstract class BaseEdgeEventServiceTest extends AbstractServiceTest { + + @Test + public void saveEdgeEvent() throws Exception { + EdgeId edgeId = new EdgeId(Uuids.timeBased()); + DeviceId deviceId = new DeviceId(Uuids.timeBased()); + EdgeEvent edgeEvent = generateEdgeEvent(null, edgeId, deviceId, DataConstants.ENTITY_CREATED); + EdgeEvent saved = edgeEventService.saveAsync(edgeEvent).get(); + Assert.assertEquals(saved.getTenantId(), edgeEvent.getTenantId()); + Assert.assertEquals(saved.getEdgeId(), edgeEvent.getEdgeId()); + Assert.assertEquals(saved.getEntityId(), edgeEvent.getEntityId()); + Assert.assertEquals(saved.getEdgeEventType(), edgeEvent.getEdgeEventType()); + Assert.assertEquals(saved.getEdgeEventAction(), edgeEvent.getEdgeEventAction()); + Assert.assertEquals(saved.getEntityBody(), edgeEvent.getEntityBody()); + } + + protected EdgeEvent generateEdgeEvent(TenantId tenantId, EdgeId edgeId, EntityId entityId, String edgeEventAction) throws IOException { + if (tenantId == null) { + tenantId = new TenantId(Uuids.timeBased()); + } + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setTenantId(tenantId); + edgeEvent.setEdgeId(edgeId); + edgeEvent.setEntityId(entityId.getId()); + edgeEvent.setEdgeEventType(EdgeEventType.DEVICE); + edgeEvent.setEdgeEventAction(edgeEventAction); + edgeEvent.setEntityBody(readFromResource("TestJsonData.json")); + return edgeEvent; + } + + + @Test + public void findEdgeEventsByTimeDescOrder() throws Exception { + long timeBeforeStartTime = LocalDateTime.of(2020, Month.NOVEMBER, 1, 11, 30).toEpochSecond(ZoneOffset.UTC); + long startTime = LocalDateTime.of(2020, Month.NOVEMBER, 1, 12, 0).toEpochSecond(ZoneOffset.UTC); + long eventTime = LocalDateTime.of(2020, Month.NOVEMBER, 1, 12, 30).toEpochSecond(ZoneOffset.UTC); + long endTime = LocalDateTime.of(2020, Month.NOVEMBER, 1, 13, 0).toEpochSecond(ZoneOffset.UTC); + long timeAfterEndTime = LocalDateTime.of(2020, Month.NOVEMBER, 1, 13, 30).toEpochSecond(ZoneOffset.UTC); + + EdgeId edgeId = new EdgeId(Uuids.timeBased()); + DeviceId deviceId = new DeviceId(Uuids.timeBased()); + TenantId tenantId = new TenantId(Uuids.timeBased()); + saveEdgeEventWithProvidedTime(timeBeforeStartTime, edgeId, deviceId, tenantId); + EdgeEvent savedEdgeEvent = saveEdgeEventWithProvidedTime(eventTime, edgeId, deviceId, tenantId); + EdgeEvent savedEdgeEvent2 = saveEdgeEventWithProvidedTime(eventTime + 1, edgeId, deviceId, tenantId); + EdgeEvent savedEdgeEvent3 = saveEdgeEventWithProvidedTime(eventTime + 2, edgeId, deviceId, tenantId); + saveEdgeEventWithProvidedTime(timeAfterEndTime, edgeId, deviceId, tenantId); + + TimePageLink pageLink = new TimePageLink(2, 0, "", new SortOrder("createdTime", SortOrder.Direction.DESC), startTime, endTime); + PageData edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink); + + Assert.assertNotNull(edgeEvents.getData()); + Assert.assertTrue(edgeEvents.getData().size() == 2); + Assert.assertTrue(edgeEvents.getData().get(0).getUuidId().equals(savedEdgeEvent3.getUuidId())); + Assert.assertTrue(edgeEvents.getData().get(1).getUuidId().equals(savedEdgeEvent2.getUuidId())); + Assert.assertTrue(edgeEvents.hasNext()); + Assert.assertNotNull(pageLink.nextPageLink()); + + edgeEvents = edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink.nextPageLink()); + + Assert.assertNotNull(edgeEvents.getData()); + Assert.assertTrue(edgeEvents.getData().size() == 1); + Assert.assertTrue(edgeEvents.getData().get(0).getUuidId().equals(savedEdgeEvent.getUuidId())); + Assert.assertFalse(edgeEvents.hasNext()); + } + + private EdgeEvent saveEdgeEventWithProvidedTime(long time, EdgeId edgeId, EntityId entityId, TenantId tenantId) throws Exception { + EdgeEvent edgeEvent = generateEdgeEvent(tenantId, edgeId, entityId, DataConstants.ENTITY_CREATED); + edgeEvent.setId(new EdgeEventId(Uuids.startOf(time))); + return edgeEventService.saveAsync(edgeEvent).get(); + } +} \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java index 669642d10a..c49534a046 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseEdgeServiceTest.java @@ -59,10 +59,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { @Test public void testSaveEdge() { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge"); - edge.setType("default"); + Edge edge = constructEdge("My edge", "default"); Edge savedEdge = edgeService.saveEdge(edge); Assert.assertNotNull(savedEdge); @@ -109,10 +106,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { @Test(expected = DataValidationException.class) public void testAssignEdgeToNonExistentCustomer() { - Edge edge = new Edge(); - edge.setName("My edge"); - edge.setType("default"); - edge.setTenantId(tenantId); + Edge edge = constructEdge("My edge", "default"); edge = edgeService.saveEdge(edge); try { edgeService.assignEdgeToCustomer(tenantId, edge.getId(), new CustomerId(Uuids.timeBased())); @@ -123,10 +117,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { @Test(expected = DataValidationException.class) public void testAssignEdgeToCustomerFromDifferentTenant() { - Edge edge = new Edge(); - edge.setName("My edge"); - edge.setType("default"); - edge.setTenantId(tenantId); + Edge edge = constructEdge("My edge", "default"); edge = edgeService.saveEdge(edge); Tenant tenant = new Tenant(); tenant.setTitle("Test different tenant"); @@ -145,10 +136,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { @Test public void testFindEdgeById() { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge"); - edge.setType("default"); + Edge edge = constructEdge("My edge", "default"); Edge savedEdge = edgeService.saveEdge(edge); Edge foundEdge = edgeService.findEdgeById(tenantId, savedEdge.getId()); Assert.assertNotNull(foundEdge); @@ -161,24 +149,15 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { List edges = new ArrayList<>(); try { for (int i = 0; i < 3; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge B" + i); - edge.setType("typeB"); + Edge edge = constructEdge("My edge B" + i, "typeB"); edges.add(edgeService.saveEdge(edge)); } for (int i = 0; i < 7; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge C" + i); - edge.setType("typeC"); + Edge edge = constructEdge("My edge C" + i, "typeC"); edges.add(edgeService.saveEdge(edge)); } for (int i = 0; i < 9; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge A" + i); - edge.setType("typeA"); + Edge edge = constructEdge("My edge A" + i, "typeA"); edges.add(edgeService.saveEdge(edge)); } List edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId).get(); @@ -196,10 +175,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { @Test public void testDeleteEdge() { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("My edge"); - edge.setType("default"); + Edge edge = constructEdge("My edge", "default"); Edge savedEdge = edgeService.saveEdge(edge); Edge foundEdge = edgeService.findEdgeById(tenantId, savedEdge.getId()); Assert.assertNotNull(foundEdge); @@ -218,10 +194,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { List edges = new ArrayList<>(); for (int i = 0; i < 178; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("Edge" + i); - edge.setType("default"); + Edge edge = constructEdge(tenantId, "Edge " + i, "default"); edges.add(edgeService.saveEdge(edge)); } @@ -256,25 +229,19 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String title1 = "Edge title 1"; List edgesTitle1 = new ArrayList<>(); for (int i = 0; i < 143; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); 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(edgeService.saveEdge(edge)); } String title2 = "Edge title 2"; List edgesTitle2 = new ArrayList<>(); for (int i = 0; i < 175; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); 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(edgeService.saveEdge(edge)); } @@ -334,26 +301,20 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String type1 = "typeA"; List edgesType1 = new ArrayList<>(); for (int i = 0; i < 143; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); 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(edgeService.saveEdge(edge)); } String title2 = "Edge title 2"; String type2 = "typeB"; List edgesType2 = new ArrayList<>(); for (int i = 0; i < 175; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); 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(edgeService.saveEdge(edge)); } @@ -423,10 +384,7 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { List edges = new ArrayList<>(); for (int i = 0; i < 278; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); - edge.setName("Edge" + i); - edge.setType("default"); + Edge edge = constructEdge(tenantId, "Edge" + i, "default"); edge = edgeService.saveEdge(edge); edges.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -469,26 +427,20 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String title1 = "Edge title 1"; List edgesTitle1 = new ArrayList<>(); for (int i = 0; i < 175; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); 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 = edgeService.saveEdge(edge); edgesTitle1.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } String title2 = "Edge title 2"; List edgesTitle2 = new ArrayList<>(); for (int i = 0; i < 143; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); 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 = edgeService.saveEdge(edge); edgesTitle2.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -557,13 +509,10 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String type1 = "typeC"; List edgesType1 = new ArrayList<>(); for (int i = 0; i < 175; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); 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 = edgeService.saveEdge(edge); edgesType1.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -571,13 +520,10 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { String type2 = "typeD"; List edgesType2 = new ArrayList<>(); for (int i = 0; i < 143; i++) { - Edge edge = new Edge(); - edge.setTenantId(tenantId); 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 = edgeService.saveEdge(edge); edgesType2.add(edgeService.assignEdgeToCustomer(tenantId, edge.getId(), customerId)); } @@ -633,4 +579,18 @@ public abstract class BaseEdgeServiceTest extends AbstractServiceTest { customerService.deleteCustomer(tenantId, customerId); } + 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; + } + } \ No newline at end of file diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java index 9d1cd83ce4..071bad925c 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java @@ -23,6 +23,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.thingsboard.server.common.data.Tenant; +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; @@ -144,7 +145,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { PageLink pageLink = new PageLink(16); PageData pageData = null; do { - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); loadedRuleChains.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -159,7 +160,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { ruleChainService.deleteRuleChainsByTenantId(tenantId); pageLink = new PageLink(31); - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertTrue(pageData.getData().isEmpty()); @@ -195,7 +196,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { PageLink pageLink = new PageLink(19, 0, name1); PageData pageData = null; do { - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); loadedRuleChainsName1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -210,7 +211,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { List loadedRuleChainsName2 = new ArrayList<>(); pageLink = new PageLink(4, 0, name2); do { - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); loadedRuleChainsName2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -227,7 +228,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { } pageLink = new PageLink(4, 0, name1); - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -236,7 +237,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { } pageLink = new PageLink(4, 0, name2); - pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.SYSTEM, pageLink); + pageData = ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } @@ -318,6 +319,29 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest { ruleChainService.deleteRuleChainById(tenantId, savedRuleChainMetaData.getRuleChainId()); } + @Test + public void testGetDefaultEdgeRuleChains() throws Exception { + RuleChainId ruleChainId = saveRuleChainAndSetDefaultEdge("Default Edge Rule Chain 1"); + saveRuleChainAndSetDefaultEdge("Default Edge Rule Chain 2"); + List result = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId).get(); + Assert.assertEquals(2, result.size()); + + ruleChainService.removeDefaultEdgeRuleChain(tenantId, ruleChainId); + + result = ruleChainService.findDefaultEdgeRuleChainsByTenantId(tenantId).get(); + Assert.assertEquals(1, result.size()); + } + + private RuleChainId saveRuleChainAndSetDefaultEdge(String name) { + RuleChain edgeRuleChain = new RuleChain(); + edgeRuleChain.setTenantId(tenantId); + edgeRuleChain.setType(RuleChainType.EDGE); + edgeRuleChain.setName(name); + RuleChain savedEdgeRuleChain = ruleChainService.saveRuleChain(edgeRuleChain); + ruleChainService.addDefaultEdgeRuleChain(tenantId, savedEdgeRuleChain.getId()); + return savedEdgeRuleChain.getId(); + } + private RuleChainMetaData createRuleChainMetadata() throws Exception { RuleChain ruleChain = new RuleChain(); ruleChain.setName("My RuleChain"); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeEventServiceSqlTest.java similarity index 70% rename from dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java rename to dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeEventServiceSqlTest.java index 4b91ee9a6e..58b7a3014e 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/nosql/EdgeServiceNoSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/EdgeEventServiceSqlTest.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.service.nosql; +package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.BaseEdgeServiceTest; -import org.thingsboard.server.dao.service.DaoNoSqlTest; +import org.thingsboard.server.dao.service.BaseEdgeEventServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; -@DaoNoSqlTest -public class EdgeServiceNoSqlTest extends BaseEdgeServiceTest { +@DaoSqlTest +public class EdgeEventServiceSqlTest extends BaseEdgeEventServiceTest { } diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index 8946f77d6c..5b1234483f 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -20,4 +20,5 @@ DROP TABLE IF EXISTS widgets_bundle; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; -DROP TABLE IF EXISTS edge; \ No newline at end of file +DROP TABLE IF EXISTS edge; +DROP TABLE IF EXISTS edge_event; \ No newline at end of file diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index b2e4a27963..b72a02657a 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/psql/drop-all-tables.sql @@ -21,4 +21,6 @@ DROP TABLE IF EXISTS widgets_bundle; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; +DROP TABLE IF EXISTS edge; +DROP TABLE IF EXISTS edge_event; DROP TABLE IF EXISTS tb_schema_settings; \ No newline at end of file diff --git a/dao/src/test/resources/sql/timescale/drop-all-tables.sql b/dao/src/test/resources/sql/timescale/drop-all-tables.sql index b2e4a27963..b72a02657a 100644 --- a/dao/src/test/resources/sql/timescale/drop-all-tables.sql +++ b/dao/src/test/resources/sql/timescale/drop-all-tables.sql @@ -21,4 +21,6 @@ DROP TABLE IF EXISTS widgets_bundle; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; +DROP TABLE IF EXISTS edge; +DROP TABLE IF EXISTS edge_event; DROP TABLE IF EXISTS tb_schema_settings; \ No newline at end of file diff --git a/pom.xml b/pom.xml index 38a4561632..756a4cadfa 100755 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,6 @@ 28.2-jre 2.6.1 3.4 - 1.6 2.5 1.4 2.10.2 @@ -1069,11 +1068,6 @@ commons-lang3 ${commons-lang3.version} - - commons-validator - commons-validator - ${commons-validator.version} - commons-io commons-io diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java index 96b45c2cbb..1efe0081d7 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java @@ -58,6 +58,6 @@ public @interface RuleNode { boolean customRelations() default false; - RuleChainType[] ruleChainTypes() default RuleChainType.SYSTEM; + RuleChainType[] ruleChainTypes() default RuleChainType.CORE; } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index b5b450a0fa..393a5f2d1f 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -34,6 +34,7 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; 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.EdgeEventService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; @@ -186,6 +187,8 @@ public interface TbContext { EdgeService getEdgeService(); + EdgeEventService getEdgeEventService(); + ListeningExecutor getJsExecutor(); ListeningExecutor getMailExecutor(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java index 5c2392b6f7..cbbc3b260d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java @@ -36,6 +36,7 @@ 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.edge.Edge; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.page.PageData; @@ -48,6 +49,7 @@ import org.thingsboard.server.dao.asset.AssetService; 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.entityview.EntityViewService; import java.util.List; @@ -236,6 +238,13 @@ public abstract class TbAbstractRelationActionNode dashboardInfoTextPageData = dashboardService.findDashboardsByTenantId(ctx.getTenantId(), new PageLink(200, 0, entitykey.getEntityName())); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java index f6ccd33119..5d0fe87fa9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java @@ -38,7 +38,7 @@ import org.thingsboard.server.common.msg.TbMsg; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeAssignToCustomerConfig", icon = "add_circle", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbAssignToCustomerNode extends TbAbstractCustomerActionNode { @@ -69,6 +69,9 @@ public class TbAssignToCustomerNode extends TbAbstractCustomerActionNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java index e7ed1f52b2..96595eb845 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java @@ -58,7 +58,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig", icon = "content_copy", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbCopyAttributesToEntityViewNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java index 4644b7820b..4bd56be6be 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java @@ -52,7 +52,7 @@ import java.util.List; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCreateAlarmConfig", icon = "notifications_active", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbCreateAlarmNode extends TbAbstractAlarmNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java index 54678f749d..c9c0edd96f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java @@ -28,6 +28,7 @@ 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.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentType; @@ -54,7 +55,7 @@ import java.util.List; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeCreateRelationConfig", icon = "add_circle", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbCreateRelationNode extends TbAbstractRelationActionNode { @@ -137,6 +138,8 @@ public class TbCreateRelationNode extends TbAbstractRelationActionNode processEdge(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId, String relationType) { + return Futures.transformAsync(ctx.getEdgeService().findEdgeByIdAsync(ctx.getTenantId(), new EdgeId(entityContainer.getEntityId().getId())), edge -> { + if (edge != null) { + return processSave(ctx, sdId, relationType); + } else { + return Futures.immediateFuture(true); + } + }, ctx.getDbCallbackExecutor()); + } + private ListenableFuture processDevice(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId, String relationType) { return Futures.transformAsync(ctx.getDeviceService().findDeviceByIdAsync(ctx.getTenantId(), new DeviceId(entityContainer.getEntityId().getId())), device -> { if (device != null) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java index cf53645e84..2ea2264dad 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java @@ -45,7 +45,7 @@ import java.util.List; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeDeleteRelationConfig", icon = "remove_circle", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbDeleteRelationNode extends TbAbstractRelationActionNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java index d84a6f4e2e..393cc72b0b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java @@ -38,7 +38,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeLogConfig", icon = "menu", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbLogNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java index b6518f7605..9062b2bbae 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java @@ -44,7 +44,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; icon = "functions", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeMsgCountConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgCountNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java index a8ab882a46..79513c728d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.TbMsg; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeUnAssignToCustomerConfig", icon = "remove_circle", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbUnassignFromCustomerNode extends TbAbstractCustomerActionNode { @@ -62,6 +62,9 @@ public class TbUnassignFromCustomerNode extends TbAbstractCustomerActionNode { - private final TbContext ctx; - private final TbMsg msg; - - @Override - public void onSuccess(@Nullable Void result) { - ctx.tellNext(msg, SUCCESS); - } - - @Override - public void onFailure(Throwable t) { - ctx.tellFailure(msg, t); - } -} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java index 92a4103867..ad757220b3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java @@ -15,6 +15,10 @@ */ package org.thingsboard.rule.engine.edge; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; import org.thingsboard.rule.engine.api.RuleNode; @@ -23,10 +27,25 @@ import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeEventType; +import org.thingsboard.server.common.data.id.EdgeId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentType; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.session.SessionMsgType; +import javax.annotation.Nullable; +import java.util.List; + +import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; + @Slf4j @RuleNode( type = ComponentType.ACTION, @@ -40,13 +59,10 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; ) public class TbMsgPushToEdgeNode implements TbNode { - private static final String CLOUD_MSG_SOURCE = "cloud"; - private static final String EDGE_MSG_SOURCE = "edge"; - private static final String MSG_SOURCE_KEY = "source"; - private static final String TS_METADATA_KEY = "ts"; - private EmptyNodeConfiguration config; + private static final ObjectMapper json = new ObjectMapper(); + @Override public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { this.config = TbNodeUtils.convert(configuration, EmptyNodeConfiguration.class); @@ -54,14 +70,96 @@ public class TbMsgPushToEdgeNode implements TbNode { @Override public void onMsg(TbContext ctx, TbMsg msg) { - if (EDGE_MSG_SOURCE.equalsIgnoreCase(msg.getMetaData().getValue(MSG_SOURCE_KEY))) { + if (DataConstants.EDGE_MSG_SOURCE.equalsIgnoreCase(msg.getMetaData().getValue(DataConstants.MSG_SOURCE_KEY))) { + log.debug("Ignoring msg from the cloud, msg [{}]", msg); return; } - if (msg.getType().equals(SessionMsgType.POST_TELEMETRY_REQUEST.name())) { - msg.getMetaData().putValue(TS_METADATA_KEY, Long.toString(System.currentTimeMillis())); + if (isSupportedOriginator(msg.getOriginator().getEntityType())) { + if (isSupportedMsgType(msg.getType())) { + ListenableFuture getEdgeIdFuture = getEdgeIdByOriginatorId(ctx, ctx.getTenantId(), msg.getOriginator()); + Futures.transform(getEdgeIdFuture, edgeId -> { + EdgeEventType edgeEventTypeByEntityType = ctx.getEdgeEventService().getEdgeEventTypeByEntityType(msg.getOriginator().getEntityType()); + if (edgeEventTypeByEntityType == null) { + log.debug("Edge event type is null. Entity Type {}", msg.getOriginator().getEntityType()); + ctx.tellFailure(msg, new RuntimeException("Edge event type is null. Entity Type '" + msg.getOriginator().getEntityType() + "'")); + } + EdgeEvent edgeEvent = new EdgeEvent(); + edgeEvent.setTenantId(ctx.getTenantId()); + edgeEvent.setEdgeId(edgeId); + edgeEvent.setEdgeEventAction(getActionTypeByMsgType(msg.getType()).name()); + edgeEvent.setEntityId(msg.getOriginator().getId()); + edgeEvent.setEdgeEventType(edgeEventTypeByEntityType); + edgeEvent.setEntityBody(json.valueToTree(msg.getData())); + ListenableFuture saveFuture = ctx.getEdgeEventService().saveAsync(edgeEvent); + Futures.addCallback(saveFuture, new FutureCallback() { + @Override + public void onSuccess(@Nullable EdgeEvent event) { + ctx.tellNext(msg, SUCCESS); + } + + @Override + public void onFailure(Throwable th) { + log.error("Could not save edge event", th); + ctx.tellFailure(msg, th); + } + }, ctx.getDbCallbackExecutor()); + return null; + }, ctx.getDbCallbackExecutor()); + } else { + log.debug("Unsupported msg type {}", msg.getType()); + ctx.tellFailure(msg, new RuntimeException("Unsupported msg type '" + msg.getType() + "'")); + } + } else { + log.debug("Unsupported originator type {}", msg.getOriginator().getEntityType()); + ctx.tellFailure(msg, new RuntimeException("Unsupported originator type '" + msg.getOriginator().getEntityType() + "'")); + } + } + + private ActionType getActionTypeByMsgType(String msgType) { + ActionType actionType; + if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msgType)) { + actionType = ActionType.TIMESERIES_UPDATED; + } else if (SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msgType) + || DataConstants.ATTRIBUTES_UPDATED.equals(msgType)) { + actionType = ActionType.ATTRIBUTES_UPDATED; + } else { + actionType = ActionType.ATTRIBUTES_DELETED; + } + return actionType; + } + + private boolean isSupportedOriginator(EntityType entityType) { + switch (entityType) { + case DEVICE: + case ASSET: + case ENTITY_VIEW: + case DASHBOARD: + return true; + default: + return false; + } + } + + private boolean isSupportedMsgType(String msgType) { + if (SessionMsgType.POST_TELEMETRY_REQUEST.name().equals(msgType) + || SessionMsgType.POST_ATTRIBUTES_REQUEST.name().equals(msgType) + || DataConstants.ATTRIBUTES_UPDATED.equals(msgType) + || DataConstants.ATTRIBUTES_DELETED.equals(msgType)) { + return true; + } else { + return false; } - msg.getMetaData().putValue(MSG_SOURCE_KEY, CLOUD_MSG_SOURCE); - ctx.getEdgeService().pushEventToEdge(ctx.getTenantId(), msg, new PushToEdgeNodeCallback(ctx, msg)); + } + + private ListenableFuture getEdgeIdByOriginatorId(TbContext ctx, TenantId tenantId, EntityId originatorId) { + ListenableFuture> future = ctx.getRelationService().findByToAndTypeAsync(tenantId, originatorId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.EDGE); + return Futures.transform(future, relations -> { + if (relations != null && relations.size() > 0) { + return new EdgeId(relations.get(0).getFrom().getId()); + } else { + return null; + } + }, ctx.getDbCallbackExecutor()); } @Override diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java index 44175abac6..e9d54de5de 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java @@ -41,7 +41,7 @@ import java.util.Map; "Else if the checkbox is not selected, and at least one of the keys from data or metadata of the message exists - send Message via True chain, otherwise, False chain is used. ", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeCheckMessageConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbCheckMessageNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java index 01ac3db853..f5912d1e5b 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java @@ -53,7 +53,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; nodeDetails = "If at least one relation exists - send Message via True chain, otherwise False chain is used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeCheckRelationConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbCheckRelationNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java index 3d1aa94b9b..997a872a4a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java @@ -38,7 +38,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "Message type can be accessed via msgType property.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeScriptConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbJsFilterNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java index 207c029a5d..fa8f332d69 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java @@ -41,7 +41,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; "Message type can be accessed via msgType property.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeSwitchConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbJsSwitchNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java index 30e77a1084..2e9cfc4657 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDetails = "If incoming MessageType is expected - send Message via True chain, otherwise False chain is used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeMessageTypeConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgTypeFilterNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java index bbb7b5a113..0973b49a97 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java @@ -36,7 +36,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; nodeDetails = "Sends messages with message types \"Post attributes\", \"Post telemetry\", \"RPC Request\" etc. via corresponding chain, otherwise Other chain is used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgTypeSwitchNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java index 09ee3a63eb..63c6e627b0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java @@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDetails = "If Originator Type of incoming message is expected - send Message via True chain, otherwise False chain is used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeOriginatorTypeConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbOriginatorTypeFilterNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java index 55e821078c..9bd2109746 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java @@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg; nodeDetails = "Routes messages to chain according to the originator type ('Device', 'Asset', etc.).", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbNodeEmptyConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbOriginatorTypeSwitchNode implements TbNode { @@ -70,6 +70,9 @@ public class TbOriginatorTypeSwitchNode implements TbNode { case ENTITY_VIEW: relationType = "Entity View"; break; + case EDGE: + relationType = "Edge"; + break; case RULE_CHAIN: relationType = "Rule chain"; break; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java index 681fac3ef1..036bfbd762 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java @@ -51,7 +51,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodePubSubConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+Cjx0aXRsZT5DbG91ZCBQdWJTdWI8L3RpdGxlPgo8Zz4KPHBhdGggZD0iTTEyNi40Nyw1OC4xMmwtMjYuMy00NS43NEExMS41NiwxMS41NiwwLDAsMCw5MC4zMSw2LjVIMzcuN2ExMS41NSwxMS41NSwwLDAsMC05Ljg2LDUuODhMMS41Myw1OGExMS40OCwxMS40OCwwLDAsMCwwLDExLjQ0bDI2LjMsNDZhMTEuNzcsMTEuNzcsMCwwLDAsOS44Niw2LjA5SDkwLjNhMTEuNzMsMTEuNzMsMCwwLDAsOS44Ny02LjA2bDI2LjMtNDUuNzRBMTEuNzMsMTEuNzMsMCwwLDAsMTI2LjQ3LDU4LjEyWiIgc3R5bGU9ImZpbGw6ICM3MzViMmYiLz4KPHBhdGggZD0iTTg5LjIyLDQ3Ljc0LDgzLjM2LDQ5bC0xNC42LTE0LjZMNjQuMDksNDMuMSw2MS41NSw1My4ybDQuMjksNC4yOUw1Ny42LDU5LjE4LDQ2LjMsNDcuODhsLTcuNjcsNy4zOEw1Mi43Niw2OS4zN2wtMTUsMTEuOUw3OCwxMjEuNUg5MC4zYTExLjczLDExLjczLDAsMCwwLDkuODctNi4wNmwyMC43Mi0zNloiIHN0eWxlPSJvcGFjaXR5OiAwLjA3MDAwMDAwMDI5ODAyMztpc29sYXRpb246IGlzb2xhdGUiLz4KPHBhdGggZD0iTTgyLjg2LDQ3YTUuMzIsNS4zMiwwLDEsMS0xLjk1LDcuMjdBNS4zMiw1LjMyLDAsMCwxLDgyLjg2LDQ3IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNMzkuODIsNTYuMThhNS4zMiw1LjMyLDAsMSwxLDcuMjctMS45NSw1LjMyLDUuMzIsMCwwLDEtNy4yNywxLjk1IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNjkuMzIsODguODVBNS4zMiw1LjMyLDAsMSwxLDY0LDgzLjUyYTUuMzIsNS4zMiwwLDAsMSw1LjMyLDUuMzIiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxnPgo8cGF0aCBkPSJNNjQsNTIuOTRhMTEuMDYsMTEuMDYsMCwwLDEsMi40Ni4yOFYzOS4xNUg2MS41NFY1My4yMkExMS4wNiwxMS4wNiwwLDAsMSw2NCw1Mi45NFoiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik03NC41Nyw2Ny4yNmExMSwxMSwwLDAsMS0yLjQ3LDQuMjVsMTIuMTksNywyLjQ2LTQuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNTMuNDMsNjcuMjZsLTEyLjE4LDcsMi40Niw0LjI2LDEyLjE5LTdBMTEsMTEsMCwwLDEsNTMuNDMsNjcuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi42LDY0QTguNiw4LjYsMCwxLDEsNjQsNTUuNCw4LjYsOC42LDAsMCwxLDcyLjYsNjQiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik0zOS4xLDcwLjU3YTYuNzYsNi43NiwwLDEsMS0yLjQ3LDkuMjMsNi43Niw2Ljc2LDAsMCwxLDIuNDctOS4yMyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTgyLjE0LDgyLjI3YTYuNzYsNi43NiwwLDEsMSw5LjIzLTIuNDcsNi43NSw2Ljc1LDAsMCwxLTkuMjMsMi40NyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTcwLjc2LDM5LjE1QTYuNzYsNi43NiwwLDEsMSw2NCwzMi4zOWE2Ljc2LDYuNzYsMCwwLDEsNi43Niw2Ljc2IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+Cjwvc3ZnPgo=", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbPubSubNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java index 0a177cdce6..bb2e0bbca9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java @@ -53,7 +53,7 @@ import java.util.concurrent.TimeoutException; nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeGpsGeofencingConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGpsGeofencingActionNode extends AbstractGeofencingNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java index 260c630bd9..a40f416f92 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java @@ -54,7 +54,7 @@ import java.util.List; nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns 'True' if they are inside configured perimeters, 'False' otherwise.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbFilterNodeGpsGeofencingConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java index b0dc4b636b..2945b09ab8 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java @@ -53,7 +53,7 @@ import java.util.Properties; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeKafkaConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUzOCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDQxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTIwMS44MTYgMjMwLjIxNmMtMTYuMTg2IDAtMzAuNjk3IDcuMTcxLTQwLjYzNCAxOC40NjFsLTI1LjQ2My0xOC4wMjZjMi43MDMtNy40NDIgNC4yNTUtMTUuNDMzIDQuMjU1LTIzLjc5NyAwLTguMjE5LTEuNDk4LTE2LjA3Ni00LjExMi0yMy40MDhsMjUuNDA2LTE3LjgzNWM5LjkzNiAxMS4yMzMgMjQuNDA5IDE4LjM2NSA0MC41NDggMTguMzY1IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI5Ljg3OS0yNC4zMDktNTQuMTg0LTU0LjE4NC01NC4xODQtMjkuODc1IDAtNTQuMTg0IDI0LjMwNS01NC4xODQgNTQuMTg0IDAgNS4zNDguODA4IDEwLjUwNSAyLjI1OCAxNS4zODlsLTI1LjQyMyAxNy44NDRjLTEwLjYyLTEzLjE3NS0yNS45MTEtMjIuMzc0LTQzLjMzMy0yNS4xODJ2LTMwLjY0YzI0LjU0NC01LjE1NSA0My4wMzctMjYuOTYyIDQzLjAzNy01My4wMTlDMTI0LjE3MSAyNC4zMDUgOTkuODYyIDAgNjkuOTg3IDAgNDAuMTEyIDAgMTUuODAzIDI0LjMwNSAxNS44MDMgNTQuMTg0YzAgMjUuNzA4IDE4LjAxNCA0Ny4yNDYgNDIuMDY3IDUyLjc2OXYzMS4wMzhDMjUuMDQ0IDE0My43NTMgMCAxNzIuNDAxIDAgMjA2Ljg1NGMwIDM0LjYyMSAyNS4yOTIgNjMuMzc0IDU4LjM1NSA2OC45NHYzMi43NzRjLTI0LjI5OSA1LjM0MS00Mi41NTIgMjcuMDExLTQyLjU1MiA1Mi44OTQgMCAyOS44NzkgMjQuMzA5IDU0LjE4NCA1NC4xODQgNTQuMTg0IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI1Ljg4My0xOC4yNTMtNDcuNTUzLTQyLjU1Mi01Mi44OTR2LTMyLjc3NWE2OS45NjUgNjkuOTY1IDAgMCAwIDQyLjYtMjQuNzc2bDI1LjYzMyAxOC4xNDNjLTEuNDIzIDQuODQtMi4yMiA5Ljk0Ni0yLjIyIDE1LjI0IDAgMjkuODc5IDI0LjMwOSA1NC4xODQgNTQuMTg0IDU0LjE4NCAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yOS44NzktMjQuMzA5LTU0LjE4NC01NC4xODQtNTQuMTg0em0wLTEyNi42OTVjMTQuNDg3IDAgMjYuMjcgMTEuNzg4IDI2LjI3IDI2LjI3MXMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3LTI2LjI3LTExLjc4Ny0yNi4yNy0yNi4yN2MwLTE0LjQ4MyAxMS43ODMtMjYuMjcxIDI2LjI3LTI2LjI3MXptLTE1OC4xLTQ5LjMzN2MwLTE0LjQ4MyAxMS43ODQtMjYuMjcgMjYuMjcxLTI2LjI3czI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN2MwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3em01Mi41NDEgMzA3LjI3OGMwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3YzAtMTQuNDgzIDExLjc4NC0yNi4yNyAyNi4yNzEtMjYuMjdzMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3em0tMjYuMjcyLTExNy45N2MtMjAuMjA1IDAtMzYuNjQyLTE2LjQzNC0zNi42NDItMzYuNjM4IDAtMjAuMjA1IDE2LjQzNy0zNi42NDIgMzYuNjQyLTM2LjY0MiAyMC4yMDQgMCAzNi42NDEgMTYuNDM3IDM2LjY0MSAzNi42NDIgMCAyMC4yMDQtMTYuNDM3IDM2LjYzOC0zNi42NDEgMzYuNjM4em0xMzEuODMxIDY3LjE3OWMtMTQuNDg3IDAtMjYuMjctMTEuNzg4LTI2LjI3LTI2LjI3MXMxMS43ODMtMjYuMjcgMjYuMjctMjYuMjcgMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3YzAgMTQuNDgzLTExLjc4MyAyNi4yNzEtMjYuMjcgMjYuMjcxeiIvPjwvc3ZnPg==", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbKafkaNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java index 2133314e61..1a4fb244b3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java @@ -42,7 +42,7 @@ import static org.thingsboard.rule.engine.mail.TbSendEmailNode.SEND_EMAIL_TYPE; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeToEmailConfig", icon = "email", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMsgToEmailNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java index 237a56a36c..043b5008c4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java @@ -48,7 +48,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeSendEmailConfig", icon = "send", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbSendEmailNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java index cbe0054864..92aca60332 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java @@ -42,7 +42,7 @@ import org.thingsboard.server.common.msg.TbMsg; "metadata.cs_temperature or metadata.shared_limit ", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeOriginatorAttributesConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetAttributesNode extends TbAbstractGetAttributesNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java index c2cb7833b7..77ae6acccc 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeCustomerAttributesConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java index 28eabec41f..93a03498d7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.ContactBased; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.rule.RuleChainType; @@ -43,7 +44,7 @@ import org.thingsboard.server.common.msg.TbMsg; "If the originator of the message is not assigned to Customer, or originator type is not supported - Message will be forwarded to Failure chain, otherwise, Success chain will be used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeEntityDetailsConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode { @@ -108,6 +109,18 @@ public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode { + if (edge != null) { + if (!edge.getCustomerId().isNullUid()) { + return ctx.getCustomerService().findCustomerByIdAsync(ctx.getTenantId(), edge.getCustomerId()); + } else { + throw new RuntimeException("Edge with name '" + edge.getName() + "' is not assigned to Customer."); + } + } else { + return Futures.immediateFuture(null); + } + }, MoreExecutors.directExecutor()); default: throw new RuntimeException("Entity with entityType '" + msg.getOriginator().getEntityType() + "' is not supported."); } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java index 48649aab58..af6cbf9265 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java @@ -40,7 +40,7 @@ import org.thingsboard.server.common.msg.TbMsg; "metadata.cs_temperature or metadata.shared_limit ", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeDeviceAttributesConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetDeviceAttrNode extends TbAbstractGetAttributesNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java index 6885b1362d..6f240d2df6 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java @@ -45,7 +45,7 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; nodeDetails = "Will fetch fields values specified in mapping. If specified field is not part of originator fields it will be ignored.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeOriginatorFieldsConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetOriginatorFieldsNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java index 5e313ddc92..9603c8afe4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java @@ -37,7 +37,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeRelatedAttributesConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java index a438208bcb..009870e404 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java @@ -68,7 +68,7 @@ import static org.thingsboard.server.common.data.kv.Aggregation.NONE; "Note: The maximum size of the fetched array is 1000 records.\n ", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetTelemetryNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java index 22baadeb4b..979588035d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java @@ -37,7 +37,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; "metadata.temperature.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeTenantAttributesConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetTenantAttributeNode extends TbEntityGetAttrNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java index 434200228f..cf4bcaf064 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java @@ -39,7 +39,7 @@ import org.thingsboard.server.common.msg.TbMsg; "If the originator of the message is not assigned to Tenant, or originator type is not supported - Message will be forwarded to Failure chain, otherwise, Success chain will be used.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbEnrichmentNodeEntityDetailsConfig", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java index 813c413fad..18e7b621fb 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java @@ -51,7 +51,7 @@ import java.util.concurrent.TimeoutException; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeMqttConfig", icon = "call_split", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbMqttNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java index 7756dd71c4..78ac75a642 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java @@ -41,7 +41,7 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback; uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRabbitMqConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZlcnNpb249IjEuMSIgeT0iMHB4IiB4PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIHN0cm9rZS13aWR0aD0iLjg0OTU2IiBkPSJtODYwLjQ3IDQxNi4zMmgtMjYyLjAxYy0xMi45MTMgMC0yMy42MTgtMTAuNzA0LTIzLjYxOC0yMy42MTh2LTI3Mi43MWMwLTIwLjMwNS0xNi4yMjctMzYuMjc2LTM2LjI3Ni0zNi4yNzZoLTkzLjc5MmMtMjAuMzA1IDAtMzYuMjc2IDE2LjIyNy0zNi4yNzYgMzYuMjc2djI3MC44NGMtMC4yNTQ4NyAxNC4xMDMtMTEuNDY5IDI1LjU3Mi0yNS43NDIgMjUuNTcybC04NS42MzYgMC42Nzk2NWMtMTQuMTAzIDAtMjUuNTcyLTExLjQ2OS0yNS41NzItMjUuNTcybDAuNjc5NjUtMjcxLjUyYzAtMjAuMzA1LTE2LjIyNy0zNi4yNzYtMzYuMjc2LTM2LjI3NmgtOTMuNTM3Yy0yMC4zMDUgMC0zNi4yNzYgMTYuMjI3LTM2LjI3NiAzNi4yNzZ2NzYzLjg0YzAgMTguMDk2IDE0Ljc4MiAzMi40NTMgMzIuNDUzIDMyLjQ1M2g3MjIuODFjMTguMDk2IDAgMzIuNDUzLTE0Ljc4MiAzMi40NTMtMzIuNDUzdi00MzUuMzFjLTEuMTg5NC0xOC4xODEtMTUuMjkyLTMyLjE5OC0zMy4zODgtMzIuMTk4em0tMTIyLjY4IDI4Ny4wN2MwIDIzLjYxOC0xOC44NiA0Mi40NzgtNDIuNDc4IDQyLjQ3OGgtNzMuOTk3Yy0yMy42MTggMC00Mi40NzgtMTguODYtNDIuNDc4LTQyLjQ3OHYtNzQuMjUyYzAtMjMuNjE4IDE4Ljg2LTQyLjQ3OCA0Mi40NzgtNDIuNDc4aDczLjk5N2MyMy42MTggMCA0Mi40NzggMTguODYgNDIuNDc4IDQyLjQ3OHoiLz48L3N2Zz4=", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbRabbitMqNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java index ba860c103b..57bde77f9c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java @@ -20,11 +20,22 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.handler.ssl.SslContextBuilder; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.apache.http.impl.nio.client.HttpAsyncClients; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory; import org.springframework.http.client.Netty4ClientHttpRequestFactory; +import org.springframework.util.StringUtils; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; import org.springframework.web.client.AsyncRestTemplate; @@ -36,7 +47,11 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.security.NoSuchAlgorithmException; import java.util.Deque; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.TimeUnit; @@ -50,6 +65,7 @@ class TbHttpClient { private static final String STATUS_REASON = "statusReason"; private static final String ERROR = "error"; private static final String ERROR_BODY = "error_body"; + private static final String ERROR_SYSTEM_PROPERTIES = "Didn't set any system proxy properties. Should be added next system proxy properties: \"http.proxyHost\" and \"http.proxyPort\" or \"https.proxyHost\" and \"https.proxyPort\" or \"socksProxyHost\" and \"socksProxyPort\""; private final TbRestApiCallNodeConfiguration config; @@ -63,7 +79,56 @@ class TbHttpClient { if (config.getMaxParallelRequestsCount() > 0) { pendingFutures = new ConcurrentLinkedDeque<>(); } - if (config.isUseSimpleClientHttpFactory()) { + + if (config.isEnableProxy()) { + checkProxyHost(config.getProxyHost()); + checkProxyPort(config.getProxyPort()); + + String proxyUser; + String proxyPassword; + + CloseableHttpAsyncClient asyncClient; + HttpComponentsAsyncClientHttpRequestFactory requestFactory = new HttpComponentsAsyncClientHttpRequestFactory(); + + if (config.isUseSystemProxyProperties()) { + checkSystemProxyProperties(); + + asyncClient = HttpAsyncClients.createSystem(); + + proxyUser = System.getProperty("tb.proxy.user"); + proxyPassword = System.getProperty("tb.proxy.password"); + + if (useAuth(proxyUser, proxyPassword)) { + Authenticator.setDefault(new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(proxyUser, proxyPassword.toCharArray()); + } + }); + } + } else { + HttpAsyncClientBuilder httpAsyncClientBuilder = HttpAsyncClientBuilder.create() + .setSSLHostnameVerifier(new DefaultHostnameVerifier()) + .setSSLContext(SSLContext.getDefault()) + .setProxy(new HttpHost(config.getProxyHost(), config.getProxyPort(), config.getProxyScheme())); + + proxyUser = config.getProxyUser(); + proxyPassword = config.getProxyPassword(); + + if (useAuth(proxyUser, proxyPassword)) { + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + credsProvider.setCredentials( + new AuthScope(config.getProxyHost(), config.getProxyPort()), + new UsernamePasswordCredentials(proxyUser, proxyPassword) + ); + httpAsyncClientBuilder.setDefaultCredentialsProvider(credsProvider); + } + asyncClient = httpAsyncClientBuilder.build(); + } + + requestFactory.setAsyncClient(asyncClient); + requestFactory.setReadTimeout(config.getReadTimeoutMs()); + httpClient = new AsyncRestTemplate(requestFactory); + } else if (config.isUseSimpleClientHttpFactory()) { httpClient = new AsyncRestTemplate(); } else { this.eventLoopGroup = new NioEventLoopGroup(); @@ -72,11 +137,25 @@ class TbHttpClient { nettyFactory.setReadTimeout(config.getReadTimeoutMs()); httpClient = new AsyncRestTemplate(nettyFactory); } - } catch (SSLException e) { + } catch (SSLException | NoSuchAlgorithmException e) { throw new TbNodeException(e); } } + private void checkSystemProxyProperties() throws TbNodeException { + boolean useHttpProxy = !StringUtils.isEmpty(System.getProperty("http.proxyHost")) && !StringUtils.isEmpty(System.getProperty("http.proxyPort")); + boolean useHttpsProxy = !StringUtils.isEmpty(System.getProperty("https.proxyHost")) && !StringUtils.isEmpty(System.getProperty("https.proxyPort")); + boolean useSocksProxy = !StringUtils.isEmpty(System.getProperty("socksProxyHost")) && !StringUtils.isEmpty(System.getProperty("socksProxyPort")); + if (!(useHttpProxy || useHttpsProxy || useSocksProxy)) { + log.warn(ERROR_SYSTEM_PROPERTIES); + throw new TbNodeException(ERROR_SYSTEM_PROPERTIES); + } + } + + private boolean useAuth(String proxyUser, String proxyPassword) { + return !StringUtils.isEmpty(proxyUser) && !StringUtils.isEmpty(proxyPassword); + } + void destroy() { if (this.eventLoopGroup != null) { this.eventLoopGroup.shutdownGracefully(0, 5, TimeUnit.SECONDS); @@ -169,4 +248,15 @@ class TbHttpClient { } } + private static void checkProxyHost(String proxyHost) throws TbNodeException { + if (StringUtils.isEmpty(proxyHost)) { + throw new TbNodeException("Proxy host can't be empty"); + } + } + + private static void checkProxyPort(int proxyPort) throws TbNodeException { + if (proxyPort < 0 || proxyPort > 65535) { + throw new TbNodeException("Proxy port out of range:" + proxyPort); + } + } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java index 930d145259..eee1b8627f 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java @@ -37,11 +37,13 @@ import org.thingsboard.server.common.msg.TbMsg; " Outbound message will contain response fields " + "(status, statusCode, statusReason and response headers) in the Message Metadata." + " Response body saved in outbound Message payload. " + - "For example statusCode field can be accessed with metadata.statusCode.", + "For example statusCode field can be accessed with metadata.statusCode." + + "
Note- if you use system proxy properties, the next system proxy properties should be added: \"http.proxyHost\" and \"http.proxyPort\" or \"https.proxyHost\" and \"https.proxyPort\" or \"socksProxyHost\" and \"socksProxyPort\"," + + "and if your proxy with auth, the next ones should be added: \"tb.proxy.user\" and \"tb.proxy.password\" to the thingsboard.conf file.", uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeRestApiCallConfig", iconUrl = "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB2ZXJzaW9uPSIxLjEiIHk9IjBweCIgeD0iMHB4Ij48ZyB0cmFuc2Zvcm09Im1hdHJpeCguOTQ5NzUgMCAwIC45NDk3NSAxNy4xMiAyNi40OTIpIj48cGF0aCBkPSJtMTY5LjExIDEwOC41NGMtOS45MDY2IDAuMDczNC0xOS4wMTQgNi41NzI0LTIyLjAxNCAxNi40NjlsLTY5Ljk5MyAyMzEuMDhjLTMuNjkwNCAxMi4xODEgMy4yODkyIDI1LjIyIDE1LjQ2OSAyOC45MSAyLjIyNTkgMC42NzQ4MSA0LjQ5NjkgMSA2LjcyODUgMSA5Ljk3MjEgMCAxOS4xNjUtNi41MTUzIDIyLjE4Mi0xNi40NjdhNi41MjI0IDYuNTIyNCAwIDAgMCAwLjAwMiAtMC4wMDJsNjkuOTktMjMxLjA3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMCAtMC4wMDJjMy42ODU1LTEyLjE4MS0zLjI4Ny0yNS4yMjUtMTUuNDcxLTI4LjkxMi0yLjI4MjUtMC42OTE0NS00LjYxMTYtMS4wMTY5LTYuODk4NC0xem04NC45ODggMGMtOS45MDQ4IDAuMDczNC0xOS4wMTggNi41Njc1LTIyLjAxOCAxNi40NjlsLTY5Ljk4NiAyMzEuMDhjLTMuNjg5OCAxMi4xNzkgMy4yODUzIDI1LjIxNyAxNS40NjUgMjguOTA4IDIuMjI5NyAwLjY3NjQ3IDQuNTAwOCAxLjAwMiA2LjczMjQgMS4wMDIgOS45NzIxIDAgMTkuMTY1LTYuNTE1MyAyMi4xODItMTYuNDY3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMC4wMDIgLTAuMDAybDY5Ljk4OC0yMzEuMDdjMy42OTA4LTEyLjE4MS0zLjI4NTItMjUuMjIzLTE1LjQ2Ny0yOC45MTItMi4yODE0LTAuNjkyMzEtNC42MTA4LTEuMDE4OS02Ljg5ODQtMS4wMDJ6bS0yMTcuMjkgNDIuMjNjLTEyLjcyOS0wLjAwMDg3LTIzLjE4OCAxMC40NTYtMjMuMTg4IDIzLjE4NiAwLjAwMSAxMi43MjggMTAuNDU5IDIzLjE4NiAyMy4xODggMjMuMTg2IDEyLjcyNy0wLjAwMSAyMy4xODMtMTAuNDU5IDIzLjE4NC0yMy4xODYgMC4wMDA4NzYtMTIuNzI4LTEwLjQ1Ni0yMy4xODUtMjMuMTg0LTIzLjE4NnptMCAxNDYuNjRjLTEyLjcyNy0wLjAwMDg3LTIzLjE4NiAxMC40NTUtMjMuMTg4IDIzLjE4NC0wLjAwMDg3MyAxMi43MjkgMTAuNDU4IDIzLjE4OCAyMy4xODggMjMuMTg4IDEyLjcyOC0wLjAwMSAyMy4xODQtMTAuNDYgMjMuMTg0LTIzLjE4OC0wLjAwMS0xMi43MjYtMTAuNDU3LTIzLjE4My0yMy4xODQtMjMuMTg0em0yNzAuNzkgNDIuMjExYy0xMi43MjcgMC0yMy4xODQgMTAuNDU3LTIzLjE4NCAyMy4xODRzMTAuNDU1IDIzLjE4OCAyMy4xODQgMjMuMTg4aDE1NC45OGMxMi43MjkgMCAyMy4xODYtMTAuNDYgMjMuMTg2LTIzLjE4OCAwLjAwMS0xMi43MjgtMTAuNDU4LTIzLjE4NC0yMy4xODYtMjMuMTg0eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzc2IDAgMCAxLjAzNzYgLTcuNTY3NiAtMTQuOTI1KSIgc3Ryb2tlLXdpZHRoPSIxLjI2OTMiLz48L2c+PC9zdmc+", - ruleChainTypes = {RuleChainType.SYSTEM, RuleChainType.EDGE} + ruleChainTypes = {RuleChainType.CORE, RuleChainType.EDGE} ) public class TbRestApiCallNode implements TbNode { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java index b361c5ed8e..3cccb2c2c5 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java @@ -33,6 +33,13 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration vm.edges.data.length) { - vm.theEdges.fetchMoreItems_(index); - return null; - } - var item = vm.edges.data[index]; - if (item) { - item.indexNumber = index + 1; - } - return item; - }, - - getLength: function () { - if (vm.edges.hasNext) { - return vm.edges.data.length + vm.edges.nextPageLink.limit; - } else { - return vm.edges.data.length; - } - }, - - fetchMoreItems_: function () { - if (vm.edges.hasNext && !vm.edges.pending) { - vm.edges.pending = true; - edgeService.getEdges(vm.edges.nextPageLink).then( - function success(edges) { - vm.edges.data = vm.edges.data.concat(edges.data); - vm.edges.nextPageLink = edges.nextPageLink; - vm.edges.hasNext = edges.hasNext; - if (vm.edges.hasNext) { - vm.edges.nextPageLink.limit = vm.edges.pageSize; - } - vm.edges.pending = false; - }, - function fail() { - vm.edges.hasNext = false; - vm.edges.pending = false; - }); - } - } - }; - - function cancel() { - $mdDialog.cancel(); - } - - function assign() { - var tasks = []; - for (var i=0;i 0; - } - - function toggleEdgeSelection($event, edge) { - $event.stopPropagation(); - if (vm.isEdgeSelected(edge)) { - vm.edges.selection = null; - } else { - vm.edges.selection = edge; - } - } - - function isEdgeSelected(edge) { - return vm.edges.selection != null && edge && - edge.id.id === vm.edges.selection.id.id; - } - - function searchEdgeTextUpdated() { - vm.edges = { - pageSize: vm.edges.pageSize, - data: [], - nextPageLink: { - limit: vm.edges.pageSize, - textSearch: vm.searchText - }, - selection: null, - hasNext: true, - pending: false - }; - } -} diff --git a/ui/src/app/asset/assign-to-edge.tpl.html b/ui/src/app/asset/assign-to-edge.tpl.html deleted file mode 100644 index 851809ba2d..0000000000 --- a/ui/src/app/asset/assign-to-edge.tpl.html +++ /dev/null @@ -1,76 +0,0 @@ - - -
- -
-

asset.assign-asset-to-edge

- - - - -
-
- - - -
-
- asset.assign-to-edge-text - - - - search - - - -
- edge.no-edges-text - - - - - {{ edge.name }} - - - -
-
-
-
- - - - {{ 'action.assign' | translate }} - - {{ 'action.cancel' | - translate }} - - -
-
\ No newline at end of file diff --git a/ui/src/app/dashboard/manage-assigned-edges.controller.js b/ui/src/app/dashboard/manage-assigned-edges.controller.js deleted file mode 100644 index aeda89034c..0000000000 --- a/ui/src/app/dashboard/manage-assigned-edges.controller.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright © 2016-2020 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*@ngInject*/ -export default function ManageAssignedEdgesToDashboardController($mdDialog, $q, types, dashboardService, actionType, dashboardIds, assignedEdges) { - - var vm = this; - - vm.types = types; - vm.actionType = actionType; - vm.dashboardIds = dashboardIds; - vm.assignedEdges = assignedEdges; - if (actionType != 'manage') { - vm.assignedEdges = []; - } - - if (actionType == 'manage') { - vm.titleText = 'dashboard.manage-assigned-edges'; - vm.labelText = 'dashboard.assigned-edges'; - vm.actionName = 'action.update'; - } else if (actionType == 'assign') { - vm.titleText = 'dashboard.assign-to-edges'; - vm.labelText = 'dashboard.assign-to-edges-text'; - vm.actionName = 'action.assign'; - } else if (actionType == 'unassign') { - vm.titleText = 'dashboard.unassign-from-edges'; - vm.labelText = 'dashboard.unassign-from-edges-text'; - vm.actionName = 'action.unassign'; - } - - vm.submit = submit; - vm.cancel = cancel; - - function cancel () { - $mdDialog.cancel(); - } - - function submit () { - var tasks = []; - for (var i=0;i - -
- -
-

{{vm.titleText}}

- - - - -
-
- - - -
-
- {{vm.labelText}} - -
-
-
- - - - {{ vm.actionName | translate }} - - {{ 'action.cancel' | - translate }} - - -
-
\ No newline at end of file diff --git a/ui/src/app/device/assign-to-edge.controller.js b/ui/src/app/device/assign-to-edge.controller.js deleted file mode 100644 index 1cc4da732d..0000000000 --- a/ui/src/app/device/assign-to-edge.controller.js +++ /dev/null @@ -1,123 +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. - */ -/*@ngInject*/ -export default function AssignDeviceToEdgeController(edgeService, deviceService, $mdDialog, $q, deviceIds, edges) { - - var vm = this; - - vm.edges = edges; - vm.searchText = ''; - - vm.assign = assign; - vm.cancel = cancel; - vm.isEdgeSelected = isEdgeSelected; - vm.hasData = hasData; - vm.noData = noData; - vm.searchEdgeTextUpdated = searchEdgeTextUpdated; - vm.toggleEdgeSelection = toggleEdgeSelection; - - vm.theEdges = { - getItemAtIndex: function (index) { - if (index > vm.edges.data.length) { - vm.theEdges.fetchMoreItems_(index); - return null; - } - var item = vm.edges.data[index]; - if (item) { - item.indexNumber = index + 1; - } - return item; - }, - - getLength: function () { - if (vm.edges.hasNext) { - return vm.edges.data.length + vm.edges.nextPageLink.limit; - } else { - return vm.edges.data.length; - } - }, - - fetchMoreItems_: function () { - if (vm.edges.hasNext && !vm.edges.pending) { - vm.edges.pending = true; - edgeService.getEdges(vm.edges.nextPageLink).then( - function success(edges) { - vm.edges.data = vm.edges.data.concat(edges.data); - vm.edges.nextPageLink = edges.nextPageLink; - vm.edges.hasNext = edges.hasNext; - if (vm.edges.hasNext) { - vm.edges.nextPageLink.limit = vm.edges.pageSize; - } - vm.edges.pending = false; - }, - function fail() { - vm.edges.hasNext = false; - vm.edges.pending = false; - }); - } - } - }; - - function cancel() { - $mdDialog.cancel(); - } - - function assign() { - var tasks = []; - for (var i=0;i 0; - } - - function toggleEdgeSelection($event, edge) { - $event.stopPropagation(); - if (vm.isEdgeSelected(edge)) { - vm.edges.selection = null; - } else { - vm.edges.selection = edge; - } - } - - function isEdgeSelected(edge) { - return vm.edges.selection != null && edge && - edge.id.id === vm.edges.selection.id.id; - } - - function searchEdgeTextUpdated() { - vm.edges = { - pageSize: vm.edges.pageSize, - data: [], - nextPageLink: { - limit: vm.edges.pageSize, - textSearch: vm.searchText - }, - selection: null, - hasNext: true, - pending: false - }; - } -} diff --git a/ui/src/app/device/assign-to-edge.tpl.html b/ui/src/app/device/assign-to-edge.tpl.html deleted file mode 100644 index 402b76540e..0000000000 --- a/ui/src/app/device/assign-to-edge.tpl.html +++ /dev/null @@ -1,76 +0,0 @@ - - -
- -
-

device.assign-device-to-edge

- - - - -
-
- - - -
-
- device.assign-to-edge-text - - - - search - - - -
- edge.no-edges-text - - - - - {{ edge.name }} - - - -
-
-
-
- - - - {{ 'action.assign' | translate }} - - {{ 'action.cancel' | - translate }} - - -
-
\ No newline at end of file diff --git a/ui/src/app/edge/edge.controller.js b/ui/src/app/edge/edge.controller.js index 3afa5323ef..51269ea753 100644 --- a/ui/src/app/edge/edge.controller.js +++ b/ui/src/app/edge/edge.controller.js @@ -577,7 +577,6 @@ export function EdgeController($rootScope, userService, edgeService, customerSer }); } - function assignEdgesToCustomer($event, items) { var edgeIds = []; for (var id in items.selections) { @@ -667,7 +666,6 @@ export function EdgeController($rootScope, userService, edgeService, customerSer $state.go('home.edges.ruleChains', {edgeId: edge.id.id}); } - function openEdgeAssets($event, edge) { if ($event) { $event.stopPropagation(); diff --git a/ui/src/app/edge/edge.routes.js b/ui/src/app/edge/edge.routes.js index c7b7e45752..1afd319609 100644 --- a/ui/src/app/edge/edge.routes.js +++ b/ui/src/app/edge/edge.routes.js @@ -16,6 +16,11 @@ /* eslint-disable import/no-unresolved, import/default */ import edgesTemplate from './edges.tpl.html'; +import entityViewsTemplate from "../entity-view/entity-views.tpl.html"; +import devicesTemplate from "../device/devices.tpl.html"; +import assetsTemplate from "../asset/assets.tpl.html"; +import dashboardsTemplate from "../dashboard/dashboards.tpl.html"; +import dashboardTemplate from "../dashboard/dashboard.tpl.html"; /* eslint-enable import/no-unresolved, import/default */ @@ -45,27 +50,116 @@ export default function EdgeRoutes($stateProvider, types) { label: '{"icon": "transform", "label": "edge.edges"}' } }) - .state('home.customers.edges', { - url: '/:customerId/edges', + .state('home.edges.entityViews', { + url: '/:edgeId/entityViews', params: {'topIndex': 0}, module: 'private', auth: ['TENANT_ADMIN'], views: { "content@home": { - templateUrl: edgesTemplate, + templateUrl: entityViewsTemplate, controllerAs: 'vm', - controller: 'EdgeController' + controller: 'EntityViewController' } }, data: { - edgesType: 'customer', + entityViewsType: 'edge', searchEnabled: true, searchByEntitySubtype: true, - searchEntityType: types.entityType.edge, - pageTitle: 'customer.edges' + searchEntityType: types.entityType.entityView, + pageTitle: 'edge.entity-views' + }, + ncyBreadcrumb: { + label: '{"icon": "view_quilt", "label": "edge.entity-views"}' + } + }) + .state('home.edges.devices', { + url: '/:edgeId/devices', + params: {'topIndex': 0}, + module: 'private', + auth: ['TENANT_ADMIN'], + views: { + "content@home": { + templateUrl: devicesTemplate, + controllerAs: 'vm', + controller: 'DeviceController' + } + }, + data: { + devicesType: 'edge', + searchEnabled: true, + searchByEntitySubtype: true, + searchEntityType: types.entityType.device, + pageTitle: 'edge.devices' + }, + ncyBreadcrumb: { + label: '{"icon": "devices_other", "label": "edge.devices"}' + } + }) + .state('home.edges.assets', { + url: '/:edgeId/assets', + params: {'topIndex': 0}, + module: 'private', + auth: ['TENANT_ADMIN'], + views: { + "content@home": { + templateUrl: assetsTemplate, + controllerAs: 'vm', + controller: 'AssetController' + } + }, + data: { + assetsType: 'edge', + searchEnabled: true, + searchByEntitySubtype: true, + searchEntityType: types.entityType.asset, + pageTitle: 'edge.assets' + }, + ncyBreadcrumb: { + label: '{"icon": "domain", "label": "edge.assets"}' + } + }) + .state('home.edges.dashboards', { + url: '/:edgeId/dashboards', + params: {'topIndex': 0}, + module: 'private', + auth: ['TENANT_ADMIN'], + views: { + "content@home": { + templateUrl: dashboardsTemplate, + controllerAs: 'vm', + controller: 'DashboardsController' + } + }, + data: { + dashboardsType: 'edge', + searchEnabled: true, + pageTitle: 'edge.dashboards' + }, + ncyBreadcrumb: { + label: '{"icon": "dashboard", "label": "edge.dashboards"}' + } + }) + .state('home.edges.dashboards.dashboard', { + url: '/:dashboardId?state', + reloadOnSearch: false, + module: 'private', + auth: ['TENANT_ADMIN', 'CUSTOMER_USER'], + views: { + "content@home": { + templateUrl: dashboardTemplate, + controller: 'DashboardController', + controllerAs: 'vm' + } + }, + data: { + widgetEditMode: false, + searchEnabled: false, + pageTitle: 'dashboard.dashboard', + dashboardsType: 'edge', }, ncyBreadcrumb: { - label: '{"icon": "transform", "label": "{{ vm.customerEdgesTitle }}", "translate": "false"}' + label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}' } }); } diff --git a/ui/src/app/entity-view/assign-to-edge.controller.js b/ui/src/app/entity-view/assign-to-edge.controller.js deleted file mode 100644 index 90d0383743..0000000000 --- a/ui/src/app/entity-view/assign-to-edge.controller.js +++ /dev/null @@ -1,123 +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. - */ -/*@ngInject*/ -export default function AssignEntityViewToEdgeController(edgeService, entityViewService, $mdDialog, $q, entityViewIds, edges) { - - var vm = this; - - vm.edges = edges; - vm.searchText = ''; - - vm.assign = assign; - vm.cancel = cancel; - vm.isEdgeSelected = isEdgeSelected; - vm.hasData = hasData; - vm.noData = noData; - vm.searchEdgeTextUpdated = searchEdgeTextUpdated; - vm.toggleEdgeSelection = toggleEdgeSelection; - - vm.theEdges = { - getItemAtIndex: function (index) { - if (index > vm.edges.data.length) { - vm.theEdges.fetchMoreItems_(index); - return null; - } - var item = vm.edges.data[index]; - if (item) { - item.indexNumber = index + 1; - } - return item; - }, - - getLength: function () { - if (vm.edges.hasNext) { - return vm.edges.data.length + vm.edges.nextPageLink.limit; - } else { - return vm.edges.data.length; - } - }, - - fetchMoreItems_: function () { - if (vm.edges.hasNext && !vm.edges.pending) { - vm.edges.pending = true; - edgeService.getEdges(vm.edges.nextPageLink).then( - function success(edges) { - vm.edges.data = vm.edges.data.concat(edges.data); - vm.edges.nextPageLink = edges.nextPageLink; - vm.edges.hasNext = edges.hasNext; - if (vm.edges.hasNext) { - vm.edges.nextPageLink.limit = vm.edges.pageSize; - } - vm.edges.pending = false; - }, - function fail() { - vm.edges.hasNext = false; - vm.edges.pending = false; - }); - } - } - }; - - function cancel() { - $mdDialog.cancel(); - } - - function assign() { - var tasks = []; - for (var i=0; i < entityViewIds.length;i++) { - tasks.push(entityViewService.assignEntityViewToEdge(vm.edges.selection.id.id, entityViewIds[i])); - } - $q.all(tasks).then(function () { - $mdDialog.hide(); - }); - } - - function noData() { - return vm.edges.data.length == 0 && !vm.edges.hasNext; - } - - function hasData() { - return vm.edges.data.length > 0; - } - - function toggleEdgeSelection($event, edge) { - $event.stopPropagation(); - if (vm.isEdgeSelected(edge)) { - vm.edges.selection = null; - } else { - vm.edges.selection = edge; - } - } - - function isEdgeSelected(edge) { - return vm.edges.selection != null && edge && - edge.id.id === vm.edges.selection.id.id; - } - - function searchEdgeTextUpdated() { - vm.edges = { - pageSize: vm.edges.pageSize, - data: [], - nextPageLink: { - limit: vm.edges.pageSize, - textSearch: vm.searchText - }, - selection: null, - hasNext: true, - pending: false - }; - } -} diff --git a/ui/src/app/entity-view/assign-to-edge.tpl.html b/ui/src/app/entity-view/assign-to-edge.tpl.html deleted file mode 100644 index bf46c93238..0000000000 --- a/ui/src/app/entity-view/assign-to-edge.tpl.html +++ /dev/null @@ -1,76 +0,0 @@ - - -
- -
-

entity-view.assign-entity-view-to-edge

- - - - -
-
- - - -
-
- entity-view.assign-to-edge-text - - - - search - - - -
- edge.no-edges-text - - - - - {{ edge.name }} - - - -
-
-
-
- - - - {{ 'action.assign' | translate }} - - {{ 'action.cancel' | - translate }} - - -
-
\ No newline at end of file diff --git a/ui/src/app/rulechain/manage-assigned-edges.controller.js b/ui/src/app/rulechain/manage-assigned-edges.controller.js deleted file mode 100644 index f9a46c0d2b..0000000000 --- a/ui/src/app/rulechain/manage-assigned-edges.controller.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright © 2016-2020 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/*@ngInject*/ -export default function ManageAssignedEdgesToRuleChainController($mdDialog, $q, types, ruleChainService, actionType, ruleChainIds, assignedEdges) { - - var vm = this; - - vm.types = types; - vm.actionType = actionType; - vm.ruleChainIds = ruleChainIds; - vm.assignedEdges = assignedEdges; - if (actionType != 'manage') { - vm.assignedEdges = []; - } - - if (actionType == 'manage') { - vm.titleText = 'rulechain.manage-assigned-edges'; - vm.labelText = 'rulechain.assigned-edges'; - vm.actionName = 'action.update'; - } else if (actionType == 'assign') { - vm.titleText = 'rulechain.assign-to-edges'; - vm.labelText = 'rulechain.assign-to-edges-text'; - vm.actionName = 'action.assign'; - } else if (actionType == 'unassign') { - vm.titleText = 'rulechain.unassign-from-edges'; - vm.labelText = 'rulechain.unassign-from-edges-text'; - vm.actionName = 'action.unassign'; - } - - vm.submit = submit; - vm.cancel = cancel; - - function cancel () { - $mdDialog.cancel(); - } - - function submit () { - var tasks = []; - for (var i=0;i - -
- -
-

{{vm.titleText}}

- - - - -
-
- - - -
-
- {{vm.labelText}} - -
-
-
- - - - {{ vm.actionName | translate }} - - {{ 'action.cancel' | - translate }} - - -
-
\ No newline at end of file diff --git a/ui/src/app/rulechain/rulechain-card.scss b/ui/src/app/rulechain/rulechain-card.scss deleted file mode 100644 index 86e86680bd..0000000000 --- a/ui/src/app/rulechain/rulechain-card.scss +++ /dev/null @@ -1,25 +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. - */ -.tb-rule-chain-assigned-edges { - display: block; - display: -webkit-box; /* stylelint-disable-line value-no-vendor-prefix */ - height: 34px; - margin-bottom: 4px; - overflow: hidden; - text-overflow: ellipsis; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; /* stylelint-disable-line property-no-vendor-prefix */ -}