Commit e0f978992e67b1bf6738c3688c013070f41b67f1

Authored by Igor Kulikov
Committed by GitHub
2 parents dc875508 4e5c726e

Merge pull request #3811 from volodymyr-babak/develop/3.3-edge

Edge functionality
Showing 44 changed files with 3444 additions and 76 deletions

Too many changes to show.

To preserve performance only 44 of 371 files are displayed.

@@ -33,5 +33,6 @@ pom.xml.versionsBackup @@ -33,5 +33,6 @@ pom.xml.versionsBackup
33 **/.env 33 **/.env
34 .instance_id 34 .instance_id
35 rebuild-docker.sh 35 rebuild-docker.sh
  36 +*/.run/**
36 .run/** 37 .run/**
37 .run 38 .run
@@ -102,6 +102,10 @@ @@ -102,6 +102,10 @@
102 <artifactId>stats</artifactId> 102 <artifactId>stats</artifactId>
103 </dependency> 103 </dependency>
104 <dependency> 104 <dependency>
  105 + <groupId>org.thingsboard.common</groupId>
  106 + <artifactId>edge-api</artifactId>
  107 + </dependency>
  108 + <dependency>
105 <groupId>org.thingsboard</groupId> 109 <groupId>org.thingsboard</groupId>
106 <artifactId>dao</artifactId> 110 <artifactId>dao</artifactId>
107 <type>test-jar</type> 111 <type>test-jar</type>
  1 +{
  2 + "ruleChain": {
  3 + "additionalInfo": null,
  4 + "name": "Edge Root Rule Chain",
  5 + "type": "EDGE",
  6 + "firstRuleNodeId": null,
  7 + "root": true,
  8 + "debugMode": false,
  9 + "configuration": null
  10 + },
  11 + "metadata": {
  12 + "firstNodeIndex": 0,
  13 + "nodes": [
  14 + {
  15 + "additionalInfo": {
  16 + "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
  17 + "layoutX": 203,
  18 + "layoutY": 259
  19 + },
  20 + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
  21 + "name": "Device Profile Node",
  22 + "debugMode": false,
  23 + "configuration": {
  24 + "persistAlarmRulesState": false,
  25 + "fetchAlarmRulesStateOnStart": false
  26 + }
  27 + },
  28 + {
  29 + "additionalInfo": {
  30 + "layoutX": 823,
  31 + "layoutY": 157
  32 + },
  33 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
  34 + "name": "Save Timeseries",
  35 + "debugMode": false,
  36 + "configuration": {
  37 + "defaultTTL": 0
  38 + }
  39 + },
  40 + {
  41 + "additionalInfo": {
  42 + "layoutX": 824,
  43 + "layoutY": 52
  44 + },
  45 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
  46 + "name": "Save Client Attributes",
  47 + "debugMode": false,
  48 + "configuration": {
  49 + "scope": "CLIENT_SCOPE"
  50 + }
  51 + },
  52 + {
  53 + "additionalInfo": {
  54 + "layoutX": 347,
  55 + "layoutY": 149
  56 + },
  57 + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
  58 + "name": "Message Type Switch",
  59 + "debugMode": false,
  60 + "configuration": {
  61 + "version": 0
  62 + }
  63 + },
  64 + {
  65 + "additionalInfo": {
  66 + "layoutX": 825,
  67 + "layoutY": 266
  68 + },
  69 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  70 + "name": "Log RPC from Device",
  71 + "debugMode": false,
  72 + "configuration": {
  73 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  74 + }
  75 + },
  76 + {
  77 + "additionalInfo": {
  78 + "layoutX": 824,
  79 + "layoutY": 378
  80 + },
  81 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  82 + "name": "Log Other",
  83 + "debugMode": false,
  84 + "configuration": {
  85 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  86 + }
  87 + },
  88 + {
  89 + "additionalInfo": {
  90 + "layoutX": 824,
  91 + "layoutY": 466
  92 + },
  93 + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
  94 + "name": "RPC Call Request",
  95 + "debugMode": false,
  96 + "configuration": {
  97 + "timeoutInSeconds": 60
  98 + }
  99 + },
  100 + {
  101 + "additionalInfo": {
  102 + "layoutX": 1134,
  103 + "layoutY": 132
  104 + },
  105 + "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
  106 + "name": "Push to cloud",
  107 + "debugMode": false,
  108 + "configuration": {
  109 + "version": 0
  110 + }
  111 + }
  112 + ],
  113 + "connections": [
  114 + {
  115 + "fromIndex": 0,
  116 + "toIndex": 3,
  117 + "type": "Success"
  118 + },
  119 + {
  120 + "fromIndex": 1,
  121 + "toIndex": 7,
  122 + "type": "Success"
  123 + },
  124 + {
  125 + "fromIndex": 2,
  126 + "toIndex": 7,
  127 + "type": "Success"
  128 + },
  129 + {
  130 + "fromIndex": 3,
  131 + "toIndex": 6,
  132 + "type": "RPC Request to Device"
  133 + },
  134 + {
  135 + "fromIndex": 3,
  136 + "toIndex": 5,
  137 + "type": "Other"
  138 + },
  139 + {
  140 + "fromIndex": 3,
  141 + "toIndex": 2,
  142 + "type": "Post attributes"
  143 + },
  144 + {
  145 + "fromIndex": 3,
  146 + "toIndex": 1,
  147 + "type": "Post telemetry"
  148 + },
  149 + {
  150 + "fromIndex": 3,
  151 + "toIndex": 4,
  152 + "type": "RPC Request from Device"
  153 + },
  154 + {
  155 + "fromIndex": 4,
  156 + "toIndex": 7,
  157 + "type": "Success"
  158 + }
  159 + ],
  160 + "ruleChainConnections": null
  161 + }
  162 +}
  1 +{
  2 + "widgetsBundle": {
  3 + "alias": "edge_widgets",
  4 + "title": "Edge widgets",
  5 + "image": null
  6 + },
  7 + "widgetTypes": [
  8 + {
  9 + "alias": "edges_overview",
  10 + "name": "Edge Quick Overview",
  11 + "descriptor": {
  12 + "type": "latest",
  13 + "sizeX": 7.5,
  14 + "sizeY": 5,
  15 + "resources": [],
  16 + "templateHtml": "<tb-edges-overview-widget \n [ctx]=\"ctx\">\n</tb-edges-overview-widget>",
  17 + "templateCss": "",
  18 + "controllerScript": "self.onInit = function() {\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.onDestroy = function() {\n};\n",
  19 + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EdgeOverviewSettings\",\n \"properties\": {\n \"enableDefaultTitle\": {\n \"title\": \"Display default title\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"enableDefaultTitle\"\n ]\n}",
  20 + "dataKeySettingsSchema": "{}\n",
  21 + "defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"showTitleIcon\":true,\"titleIcon\":\"router\",\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{},\"title\":\"Edge Quick Overview\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"widgetStyle\":{},\"actions\":{}}"
  22 + }
  23 + }
  24 + ]
  25 +}
  1 +{
  2 + "ruleChain": {
  3 + "additionalInfo": null,
  4 + "name": "Edge Root Rule Chain",
  5 + "type": "EDGE",
  6 + "firstRuleNodeId": null,
  7 + "root": true,
  8 + "debugMode": false,
  9 + "configuration": null
  10 + },
  11 + "metadata": {
  12 + "firstNodeIndex": 0,
  13 + "nodes": [
  14 + {
  15 + "additionalInfo": {
  16 + "description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
  17 + "layoutX": 203,
  18 + "layoutY": 259
  19 + },
  20 + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
  21 + "name": "Device Profile Node",
  22 + "debugMode": false,
  23 + "configuration": {
  24 + "persistAlarmRulesState": false,
  25 + "fetchAlarmRulesStateOnStart": false
  26 + }
  27 + },
  28 + {
  29 + "additionalInfo": {
  30 + "layoutX": 823,
  31 + "layoutY": 157
  32 + },
  33 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
  34 + "name": "Save Timeseries",
  35 + "debugMode": false,
  36 + "configuration": {
  37 + "defaultTTL": 0
  38 + }
  39 + },
  40 + {
  41 + "additionalInfo": {
  42 + "layoutX": 824,
  43 + "layoutY": 52
  44 + },
  45 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
  46 + "name": "Save Client Attributes",
  47 + "debugMode": false,
  48 + "configuration": {
  49 + "scope": "CLIENT_SCOPE"
  50 + }
  51 + },
  52 + {
  53 + "additionalInfo": {
  54 + "layoutX": 347,
  55 + "layoutY": 149
  56 + },
  57 + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
  58 + "name": "Message Type Switch",
  59 + "debugMode": false,
  60 + "configuration": {
  61 + "version": 0
  62 + }
  63 + },
  64 + {
  65 + "additionalInfo": {
  66 + "layoutX": 825,
  67 + "layoutY": 266
  68 + },
  69 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  70 + "name": "Log RPC from Device",
  71 + "debugMode": false,
  72 + "configuration": {
  73 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  74 + }
  75 + },
  76 + {
  77 + "additionalInfo": {
  78 + "layoutX": 824,
  79 + "layoutY": 378
  80 + },
  81 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  82 + "name": "Log Other",
  83 + "debugMode": false,
  84 + "configuration": {
  85 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  86 + }
  87 + },
  88 + {
  89 + "additionalInfo": {
  90 + "layoutX": 824,
  91 + "layoutY": 466
  92 + },
  93 + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
  94 + "name": "RPC Call Request",
  95 + "debugMode": false,
  96 + "configuration": {
  97 + "timeoutInSeconds": 60
  98 + }
  99 + },
  100 + {
  101 + "additionalInfo": {
  102 + "layoutX": 1134,
  103 + "layoutY": 132
  104 + },
  105 + "type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode",
  106 + "name": "Push to cloud",
  107 + "debugMode": false,
  108 + "configuration": {
  109 + "version": 0
  110 + }
  111 + }
  112 + ],
  113 + "connections": [
  114 + {
  115 + "fromIndex": 0,
  116 + "toIndex": 3,
  117 + "type": "Success"
  118 + },
  119 + {
  120 + "fromIndex": 1,
  121 + "toIndex": 7,
  122 + "type": "Success"
  123 + },
  124 + {
  125 + "fromIndex": 2,
  126 + "toIndex": 7,
  127 + "type": "Success"
  128 + },
  129 + {
  130 + "fromIndex": 3,
  131 + "toIndex": 6,
  132 + "type": "RPC Request to Device"
  133 + },
  134 + {
  135 + "fromIndex": 3,
  136 + "toIndex": 5,
  137 + "type": "Other"
  138 + },
  139 + {
  140 + "fromIndex": 3,
  141 + "toIndex": 2,
  142 + "type": "Post attributes"
  143 + },
  144 + {
  145 + "fromIndex": 3,
  146 + "toIndex": 1,
  147 + "type": "Post telemetry"
  148 + },
  149 + {
  150 + "fromIndex": 3,
  151 + "toIndex": 4,
  152 + "type": "RPC Request from Device"
  153 + },
  154 + {
  155 + "fromIndex": 4,
  156 + "toIndex": 7,
  157 + "type": "Success"
  158 + }
  159 + ],
  160 + "ruleChainConnections": null
  161 + }
  162 +}
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 "ruleChain": { 2 "ruleChain": {
3 "additionalInfo": null, 3 "additionalInfo": null,
4 "name": "Root Rule Chain", 4 "name": "Root Rule Chain",
  5 + "type": "CORE",
5 "firstRuleNodeId": null, 6 "firstRuleNodeId": null,
6 "root": true, 7 "root": true,
7 "debugMode": false, 8 "debugMode": false,
  1 +--
  2 +-- Copyright © 2016-2021 The Thingsboard Authors
  3 +--
  4 +-- Licensed under the Apache License, Version 2.0 (the "License");
  5 +-- you may not use this file except in compliance with the License.
  6 +-- You may obtain a copy of the License at
  7 +--
  8 +-- http://www.apache.org/licenses/LICENSE-2.0
  9 +--
  10 +-- Unless required by applicable law or agreed to in writing, software
  11 +-- distributed under the License is distributed on an "AS IS" BASIS,
  12 +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +-- See the License for the specific language governing permissions and
  14 +-- limitations under the License.
  15 +--
  16 +
  17 +CREATE TABLE IF NOT EXISTS edge (
  18 + id uuid NOT NULL CONSTRAINT edge_pkey PRIMARY KEY,
  19 + created_time bigint NOT NULL,
  20 + additional_info varchar,
  21 + customer_id uuid,
  22 + root_rule_chain_id uuid,
  23 + type varchar(255),
  24 + name varchar(255),
  25 + label varchar(255),
  26 + routing_key varchar(255),
  27 + secret varchar(255),
  28 + edge_license_key varchar(30),
  29 + cloud_endpoint varchar(255),
  30 + search_text varchar(255),
  31 + tenant_id uuid,
  32 + CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name),
  33 + CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key)
  34 +);
  35 +
  36 +CREATE TABLE IF NOT EXISTS edge_event (
  37 + id uuid NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY,
  38 + created_time bigint NOT NULL,
  39 + edge_id uuid,
  40 + edge_event_type varchar(255),
  41 + edge_event_uid varchar(255),
  42 + entity_id uuid,
  43 + edge_event_action varchar(255),
  44 + body varchar(10000000),
  45 + tenant_id uuid,
  46 + ts bigint NOT NULL
  47 +);
  1 +--
  2 +-- Copyright © 2016-2021 The Thingsboard Authors
  3 +--
  4 +-- Licensed under the Apache License, Version 2.0 (the "License");
  5 +-- you may not use this file except in compliance with the License.
  6 +-- You may obtain a copy of the License at
  7 +--
  8 +-- http://www.apache.org/licenses/LICENSE-2.0
  9 +--
  10 +-- Unless required by applicable law or agreed to in writing, software
  11 +-- distributed under the License is distributed on an "AS IS" BASIS,
  12 +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +-- See the License for the specific language governing permissions and
  14 +-- limitations under the License.
  15 +--
  16 +
  17 +CREATE OR REPLACE PROCEDURE cleanup_edge_events_by_ttl(IN ttl bigint, INOUT deleted bigint)
  18 + LANGUAGE plpgsql AS
  19 +$$
  20 +DECLARE
  21 + ttl_ts bigint;
  22 + ttl_deleted_count bigint DEFAULT 0;
  23 +BEGIN
  24 + IF ttl > 0 THEN
  25 + ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint;
  26 + EXECUTE format(
  27 + 'WITH deleted AS (DELETE FROM edge_event WHERE ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', ttl_ts) into ttl_deleted_count;
  28 + END IF;
  29 + RAISE NOTICE 'Edge events removed by ttl: %', ttl_deleted_count;
  30 + deleted := ttl_deleted_count;
  31 +END
  32 +$$;
@@ -46,6 +46,7 @@ import org.thingsboard.server.common.msg.TbMsg; @@ -46,6 +46,7 @@ import org.thingsboard.server.common.msg.TbMsg;
46 import org.thingsboard.server.common.msg.queue.ServiceType; 46 import org.thingsboard.server.common.msg.queue.ServiceType;
47 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 47 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
48 import org.thingsboard.server.common.msg.tools.TbRateLimits; 48 import org.thingsboard.server.common.msg.tools.TbRateLimits;
  49 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
49 import org.thingsboard.server.dao.asset.AssetService; 50 import org.thingsboard.server.dao.asset.AssetService;
50 import org.thingsboard.server.dao.attributes.AttributesService; 51 import org.thingsboard.server.dao.attributes.AttributesService;
51 import org.thingsboard.server.dao.audit.AuditLogService; 52 import org.thingsboard.server.dao.audit.AuditLogService;
@@ -54,6 +55,8 @@ import org.thingsboard.server.dao.customer.CustomerService; @@ -54,6 +55,8 @@ import org.thingsboard.server.dao.customer.CustomerService;
54 import org.thingsboard.server.dao.dashboard.DashboardService; 55 import org.thingsboard.server.dao.dashboard.DashboardService;
55 import org.thingsboard.server.dao.device.ClaimDevicesService; 56 import org.thingsboard.server.dao.device.ClaimDevicesService;
56 import org.thingsboard.server.dao.device.DeviceService; 57 import org.thingsboard.server.dao.device.DeviceService;
  58 +import org.thingsboard.server.dao.edge.EdgeEventService;
  59 +import org.thingsboard.server.dao.edge.EdgeService;
57 import org.thingsboard.server.dao.entityview.EntityViewService; 60 import org.thingsboard.server.dao.entityview.EntityViewService;
58 import org.thingsboard.server.dao.event.EventService; 61 import org.thingsboard.server.dao.event.EventService;
59 import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; 62 import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor;
@@ -69,7 +72,7 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; @@ -69,7 +72,7 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
69 import org.thingsboard.server.queue.usagestats.TbApiUsageClient; 72 import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
70 import org.thingsboard.server.service.apiusage.TbApiUsageStateService; 73 import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
71 import org.thingsboard.server.service.component.ComponentDiscoveryService; 74 import org.thingsboard.server.service.component.ComponentDiscoveryService;
72 -import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; 75 +import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
73 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 76 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
74 import org.thingsboard.server.service.executors.ExternalCallExecutorService; 77 import org.thingsboard.server.service.executors.ExternalCallExecutorService;
75 import org.thingsboard.server.service.executors.SharedEventLoopGroupService; 78 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
@@ -296,6 +299,18 @@ public class ActorSystemContext { @@ -296,6 +299,18 @@ public class ActorSystemContext {
296 @Getter 299 @Getter
297 private TbCoreDeviceRpcService tbCoreDeviceRpcService; 300 private TbCoreDeviceRpcService tbCoreDeviceRpcService;
298 301
  302 + @Lazy
  303 + @Autowired(required = false)
  304 + @Getter private EdgeService edgeService;
  305 +
  306 + @Lazy
  307 + @Autowired(required = false)
  308 + @Getter private EdgeEventService edgeEventService;
  309 +
  310 + @Lazy
  311 + @Autowired(required = false)
  312 + @Getter private EdgeRpcService edgeRpcService;
  313 +
299 @Value("${actors.session.max_concurrent_sessions_per_device:1}") 314 @Value("${actors.session.max_concurrent_sessions_per_device:1}")
300 @Getter 315 @Getter
301 private long maxConcurrentSessionsPerDevice; 316 private long maxConcurrentSessionsPerDevice;
@@ -320,6 +335,9 @@ public class ActorSystemContext { @@ -320,6 +335,9 @@ public class ActorSystemContext {
320 @Getter 335 @Getter
321 private long statisticsPersistFrequency; 336 private long statisticsPersistFrequency;
322 337
  338 + @Value("${edges.enabled}")
  339 + @Getter
  340 + private boolean edgesEnabled;
323 341
324 @Scheduled(fixedDelayString = "${actors.statistics.js_print_interval_ms}") 342 @Scheduled(fixedDelayString = "${actors.statistics.js_print_interval_ms}")
325 public void printStats() { 343 public void printStats() {
@@ -35,10 +35,12 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -35,10 +35,12 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
35 import org.thingsboard.server.common.msg.MsgType; 35 import org.thingsboard.server.common.msg.MsgType;
36 import org.thingsboard.server.common.msg.TbActorMsg; 36 import org.thingsboard.server.common.msg.TbActorMsg;
37 import org.thingsboard.server.common.msg.aware.TenantAwareMsg; 37 import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
  38 +import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 39 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
39 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; 40 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
40 import org.thingsboard.server.common.msg.queue.RuleEngineException; 41 import org.thingsboard.server.common.msg.queue.RuleEngineException;
41 import org.thingsboard.server.common.msg.queue.ServiceType; 42 import org.thingsboard.server.common.msg.queue.ServiceType;
  43 +import org.thingsboard.server.dao.model.ModelConstants;
42 import org.thingsboard.server.dao.tenant.TenantService; 44 import org.thingsboard.server.dao.tenant.TenantService;
43 import org.thingsboard.server.dao.tenant.TbTenantProfileCache; 45 import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
44 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 46 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
@@ -89,10 +91,15 @@ public class AppActor extends ContextAwareActor { @@ -89,10 +91,15 @@ public class AppActor extends ContextAwareActor {
89 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: 91 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
90 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: 92 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
91 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: 93 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
  94 + case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
92 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: 95 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  96 + case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
93 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: 97 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
94 onToDeviceActorMsg((TenantAwareMsg) msg, true); 98 onToDeviceActorMsg((TenantAwareMsg) msg, true);
95 break; 99 break;
  100 + case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
  101 + onToTenantActorMsg((EdgeEventUpdateMsg) msg);
  102 + break;
96 default: 103 default:
97 return false; 104 return false;
98 } 105 }
@@ -192,6 +199,20 @@ public class AppActor extends ContextAwareActor { @@ -192,6 +199,20 @@ public class AppActor extends ContextAwareActor {
192 () -> new TenantActor.ActorCreator(systemContext, tenantId)); 199 () -> new TenantActor.ActorCreator(systemContext, tenantId));
193 } 200 }
194 201
  202 + private void onToTenantActorMsg(EdgeEventUpdateMsg msg) {
  203 + TbActorRef target = null;
  204 + if (ModelConstants.SYSTEM_TENANT.equals(msg.getTenantId())) {
  205 + log.warn("Message has system tenant id: {}", msg);
  206 + } else {
  207 + target = getOrCreateTenantActor(msg.getTenantId());
  208 + }
  209 + if (target != null) {
  210 + target.tellWithHighPriority(msg);
  211 + } else {
  212 + log.debug("[{}] Invalid edge event update msg: {}", msg.getTenantId(), msg);
  213 + }
  214 + }
  215 +
195 public static class ActorCreator extends ContextBasedCreator { 216 public static class ActorCreator extends ContextBasedCreator {
196 217
197 public ActorCreator(ActorSystemContext context) { 218 public ActorCreator(ActorSystemContext context) {
@@ -17,6 +17,7 @@ package org.thingsboard.server.actors.device; @@ -17,6 +17,7 @@ package org.thingsboard.server.actors.device;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; 19 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
  20 +import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
20 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; 21 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
21 import org.thingsboard.server.actors.ActorSystemContext; 22 import org.thingsboard.server.actors.ActorSystemContext;
22 import org.thingsboard.server.actors.TbActorCtx; 23 import org.thingsboard.server.actors.TbActorCtx;
@@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceId; @@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
26 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.msg.TbActorMsg; 28 import org.thingsboard.server.common.msg.TbActorMsg;
28 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; 29 import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
  30 +import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
29 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 31 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
30 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 32 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
31 33
@@ -70,12 +72,18 @@ public class DeviceActor extends ContextAwareActor { @@ -70,12 +72,18 @@ public class DeviceActor extends ContextAwareActor {
70 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: 72 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
71 processor.processRpcRequest(ctx, (ToDeviceRpcRequestActorMsg) msg); 73 processor.processRpcRequest(ctx, (ToDeviceRpcRequestActorMsg) msg);
72 break; 74 break;
  75 + case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  76 + processor.processRpcResponsesFromEdge(ctx, (FromDeviceRpcResponseActorMsg) msg);
  77 + break;
73 case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG: 78 case DEVICE_ACTOR_SERVER_SIDE_RPC_TIMEOUT_MSG:
74 processor.processServerSideRpcTimeout(ctx, (DeviceActorServerSideRpcTimeoutMsg) msg); 79 processor.processServerSideRpcTimeout(ctx, (DeviceActorServerSideRpcTimeoutMsg) msg);
75 break; 80 break;
76 case SESSION_TIMEOUT_MSG: 81 case SESSION_TIMEOUT_MSG:
77 processor.checkSessionsTimeout(); 82 processor.checkSessionsTimeout();
78 break; 83 break;
  84 + case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
  85 + processor.processEdgeUpdate((DeviceEdgeUpdateMsg) msg);
  86 + break;
79 default: 87 default:
80 return false; 88 return false;
81 } 89 }
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.actors.device; 16 package org.thingsboard.server.actors.device;
17 17
  18 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import com.google.common.util.concurrent.FutureCallback; 19 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.Futures; 20 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture; 21 import com.google.common.util.concurrent.ListenableFuture;
@@ -24,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; @@ -24,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
24 import org.apache.commons.collections.CollectionUtils; 25 import org.apache.commons.collections.CollectionUtils;
25 import org.thingsboard.rule.engine.api.RpcError; 26 import org.thingsboard.rule.engine.api.RpcError;
26 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; 27 import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
  28 +import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
27 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; 29 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
28 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; 30 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
29 import org.thingsboard.server.actors.ActorSystemContext; 31 import org.thingsboard.server.actors.ActorSystemContext;
@@ -31,11 +33,17 @@ import org.thingsboard.server.actors.TbActorCtx; @@ -31,11 +33,17 @@ import org.thingsboard.server.actors.TbActorCtx;
31 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; 33 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
32 import org.thingsboard.server.common.data.DataConstants; 34 import org.thingsboard.server.common.data.DataConstants;
33 import org.thingsboard.server.common.data.Device; 35 import org.thingsboard.server.common.data.Device;
  36 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  37 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  38 +import org.thingsboard.server.common.data.edge.EdgeEventType;
34 import org.thingsboard.server.common.data.id.DeviceId; 39 import org.thingsboard.server.common.data.id.DeviceId;
  40 +import org.thingsboard.server.common.data.id.EdgeId;
35 import org.thingsboard.server.common.data.id.TenantId; 41 import org.thingsboard.server.common.data.id.TenantId;
36 import org.thingsboard.server.common.data.kv.AttributeKey; 42 import org.thingsboard.server.common.data.kv.AttributeKey;
37 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 43 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
38 import org.thingsboard.server.common.data.kv.KvEntry; 44 import org.thingsboard.server.common.data.kv.KvEntry;
  45 +import org.thingsboard.server.common.data.relation.EntityRelation;
  46 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
39 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; 47 import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody;
40 import org.thingsboard.server.common.data.security.DeviceCredentials; 48 import org.thingsboard.server.common.data.security.DeviceCredentials;
41 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 49 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
@@ -68,6 +76,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceAct @@ -68,6 +76,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceAct
68 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; 76 import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto;
69 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; 77 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
70 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 78 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
  79 +import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg;
71 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; 80 import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg;
72 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 81 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
73 82
@@ -103,6 +112,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -103,6 +112,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
103 private String deviceName; 112 private String deviceName;
104 private String deviceType; 113 private String deviceType;
105 private TbMsgMetaData defaultMetaData; 114 private TbMsgMetaData defaultMetaData;
  115 + private EdgeId edgeId;
106 116
107 DeviceActorMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) { 117 DeviceActorMessageProcessor(ActorSystemContext systemContext, TenantId tenantId, DeviceId deviceId) {
108 super(systemContext); 118 super(systemContext);
@@ -125,12 +135,32 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -125,12 +135,32 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
125 this.defaultMetaData = new TbMsgMetaData(); 135 this.defaultMetaData = new TbMsgMetaData();
126 this.defaultMetaData.putValue("deviceName", deviceName); 136 this.defaultMetaData.putValue("deviceName", deviceName);
127 this.defaultMetaData.putValue("deviceType", deviceType); 137 this.defaultMetaData.putValue("deviceType", deviceType);
  138 + if (systemContext.isEdgesEnabled()) {
  139 + this.edgeId = findRelatedEdgeId();
  140 + }
128 return true; 141 return true;
129 } else { 142 } else {
130 return false; 143 return false;
131 } 144 }
132 } 145 }
133 146
  147 + private EdgeId findRelatedEdgeId() {
  148 + List<EntityRelation> result =
  149 + systemContext.getRelationService().findByToAndType(tenantId, deviceId, EntityRelation.EDGE_TYPE, RelationTypeGroup.COMMON);
  150 + if (result != null && result.size() > 0) {
  151 + EntityRelation relationToEdge = result.get(0);
  152 + if (relationToEdge.getFrom() != null && relationToEdge.getFrom().getId() != null) {
  153 + log.trace("[{}][{}] found edge [{}] for device", tenantId, deviceId, relationToEdge.getFrom().getId());
  154 + return new EdgeId(relationToEdge.getFrom().getId());
  155 + } else {
  156 + log.trace("[{}][{}] edge relation is empty {}", tenantId, deviceId, relationToEdge);
  157 + }
  158 + } else {
  159 + log.trace("[{}][{}] device doesn't have any related edge", tenantId, deviceId);
  160 + }
  161 + return null;
  162 + }
  163 +
134 void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) { 164 void processRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg) {
135 ToDeviceRpcRequest request = msg.getMsg(); 165 ToDeviceRpcRequest request = msg.getMsg();
136 ToDeviceRpcRequestBody body = request.getBody(); 166 ToDeviceRpcRequestBody body = request.getBody();
@@ -143,15 +173,22 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -143,15 +173,22 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
143 return; 173 return;
144 } 174 }
145 175
146 - boolean sent = rpcSubscriptions.size() > 0;  
147 - Set<UUID> syncSessionSet = new HashSet<>();  
148 - rpcSubscriptions.forEach((key, value) -> {  
149 - sendToTransport(rpcRequest, key, value.getNodeId());  
150 - if (SessionType.SYNC == value.getType()) {  
151 - syncSessionSet.add(key);  
152 - }  
153 - });  
154 - syncSessionSet.forEach(rpcSubscriptions::remove); 176 + boolean sent;
  177 + if (systemContext.isEdgesEnabled() && edgeId != null) {
  178 + log.debug("[{}][{}] device is related to edge [{}]. Saving RPC request to edge queue", tenantId, deviceId, edgeId.getId());
  179 + saveRpcRequestToEdgeQueue(request, rpcRequest.getRequestId());
  180 + sent = true;
  181 + } else {
  182 + sent = rpcSubscriptions.size() > 0;
  183 + Set<UUID> syncSessionSet = new HashSet<>();
  184 + rpcSubscriptions.forEach((key, value) -> {
  185 + sendToTransport(rpcRequest, key, value.getNodeId());
  186 + if (SessionType.SYNC == value.getType()) {
  187 + syncSessionSet.add(key);
  188 + }
  189 + });
  190 + syncSessionSet.forEach(rpcSubscriptions::remove);
  191 + }
155 192
156 if (request.isOneway() && sent) { 193 if (request.isOneway() && sent) {
157 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId()); 194 log.debug("[{}] Rpc command response sent [{}]!", deviceId, request.getId());
@@ -166,6 +203,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -166,6 +203,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
166 } 203 }
167 } 204 }
168 205
  206 + void processRpcResponsesFromEdge(TbActorCtx context, FromDeviceRpcResponseActorMsg responseMsg) {
  207 + log.debug("[{}] Processing rpc command response from edge session", deviceId);
  208 + ToDeviceRpcRequestMetadata requestMd = toDeviceRpcPendingMap.remove(responseMsg.getRequestId());
  209 + boolean success = requestMd != null;
  210 + if (success) {
  211 + systemContext.getTbCoreDeviceRpcService().processRpcResponseFromDeviceActor(responseMsg.getMsg());
  212 + } else {
  213 + log.debug("[{}] Rpc command response [{}] is stale!", deviceId, responseMsg.getRequestId());
  214 + }
  215 + }
  216 +
169 private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) { 217 private void registerPendingRpcRequest(TbActorCtx context, ToDeviceRpcRequestActorMsg msg, boolean sent, ToDeviceRpcRequestMsg rpcRequest, long timeout) {
170 toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent)); 218 toDeviceRpcPendingMap.put(rpcRequest.getRequestId(), new ToDeviceRpcRequestMetadata(msg, sent));
171 DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout); 219 DeviceActorServerSideRpcTimeoutMsg timeoutMsg = new DeviceActorServerSideRpcTimeoutMsg(rpcRequest.getRequestId(), timeout);
@@ -498,6 +546,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -498,6 +546,11 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
498 this.defaultMetaData.putValue("deviceType", deviceType); 546 this.defaultMetaData.putValue("deviceType", deviceType);
499 } 547 }
500 548
  549 + void processEdgeUpdate(DeviceEdgeUpdateMsg msg) {
  550 + log.trace("[{}] Processing edge update {}", deviceId, msg);
  551 + this.edgeId = msg.getEdgeId();
  552 + }
  553 +
501 private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) { 554 private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) {
502 ToTransportMsg msg = ToTransportMsg.newBuilder() 555 ToTransportMsg msg = ToTransportMsg.newBuilder()
503 .setSessionIdMSB(sessionInfo.getSessionIdMSB()) 556 .setSessionIdMSB(sessionInfo.getSessionIdMSB())
@@ -530,6 +583,36 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -530,6 +583,36 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
530 systemContext.getTbCoreToTransportService().process(nodeId, msg); 583 systemContext.getTbCoreToTransportService().process(nodeId, msg);
531 } 584 }
532 585
  586 + private void saveRpcRequestToEdgeQueue(ToDeviceRpcRequest msg, Integer requestId) {
  587 + EdgeEvent edgeEvent = new EdgeEvent();
  588 + edgeEvent.setTenantId(tenantId);
  589 + edgeEvent.setAction(EdgeEventActionType.RPC_CALL);
  590 + edgeEvent.setEntityId(deviceId.getId());
  591 + edgeEvent.setType(EdgeEventType.DEVICE);
  592 +
  593 + ObjectNode body = mapper.createObjectNode();
  594 + body.put("requestId", requestId);
  595 + body.put("requestUUID", msg.getId().toString());
  596 + body.put("oneway", msg.isOneway());
  597 + body.put("expirationTime", msg.getExpirationTime());
  598 + body.put("method", msg.getBody().getMethod());
  599 + body.put("params", msg.getBody().getParams());
  600 + edgeEvent.setBody(body);
  601 +
  602 + edgeEvent.setEdgeId(edgeId);
  603 + ListenableFuture<EdgeEvent> future = systemContext.getEdgeEventService().saveAsync(edgeEvent);
  604 + Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
  605 + @Override
  606 + public void onSuccess( EdgeEvent result) {
  607 + systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
  608 + }
  609 +
  610 + @Override
  611 + public void onFailure(Throwable t) {
  612 + log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
  613 + }
  614 + }, systemContext.getDbCallbackExecutor());
  615 + }
533 616
534 private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) { 617 private List<TsKvProto> toTsKvProtos(@Nullable List<AttributeKvEntry> result) {
535 List<TsKvProto> clientAttributes; 618 List<TsKvProto> clientAttributes;
@@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.TenantProfile; @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.TenantProfile;
42 import org.thingsboard.server.common.data.alarm.Alarm; 42 import org.thingsboard.server.common.data.alarm.Alarm;
43 import org.thingsboard.server.common.data.asset.Asset; 43 import org.thingsboard.server.common.data.asset.Asset;
44 import org.thingsboard.server.common.data.id.DeviceId; 44 import org.thingsboard.server.common.data.id.DeviceId;
  45 +import org.thingsboard.server.common.data.id.EdgeId;
45 import org.thingsboard.server.common.data.id.EntityId; 46 import org.thingsboard.server.common.data.id.EntityId;
46 import org.thingsboard.server.common.data.id.RuleChainId; 47 import org.thingsboard.server.common.data.id.RuleChainId;
47 import org.thingsboard.server.common.data.id.RuleNodeId; 48 import org.thingsboard.server.common.data.id.RuleNodeId;
@@ -62,6 +63,8 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster; @@ -62,6 +63,8 @@ import org.thingsboard.server.dao.cassandra.CassandraCluster;
62 import org.thingsboard.server.dao.customer.CustomerService; 63 import org.thingsboard.server.dao.customer.CustomerService;
63 import org.thingsboard.server.dao.dashboard.DashboardService; 64 import org.thingsboard.server.dao.dashboard.DashboardService;
64 import org.thingsboard.server.dao.device.DeviceService; 65 import org.thingsboard.server.dao.device.DeviceService;
  66 +import org.thingsboard.server.dao.edge.EdgeEventService;
  67 +import org.thingsboard.server.dao.edge.EdgeService;
65 import org.thingsboard.server.dao.entityview.EntityViewService; 68 import org.thingsboard.server.dao.entityview.EntityViewService;
66 import org.thingsboard.server.dao.nosql.CassandraStatementTask; 69 import org.thingsboard.server.dao.nosql.CassandraStatementTask;
67 import org.thingsboard.server.dao.nosql.TbResultSetFuture; 70 import org.thingsboard.server.dao.nosql.TbResultSetFuture;
@@ -319,6 +322,11 @@ class DefaultTbContext implements TbContext { @@ -319,6 +322,11 @@ class DefaultTbContext implements TbContext {
319 return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueName, ruleChainId); 322 return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueName, ruleChainId);
320 } 323 }
321 324
  325 + @Override
  326 + public void onEdgeEventUpdate(TenantId tenantId, EdgeId edgeId) {
  327 + mainCtx.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
  328 + }
  329 +
322 public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) { 330 public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) {
323 return entityActionMsg(entity, id, ruleNodeId, action, ServiceQueue.MAIN, null); 331 return entityActionMsg(entity, id, ruleNodeId, action, ServiceQueue.MAIN, null);
324 } 332 }
@@ -478,6 +486,16 @@ class DefaultTbContext implements TbContext { @@ -478,6 +486,16 @@ class DefaultTbContext implements TbContext {
478 } 486 }
479 487
480 @Override 488 @Override
  489 + public EdgeService getEdgeService() {
  490 + return mainCtx.getEdgeService();
  491 + }
  492 +
  493 + @Override
  494 + public EdgeEventService getEdgeEventService() {
  495 + return mainCtx.getEdgeEventService();
  496 + }
  497 +
  498 + @Override
481 public EventLoopGroup getSharedEventLoop() { 499 public EventLoopGroup getSharedEventLoop() {
482 return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup(); 500 return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();
483 } 501 }
@@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
32 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; 32 import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
33 import org.thingsboard.server.common.data.relation.EntityRelation; 33 import org.thingsboard.server.common.data.relation.EntityRelation;
34 import org.thingsboard.server.common.data.rule.RuleChain; 34 import org.thingsboard.server.common.data.rule.RuleChain;
  35 +import org.thingsboard.server.common.data.rule.RuleChainType;
35 import org.thingsboard.server.common.data.rule.RuleNode; 36 import org.thingsboard.server.common.data.rule.RuleNode;
36 import org.thingsboard.server.common.msg.TbMsg; 37 import org.thingsboard.server.common.msg.TbMsg;
37 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
@@ -99,7 +100,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -99,7 +100,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
99 public void start(TbActorCtx context) { 100 public void start(TbActorCtx context) {
100 if (!started) { 101 if (!started) {
101 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); 102 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
102 - if (ruleChain != null) { 103 + if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
103 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); 104 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
104 log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); 105 log.trace("[{}][{}] Starting rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
105 // Creating and starting the actors; 106 // Creating and starting the actors;
@@ -119,7 +120,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -119,7 +120,7 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
119 @Override 120 @Override
120 public void onUpdate(TbActorCtx context) { 121 public void onUpdate(TbActorCtx context) {
121 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId); 122 RuleChain ruleChain = service.findRuleChainById(tenantId, entityId);
122 - if (ruleChain != null) { 123 + if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
123 ruleChainName = ruleChain.getName(); 124 ruleChainName = ruleChain.getName();
124 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId); 125 List<RuleNode> ruleNodeList = service.getRuleChainNodes(tenantId, entityId);
125 log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size()); 126 log.trace("[{}][{}] Updating rule chain with {} nodes", tenantId, entityId, ruleNodeList.size());
@@ -23,13 +23,13 @@ import org.thingsboard.server.actors.TbEntityActorId; @@ -23,13 +23,13 @@ import org.thingsboard.server.actors.TbEntityActorId;
23 import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate; 23 import org.thingsboard.server.actors.TbEntityTypeActorIdPredicate;
24 import org.thingsboard.server.actors.service.ContextAwareActor; 24 import org.thingsboard.server.actors.service.ContextAwareActor;
25 import org.thingsboard.server.actors.service.DefaultActorService; 25 import org.thingsboard.server.actors.service.DefaultActorService;
26 -import org.thingsboard.server.actors.tenant.TenantActor;  
27 import org.thingsboard.server.common.data.EntityType; 26 import org.thingsboard.server.common.data.EntityType;
28 import org.thingsboard.server.common.data.id.EntityId; 27 import org.thingsboard.server.common.data.id.EntityId;
29 import org.thingsboard.server.common.data.id.RuleChainId; 28 import org.thingsboard.server.common.data.id.RuleChainId;
30 import org.thingsboard.server.common.data.id.TenantId; 29 import org.thingsboard.server.common.data.id.TenantId;
31 import org.thingsboard.server.common.data.page.PageDataIterable; 30 import org.thingsboard.server.common.data.page.PageDataIterable;
32 import org.thingsboard.server.common.data.rule.RuleChain; 31 import org.thingsboard.server.common.data.rule.RuleChain;
  32 +import org.thingsboard.server.common.data.rule.RuleChainType;
33 import org.thingsboard.server.common.msg.TbActorMsg; 33 import org.thingsboard.server.common.msg.TbActorMsg;
34 import org.thingsboard.server.dao.rule.RuleChainService; 34 import org.thingsboard.server.dao.rule.RuleChainService;
35 35
@@ -55,7 +55,7 @@ public abstract class RuleChainManagerActor extends ContextAwareActor { @@ -55,7 +55,7 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
55 } 55 }
56 56
57 protected void initRuleChains() { 57 protected void initRuleChains() {
58 - for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChains(tenantId, link), ContextAwareActor.ENTITY_PACK_LIMIT)) { 58 + for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {
59 RuleChainId ruleChainId = ruleChain.getId(); 59 RuleChainId ruleChainId = ruleChain.getId();
60 log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId()); 60 log.debug("[{}|{}] Creating rule chain actor", ruleChainId.getEntityType(), ruleChain.getId());
61 TbActorRef actorRef = getOrCreateActor(ruleChainId, id -> ruleChain); 61 TbActorRef actorRef = getOrCreateActor(ruleChainId, id -> ruleChain);
@@ -65,13 +65,13 @@ public abstract class RuleChainManagerActor extends ContextAwareActor { @@ -65,13 +65,13 @@ public abstract class RuleChainManagerActor extends ContextAwareActor {
65 } 65 }
66 66
67 protected void destroyRuleChains() { 67 protected void destroyRuleChains() {
68 - for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChains(tenantId, link), ContextAwareActor.ENTITY_PACK_LIMIT)) { 68 + for (RuleChain ruleChain : new PageDataIterable<>(link -> ruleChainService.findTenantRuleChainsByType(tenantId, RuleChainType.CORE, link), ContextAwareActor.ENTITY_PACK_LIMIT)) {
69 ctx.stop(new TbEntityActorId(ruleChain.getId())); 69 ctx.stop(new TbEntityActorId(ruleChain.getId()));
70 } 70 }
71 } 71 }
72 72
73 protected void visit(RuleChain entity, TbActorRef actorRef) { 73 protected void visit(RuleChain entity, TbActorRef actorRef) {
74 - if (entity != null && entity.isRoot()) { 74 + if (entity != null && entity.isRoot() && entity.getType().equals(RuleChainType.CORE)) {
75 rootChain = entity; 75 rootChain = entity;
76 rootChainActor = actorRef; 76 rootChainActor = actorRef;
77 } 77 }
@@ -33,21 +33,27 @@ import org.thingsboard.server.common.data.ApiUsageState; @@ -33,21 +33,27 @@ import org.thingsboard.server.common.data.ApiUsageState;
33 import org.thingsboard.server.common.data.EntityType; 33 import org.thingsboard.server.common.data.EntityType;
34 import org.thingsboard.server.common.data.Tenant; 34 import org.thingsboard.server.common.data.Tenant;
35 import org.thingsboard.server.common.data.TenantProfile; 35 import org.thingsboard.server.common.data.TenantProfile;
  36 +import org.thingsboard.server.common.data.edge.Edge;
36 import org.thingsboard.server.common.data.id.DeviceId; 37 import org.thingsboard.server.common.data.id.DeviceId;
  38 +import org.thingsboard.server.common.data.id.EdgeId;
37 import org.thingsboard.server.common.data.id.EntityId; 39 import org.thingsboard.server.common.data.id.EntityId;
38 import org.thingsboard.server.common.data.id.RuleChainId; 40 import org.thingsboard.server.common.data.id.RuleChainId;
39 import org.thingsboard.server.common.data.id.TenantId; 41 import org.thingsboard.server.common.data.id.TenantId;
  42 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
40 import org.thingsboard.server.common.data.rule.RuleChain; 43 import org.thingsboard.server.common.data.rule.RuleChain;
  44 +import org.thingsboard.server.common.data.rule.RuleChainType;
41 import org.thingsboard.server.common.msg.MsgType; 45 import org.thingsboard.server.common.msg.MsgType;
42 import org.thingsboard.server.common.msg.TbActorMsg; 46 import org.thingsboard.server.common.msg.TbActorMsg;
43 import org.thingsboard.server.common.msg.TbMsg; 47 import org.thingsboard.server.common.msg.TbMsg;
44 import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; 48 import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
45 import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg; 49 import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
  50 +import org.thingsboard.server.common.msg.edge.EdgeEventUpdateMsg;
46 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 51 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
47 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg; 52 import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
48 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; 53 import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
49 import org.thingsboard.server.common.msg.queue.RuleEngineException; 54 import org.thingsboard.server.common.msg.queue.RuleEngineException;
50 import org.thingsboard.server.common.msg.queue.ServiceType; 55 import org.thingsboard.server.common.msg.queue.ServiceType;
  56 +import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
51 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; 57 import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
52 58
53 import java.util.List; 59 import java.util.List;
@@ -155,13 +161,18 @@ public class TenantActor extends RuleChainManagerActor { @@ -155,13 +161,18 @@ public class TenantActor extends RuleChainManagerActor {
155 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG: 161 case DEVICE_ATTRIBUTES_UPDATE_TO_DEVICE_ACTOR_MSG:
156 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG: 162 case DEVICE_CREDENTIALS_UPDATE_TO_DEVICE_ACTOR_MSG:
157 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: 163 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
  164 + case DEVICE_EDGE_UPDATE_TO_DEVICE_ACTOR_MSG:
158 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: 165 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
  166 + case DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
159 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: 167 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
160 onToDeviceActorMsg((DeviceAwareMsg) msg, true); 168 onToDeviceActorMsg((DeviceAwareMsg) msg, true);
161 break; 169 break;
162 case RULE_CHAIN_TO_RULE_CHAIN_MSG: 170 case RULE_CHAIN_TO_RULE_CHAIN_MSG:
163 onRuleChainMsg((RuleChainAwareMsg) msg); 171 onRuleChainMsg((RuleChainAwareMsg) msg);
164 break; 172 break;
  173 + case EDGE_EVENT_UPDATE_TO_EDGE_SESSION_MSG:
  174 + onToEdgeSessionMsg((EdgeEventUpdateMsg) msg);
  175 + break;
165 default: 176 default:
166 return false; 177 return false;
167 } 178 }
@@ -230,14 +241,26 @@ public class TenantActor extends RuleChainManagerActor { @@ -230,14 +241,26 @@ public class TenantActor extends RuleChainManagerActor {
230 log.info("[{}] Received API state update. Going to ENABLE Rule Engine execution.", tenantId); 241 log.info("[{}] Received API state update. Going to ENABLE Rule Engine execution.", tenantId);
231 initRuleChains(); 242 initRuleChains();
232 } 243 }
233 - }  
234 - if (isRuleEngineForCurrentTenant) { 244 + } else if (msg.getEntityId().getEntityType() == EntityType.EDGE) {
  245 + EdgeId edgeId = new EdgeId(msg.getEntityId().getId());
  246 + EdgeRpcService edgeRpcService = systemContext.getEdgeRpcService();
  247 + if (msg.getEvent() == ComponentLifecycleEvent.DELETED) {
  248 + edgeRpcService.deleteEdge(edgeId);
  249 + } else {
  250 + Edge edge = systemContext.getEdgeService().findEdgeById(tenantId, edgeId);
  251 + if (msg.getEvent() == ComponentLifecycleEvent.UPDATED) {
  252 + edgeRpcService.updateEdge(edge);
  253 + }
  254 + }
  255 + } else if (isRuleEngineForCurrentTenant) {
235 TbActorRef target = getEntityActorRef(msg.getEntityId()); 256 TbActorRef target = getEntityActorRef(msg.getEntityId());
236 if (target != null) { 257 if (target != null) {
237 if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) { 258 if (msg.getEntityId().getEntityType() == EntityType.RULE_CHAIN) {
238 RuleChain ruleChain = systemContext.getRuleChainService(). 259 RuleChain ruleChain = systemContext.getRuleChainService().
239 findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId())); 260 findRuleChainById(tenantId, new RuleChainId(msg.getEntityId().getId()));
240 - visit(ruleChain, target); 261 + if (ruleChain != null && RuleChainType.CORE.equals(ruleChain.getType())) {
  262 + visit(ruleChain, target);
  263 + }
241 } 264 }
242 target.tellWithHighPriority(msg); 265 target.tellWithHighPriority(msg);
243 } else { 266 } else {
@@ -252,6 +275,11 @@ public class TenantActor extends RuleChainManagerActor { @@ -252,6 +275,11 @@ public class TenantActor extends RuleChainManagerActor {
252 () -> new DeviceActorCreator(systemContext, tenantId, deviceId)); 275 () -> new DeviceActorCreator(systemContext, tenantId, deviceId));
253 } 276 }
254 277
  278 + private void onToEdgeSessionMsg(EdgeEventUpdateMsg msg) {
  279 + log.trace("[{}] onToEdgeSessionMsg [{}]", msg.getTenantId(), msg);
  280 + systemContext.getEdgeRpcService().onEdgeEvent(msg.getEdgeId());
  281 + }
  282 +
255 public static class ActorCreator extends ContextBasedCreator { 283 public static class ActorCreator extends ContextBasedCreator {
256 284
257 private final TenantId tenantId; 285 private final TenantId tenantId;
@@ -71,7 +71,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt @@ -71,7 +71,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
71 public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login"; 71 public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
72 public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; 72 public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public";
73 public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; 73 public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
74 - protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**"}; 74 + protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**", "/api/license/**"};
75 public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**"; 75 public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
76 public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**"; 76 public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";
77 77
@@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
34 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 34 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
35 import org.thingsboard.server.common.data.alarm.AlarmStatus; 35 import org.thingsboard.server.common.data.alarm.AlarmStatus;
36 import org.thingsboard.server.common.data.audit.ActionType; 36 import org.thingsboard.server.common.data.audit.ActionType;
  37 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
37 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 38 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
38 import org.thingsboard.server.common.data.exception.ThingsboardException; 39 import org.thingsboard.server.common.data.exception.ThingsboardException;
39 import org.thingsboard.server.common.data.id.AlarmId; 40 import org.thingsboard.server.common.data.id.AlarmId;
@@ -93,6 +94,9 @@ public class AlarmController extends BaseController { @@ -93,6 +94,9 @@ public class AlarmController extends BaseController {
93 logEntityAction(savedAlarm.getOriginator(), savedAlarm, 94 logEntityAction(savedAlarm.getOriginator(), savedAlarm,
94 getCurrentUser().getCustomerId(), 95 getCurrentUser().getCustomerId(),
95 alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 96 alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
  97 +
  98 + sendEntityNotificationMsg(getTenantId(), savedAlarm.getId(), alarm.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
  99 +
96 return savedAlarm; 100 return savedAlarm;
97 } catch (Exception e) { 101 } catch (Exception e) {
98 logEntityAction(emptyId(EntityType.ALARM), alarm, 102 logEntityAction(emptyId(EntityType.ALARM), alarm,
@@ -109,8 +113,11 @@ public class AlarmController extends BaseController { @@ -109,8 +113,11 @@ public class AlarmController extends BaseController {
109 try { 113 try {
110 AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); 114 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
111 checkAlarmId(alarmId, Operation.WRITE); 115 checkAlarmId(alarmId, Operation.WRITE);
  116 +
  117 + sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.DELETED);
  118 +
112 return alarmService.deleteAlarm(getTenantId(), alarmId); 119 return alarmService.deleteAlarm(getTenantId(), alarmId);
113 - } catch (Exception e) { 120 + } catch (Exception e) {
114 throw handleException(e); 121 throw handleException(e);
115 } 122 }
116 } 123 }
@@ -128,6 +135,8 @@ public class AlarmController extends BaseController { @@ -128,6 +135,8 @@ public class AlarmController extends BaseController {
128 alarm.setAckTs(ackTs); 135 alarm.setAckTs(ackTs);
129 alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK); 136 alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK);
130 logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); 137 logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null);
  138 +
  139 + sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.ALARM_ACK);
131 } catch (Exception e) { 140 } catch (Exception e) {
132 throw handleException(e); 141 throw handleException(e);
133 } 142 }
@@ -146,6 +155,8 @@ public class AlarmController extends BaseController { @@ -146,6 +155,8 @@ public class AlarmController extends BaseController {
146 alarm.setClearTs(clearTs); 155 alarm.setClearTs(clearTs);
147 alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK); 156 alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK);
148 logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); 157 logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null);
  158 +
  159 + sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.ALARM_CLEAR);
149 } catch (Exception e) { 160 } catch (Exception e) {
150 throw handleException(e); 161 throw handleException(e);
151 } 162 }
@@ -33,14 +33,18 @@ import org.thingsboard.server.common.data.asset.Asset; @@ -33,14 +33,18 @@ import org.thingsboard.server.common.data.asset.Asset;
33 import org.thingsboard.server.common.data.asset.AssetInfo; 33 import org.thingsboard.server.common.data.asset.AssetInfo;
34 import org.thingsboard.server.common.data.asset.AssetSearchQuery; 34 import org.thingsboard.server.common.data.asset.AssetSearchQuery;
35 import org.thingsboard.server.common.data.audit.ActionType; 35 import org.thingsboard.server.common.data.audit.ActionType;
  36 +import org.thingsboard.server.common.data.edge.Edge;
  37 +import org.thingsboard.server.common.data.edge.EdgeEventType;
36 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 38 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
  39 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
37 import org.thingsboard.server.common.data.exception.ThingsboardException; 40 import org.thingsboard.server.common.data.exception.ThingsboardException;
38 import org.thingsboard.server.common.data.id.AssetId; 41 import org.thingsboard.server.common.data.id.AssetId;
39 import org.thingsboard.server.common.data.id.CustomerId; 42 import org.thingsboard.server.common.data.id.CustomerId;
  43 +import org.thingsboard.server.common.data.id.EdgeId;
40 import org.thingsboard.server.common.data.id.TenantId; 44 import org.thingsboard.server.common.data.id.TenantId;
41 import org.thingsboard.server.common.data.page.PageData; 45 import org.thingsboard.server.common.data.page.PageData;
42 import org.thingsboard.server.common.data.page.PageLink; 46 import org.thingsboard.server.common.data.page.PageLink;
43 -import org.thingsboard.server.common.data.security.Authority; 47 +import org.thingsboard.server.common.data.page.TimePageLink;
44 import org.thingsboard.server.dao.exception.IncorrectParameterException; 48 import org.thingsboard.server.dao.exception.IncorrectParameterException;
45 import org.thingsboard.server.dao.model.ModelConstants; 49 import org.thingsboard.server.dao.model.ModelConstants;
46 import org.thingsboard.server.queue.util.TbCoreComponent; 50 import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -54,6 +58,8 @@ import java.util.stream.Collectors; @@ -54,6 +58,8 @@ import java.util.stream.Collectors;
54 58
55 import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; 59 import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE;
56 60
  61 +import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
  62 +
57 @RestController 63 @RestController
58 @TbCoreComponent 64 @TbCoreComponent
59 @RequestMapping("/api") 65 @RequestMapping("/api")
@@ -98,7 +104,7 @@ public class AssetController extends BaseController { @@ -98,7 +104,7 @@ public class AssetController extends BaseController {
98 104
99 asset.setTenantId(getCurrentUser().getTenantId()); 105 asset.setTenantId(getCurrentUser().getTenantId());
100 106
101 - checkEntity(asset.getId(), asset, Resource.ASSET); 107 + checkEntity(asset.getId(), asset, Resource.ASSET);
102 108
103 Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); 109 Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
104 110
@@ -106,6 +112,10 @@ public class AssetController extends BaseController { @@ -106,6 +112,10 @@ public class AssetController extends BaseController {
106 savedAsset.getCustomerId(), 112 savedAsset.getCustomerId(),
107 asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 113 asset.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
108 114
  115 + if (asset.getId() != null) {
  116 + sendEntityNotificationMsg(savedAsset.getTenantId(), savedAsset.getId(), EdgeEventActionType.UPDATED);
  117 + }
  118 +
109 return savedAsset; 119 return savedAsset;
110 } catch (Exception e) { 120 } catch (Exception e) {
111 logEntityAction(emptyId(EntityType.ASSET), asset, 121 logEntityAction(emptyId(EntityType.ASSET), asset,
@@ -122,12 +132,16 @@ public class AssetController extends BaseController { @@ -122,12 +132,16 @@ public class AssetController extends BaseController {
122 try { 132 try {
123 AssetId assetId = new AssetId(toUUID(strAssetId)); 133 AssetId assetId = new AssetId(toUUID(strAssetId));
124 Asset asset = checkAssetId(assetId, Operation.DELETE); 134 Asset asset = checkAssetId(assetId, Operation.DELETE);
  135 +
  136 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), assetId);
  137 +
125 assetService.deleteAsset(getTenantId(), assetId); 138 assetService.deleteAsset(getTenantId(), assetId);
126 139
127 logEntityAction(assetId, asset, 140 logEntityAction(assetId, asset,
128 asset.getCustomerId(), 141 asset.getCustomerId(),
129 ActionType.DELETED, null, strAssetId); 142 ActionType.DELETED, null, strAssetId);
130 143
  144 + sendDeleteNotificationMsg(getTenantId(), assetId, relatedEdgeIds);
131 } catch (Exception e) { 145 } catch (Exception e) {
132 logEntityAction(emptyId(EntityType.ASSET), 146 logEntityAction(emptyId(EntityType.ASSET),
133 null, 147 null,
@@ -157,6 +171,9 @@ public class AssetController extends BaseController { @@ -157,6 +171,9 @@ public class AssetController extends BaseController {
157 savedAsset.getCustomerId(), 171 savedAsset.getCustomerId(),
158 ActionType.ASSIGNED_TO_CUSTOMER, null, strAssetId, strCustomerId, customer.getName()); 172 ActionType.ASSIGNED_TO_CUSTOMER, null, strAssetId, strCustomerId, customer.getName());
159 173
  174 + sendEntityAssignToCustomerNotificationMsg(savedAsset.getTenantId(), savedAsset.getId(),
  175 + customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
  176 +
160 return savedAsset; 177 return savedAsset;
161 } catch (Exception e) { 178 } catch (Exception e) {
162 179
@@ -188,6 +205,9 @@ public class AssetController extends BaseController { @@ -188,6 +205,9 @@ public class AssetController extends BaseController {
188 asset.getCustomerId(), 205 asset.getCustomerId(),
189 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strAssetId, customer.getId().toString(), customer.getName()); 206 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strAssetId, customer.getId().toString(), customer.getName());
190 207
  208 + sendEntityAssignToCustomerNotificationMsg(savedAsset.getTenantId(), savedAsset.getId(),
  209 + customer.getId(), EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
  210 +
191 return savedAsset; 211 return savedAsset;
192 } catch (Exception e) { 212 } catch (Exception e) {
193 213
@@ -401,4 +421,113 @@ public class AssetController extends BaseController { @@ -401,4 +421,113 @@ public class AssetController extends BaseController {
401 throw handleException(e); 421 throw handleException(e);
402 } 422 }
403 } 423 }
  424 +
  425 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  426 + @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST)
  427 + @ResponseBody
  428 + public Asset assignAssetToEdge(@PathVariable(EDGE_ID) String strEdgeId,
  429 + @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
  430 + checkParameter(EDGE_ID, strEdgeId);
  431 + checkParameter(ASSET_ID, strAssetId);
  432 + try {
  433 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  434 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  435 +
  436 + AssetId assetId = new AssetId(toUUID(strAssetId));
  437 + checkAssetId(assetId, Operation.ASSIGN_TO_EDGE);
  438 +
  439 + Asset savedAsset = checkNotNull(assetService.assignAssetToEdge(getTenantId(), assetId, edgeId));
  440 +
  441 + logEntityAction(assetId, savedAsset,
  442 + savedAsset.getCustomerId(),
  443 + ActionType.ASSIGNED_TO_EDGE, null, strAssetId, strEdgeId, edge.getName());
  444 +
  445 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedAsset.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
  446 +
  447 + return savedAsset;
  448 + } catch (Exception e) {
  449 +
  450 + logEntityAction(emptyId(EntityType.ASSET), null,
  451 + null,
  452 + ActionType.ASSIGNED_TO_EDGE, e, strAssetId, strEdgeId);
  453 +
  454 + throw handleException(e);
  455 + }
  456 + }
  457 +
  458 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  459 + @RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE)
  460 + @ResponseBody
  461 + public Asset unassignAssetFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
  462 + @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
  463 + checkParameter(EDGE_ID, strEdgeId);
  464 + checkParameter(ASSET_ID, strAssetId);
  465 + try {
  466 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  467 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  468 +
  469 + AssetId assetId = new AssetId(toUUID(strAssetId));
  470 + Asset asset = checkAssetId(assetId, Operation.UNASSIGN_FROM_EDGE);
  471 +
  472 + Asset savedAsset = checkNotNull(assetService.unassignAssetFromEdge(getTenantId(), assetId, edgeId));
  473 +
  474 + logEntityAction(assetId, asset,
  475 + asset.getCustomerId(),
  476 + ActionType.UNASSIGNED_FROM_EDGE, null, strAssetId, strEdgeId, edge.getName());
  477 +
  478 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedAsset.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
  479 +
  480 + return savedAsset;
  481 + } catch (Exception e) {
  482 +
  483 + logEntityAction(emptyId(EntityType.ASSET), null,
  484 + null,
  485 + ActionType.UNASSIGNED_FROM_EDGE, e, strAssetId, strEdgeId);
  486 +
  487 + throw handleException(e);
  488 + }
  489 + }
  490 +
  491 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  492 + @RequestMapping(value = "/edge/{edgeId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
  493 + @ResponseBody
  494 + public PageData<Asset> getEdgeAssets(
  495 + @PathVariable(EDGE_ID) String strEdgeId,
  496 + @RequestParam int pageSize,
  497 + @RequestParam int page,
  498 + @RequestParam(required = false) String type,
  499 + @RequestParam(required = false) String textSearch,
  500 + @RequestParam(required = false) String sortProperty,
  501 + @RequestParam(required = false) String sortOrder,
  502 + @RequestParam(required = false) Long startTime,
  503 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  504 + checkParameter(EDGE_ID, strEdgeId);
  505 + try {
  506 + TenantId tenantId = getCurrentUser().getTenantId();
  507 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  508 + checkEdgeId(edgeId, Operation.READ);
  509 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  510 + PageData<Asset> nonFilteredResult;
  511 + if (type != null && type.trim().length() > 0) {
  512 + nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink);
  513 + } else {
  514 + nonFilteredResult = assetService.findAssetsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
  515 + }
  516 + List<Asset> filteredAssets = nonFilteredResult.getData().stream().filter(asset -> {
  517 + try {
  518 + accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, Operation.READ, asset.getId(), asset);
  519 + return true;
  520 + } catch (ThingsboardException e) {
  521 + return false;
  522 + }
  523 + }).collect(Collectors.toList());
  524 + PageData<Asset> filteredResult = new PageData<>(filteredAssets,
  525 + nonFilteredResult.getTotalPages(),
  526 + nonFilteredResult.getTotalElements(),
  527 + nonFilteredResult.hasNext());
  528 + return checkNotNull(filteredResult);
  529 + } catch (Exception e) {
  530 + throw handleException(e);
  531 + }
  532 + }
404 } 533 }
@@ -37,6 +37,7 @@ import org.thingsboard.common.util.JacksonUtil; @@ -37,6 +37,7 @@ import org.thingsboard.common.util.JacksonUtil;
37 import org.thingsboard.rule.engine.api.MailService; 37 import org.thingsboard.rule.engine.api.MailService;
38 import org.thingsboard.server.common.data.User; 38 import org.thingsboard.server.common.data.User;
39 import org.thingsboard.server.common.data.audit.ActionType; 39 import org.thingsboard.server.common.data.audit.ActionType;
  40 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
40 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 41 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
41 import org.thingsboard.server.common.data.exception.ThingsboardException; 42 import org.thingsboard.server.common.data.exception.ThingsboardException;
42 import org.thingsboard.server.common.data.id.TenantId; 43 import org.thingsboard.server.common.data.id.TenantId;
@@ -110,6 +111,8 @@ public class AuthController extends BaseController { @@ -110,6 +111,8 @@ public class AuthController extends BaseController {
110 userCredentials.setPassword(passwordEncoder.encode(newPassword)); 111 userCredentials.setPassword(passwordEncoder.encode(newPassword));
111 userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials); 112 userService.replaceUserCredentials(securityUser.getTenantId(), userCredentials);
112 113
  114 + sendEntityNotificationMsg(getTenantId(), userCredentials.getUserId(), EdgeEventActionType.CREDENTIALS_UPDATED);
  115 +
113 eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId())); 116 eventPublisher.publishEvent(new UserAuthDataChangedEvent(securityUser.getId()));
114 ObjectNode response = JacksonUtil.newObjectNode(); 117 ObjectNode response = JacksonUtil.newObjectNode();
115 response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken()); 118 response.put("token", tokenFactory.createAccessJwtToken(securityUser).getToken());
@@ -224,6 +227,8 @@ public class AuthController extends BaseController { @@ -224,6 +227,8 @@ public class AuthController extends BaseController {
224 } 227 }
225 } 228 }
226 229
  230 + sendEntityNotificationMsg(user.getTenantId(), user.getId(), EdgeEventActionType.CREDENTIALS_UPDATED);
  231 +
227 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); 232 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
228 JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); 233 JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);
229 234
@@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.DataConstants; @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.DataConstants;
34 import org.thingsboard.server.common.data.Device; 34 import org.thingsboard.server.common.data.Device;
35 import org.thingsboard.server.common.data.DeviceInfo; 35 import org.thingsboard.server.common.data.DeviceInfo;
36 import org.thingsboard.server.common.data.DeviceProfile; 36 import org.thingsboard.server.common.data.DeviceProfile;
  37 +import org.thingsboard.server.common.data.EdgeUtils;
37 import org.thingsboard.server.common.data.EntityType; 38 import org.thingsboard.server.common.data.EntityType;
38 import org.thingsboard.server.common.data.EntityView; 39 import org.thingsboard.server.common.data.EntityView;
39 import org.thingsboard.server.common.data.EntityViewInfo; 40 import org.thingsboard.server.common.data.EntityViewInfo;
@@ -50,6 +51,10 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -50,6 +51,10 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo;
50 import org.thingsboard.server.common.data.asset.Asset; 51 import org.thingsboard.server.common.data.asset.Asset;
51 import org.thingsboard.server.common.data.asset.AssetInfo; 52 import org.thingsboard.server.common.data.asset.AssetInfo;
52 import org.thingsboard.server.common.data.audit.ActionType; 53 import org.thingsboard.server.common.data.audit.ActionType;
  54 +import org.thingsboard.server.common.data.edge.Edge;
  55 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  56 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  57 +import org.thingsboard.server.common.data.edge.EdgeInfo;
53 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 58 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
54 import org.thingsboard.server.common.data.exception.ThingsboardException; 59 import org.thingsboard.server.common.data.exception.ThingsboardException;
55 import org.thingsboard.server.common.data.id.AlarmId; 60 import org.thingsboard.server.common.data.id.AlarmId;
@@ -58,6 +63,7 @@ import org.thingsboard.server.common.data.id.CustomerId; @@ -58,6 +63,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
58 import org.thingsboard.server.common.data.id.DashboardId; 63 import org.thingsboard.server.common.data.id.DashboardId;
59 import org.thingsboard.server.common.data.id.DeviceId; 64 import org.thingsboard.server.common.data.id.DeviceId;
60 import org.thingsboard.server.common.data.id.DeviceProfileId; 65 import org.thingsboard.server.common.data.id.DeviceProfileId;
  66 +import org.thingsboard.server.common.data.id.EdgeId;
61 import org.thingsboard.server.common.data.id.EntityId; 67 import org.thingsboard.server.common.data.id.EntityId;
62 import org.thingsboard.server.common.data.id.EntityIdFactory; 68 import org.thingsboard.server.common.data.id.EntityIdFactory;
63 import org.thingsboard.server.common.data.id.EntityViewId; 69 import org.thingsboard.server.common.data.id.EntityViewId;
@@ -78,7 +84,9 @@ import org.thingsboard.server.common.data.page.SortOrder; @@ -78,7 +84,9 @@ import org.thingsboard.server.common.data.page.SortOrder;
78 import org.thingsboard.server.common.data.page.TimePageLink; 84 import org.thingsboard.server.common.data.page.TimePageLink;
79 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 85 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
80 import org.thingsboard.server.common.data.plugin.ComponentType; 86 import org.thingsboard.server.common.data.plugin.ComponentType;
  87 +import org.thingsboard.server.common.data.relation.EntityRelation;
81 import org.thingsboard.server.common.data.rule.RuleChain; 88 import org.thingsboard.server.common.data.rule.RuleChain;
  89 +import org.thingsboard.server.common.data.rule.RuleChainType;
82 import org.thingsboard.server.common.data.rule.RuleNode; 90 import org.thingsboard.server.common.data.rule.RuleNode;
83 import org.thingsboard.server.common.data.widget.WidgetTypeDetails; 91 import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
84 import org.thingsboard.server.common.data.widget.WidgetsBundle; 92 import org.thingsboard.server.common.data.widget.WidgetsBundle;
@@ -94,6 +102,7 @@ import org.thingsboard.server.dao.device.ClaimDevicesService; @@ -94,6 +102,7 @@ import org.thingsboard.server.dao.device.ClaimDevicesService;
94 import org.thingsboard.server.dao.device.DeviceCredentialsService; 102 import org.thingsboard.server.dao.device.DeviceCredentialsService;
95 import org.thingsboard.server.dao.device.DeviceProfileService; 103 import org.thingsboard.server.dao.device.DeviceProfileService;
96 import org.thingsboard.server.dao.device.DeviceService; 104 import org.thingsboard.server.dao.device.DeviceService;
  105 +import org.thingsboard.server.dao.edge.EdgeService;
97 import org.thingsboard.server.dao.entityview.EntityViewService; 106 import org.thingsboard.server.dao.entityview.EntityViewService;
98 import org.thingsboard.server.dao.exception.DataValidationException; 107 import org.thingsboard.server.dao.exception.DataValidationException;
99 import org.thingsboard.server.dao.exception.IncorrectParameterException; 108 import org.thingsboard.server.dao.exception.IncorrectParameterException;
@@ -110,10 +119,14 @@ import org.thingsboard.server.dao.user.UserService; @@ -110,10 +119,14 @@ import org.thingsboard.server.dao.user.UserService;
110 import org.thingsboard.server.dao.widget.WidgetTypeService; 119 import org.thingsboard.server.dao.widget.WidgetTypeService;
111 import org.thingsboard.server.dao.widget.WidgetsBundleService; 120 import org.thingsboard.server.dao.widget.WidgetsBundleService;
112 import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; 121 import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
  122 +import org.thingsboard.server.gen.transport.TransportProtos;
113 import org.thingsboard.server.queue.discovery.PartitionService; 123 import org.thingsboard.server.queue.discovery.PartitionService;
114 import org.thingsboard.server.queue.provider.TbQueueProducerProvider; 124 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
115 import org.thingsboard.server.queue.util.TbCoreComponent; 125 import org.thingsboard.server.queue.util.TbCoreComponent;
116 import org.thingsboard.server.service.component.ComponentDiscoveryService; 126 import org.thingsboard.server.service.component.ComponentDiscoveryService;
  127 +import org.thingsboard.server.service.edge.EdgeNotificationService;
  128 +import org.thingsboard.server.service.edge.rpc.EdgeGrpcService;
  129 +import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
117 import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository; 130 import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository;
118 import org.thingsboard.server.service.profile.TbDeviceProfileCache; 131 import org.thingsboard.server.service.profile.TbDeviceProfileCache;
119 import org.thingsboard.server.service.queue.TbClusterService; 132 import org.thingsboard.server.service.queue.TbClusterService;
@@ -244,10 +257,25 @@ public abstract class BaseController { @@ -244,10 +257,25 @@ public abstract class BaseController {
244 @Autowired 257 @Autowired
245 protected LwM2MModelsRepository lwM2MModelsRepository; 258 protected LwM2MModelsRepository lwM2MModelsRepository;
246 259
  260 + @Autowired(required = false)
  261 + protected EdgeService edgeService;
  262 +
  263 + @Autowired(required = false)
  264 + protected EdgeNotificationService edgeNotificationService;
  265 +
  266 + @Autowired(required = false)
  267 + protected SyncEdgeService syncEdgeService;
  268 +
  269 + @Autowired(required = false)
  270 + protected EdgeGrpcService edgeGrpcService;
  271 +
247 @Value("${server.log_controller_error_stack_trace}") 272 @Value("${server.log_controller_error_stack_trace}")
248 @Getter 273 @Getter
249 private boolean logControllerErrorStackTrace; 274 private boolean logControllerErrorStackTrace;
250 275
  276 + @Value("${edges.enabled}")
  277 + @Getter
  278 + protected boolean edgesEnabled;
251 279
252 @ExceptionHandler(ThingsboardException.class) 280 @ExceptionHandler(ThingsboardException.class)
253 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) { 281 public void handleThingsboardException(ThingsboardException ex, HttpServletResponse response) {
@@ -461,6 +489,9 @@ public abstract class BaseController { @@ -461,6 +489,9 @@ public abstract class BaseController {
461 case ENTITY_VIEW: 489 case ENTITY_VIEW:
462 checkEntityViewId(new EntityViewId(entityId.getId()), operation); 490 checkEntityViewId(new EntityViewId(entityId.getId()), operation);
463 return; 491 return;
  492 + case EDGE:
  493 + checkEdgeId(new EdgeId(entityId.getId()), operation);
  494 + return;
464 case WIDGETS_BUNDLE: 495 case WIDGETS_BUNDLE:
465 checkWidgetsBundleId(new WidgetsBundleId(entityId.getId()), operation); 496 checkWidgetsBundleId(new WidgetsBundleId(entityId.getId()), operation);
466 return; 497 return;
@@ -622,6 +653,30 @@ public abstract class BaseController { @@ -622,6 +653,30 @@ public abstract class BaseController {
622 } 653 }
623 } 654 }
624 655
  656 + Edge checkEdgeId(EdgeId edgeId, Operation operation) throws ThingsboardException {
  657 + try {
  658 + validateId(edgeId, "Incorrect edgeId " + edgeId);
  659 + Edge edge = edgeService.findEdgeById(getTenantId(), edgeId);
  660 + checkNotNull(edge);
  661 + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edgeId, edge);
  662 + return edge;
  663 + } catch (Exception e) {
  664 + throw handleException(e, false);
  665 + }
  666 + }
  667 +
  668 + EdgeInfo checkEdgeInfoId(EdgeId edgeId, Operation operation) throws ThingsboardException {
  669 + try {
  670 + validateId(edgeId, "Incorrect edgeId " + edgeId);
  671 + EdgeInfo edge = edgeService.findEdgeInfoById(getCurrentUser().getTenantId(), edgeId);
  672 + checkNotNull(edge);
  673 + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, edgeId, edge);
  674 + return edge;
  675 + } catch (Exception e) {
  676 + throw handleException(e, false);
  677 + }
  678 + }
  679 +
625 DashboardInfo checkDashboardInfoId(DashboardId dashboardId, Operation operation) throws ThingsboardException { 680 DashboardInfo checkDashboardInfoId(DashboardId dashboardId, Operation operation) throws ThingsboardException {
626 try { 681 try {
627 validateId(dashboardId, "Incorrect dashboardId " + dashboardId); 682 validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
@@ -643,19 +698,19 @@ public abstract class BaseController { @@ -643,19 +698,19 @@ public abstract class BaseController {
643 } 698 }
644 } 699 }
645 700
646 - List<ComponentDescriptor> checkComponentDescriptorsByType(ComponentType type) throws ThingsboardException { 701 + List<ComponentDescriptor> checkComponentDescriptorsByType(ComponentType type, RuleChainType ruleChainType) throws ThingsboardException {
647 try { 702 try {
648 log.debug("[{}] Lookup component descriptors", type); 703 log.debug("[{}] Lookup component descriptors", type);
649 - return componentDescriptorService.getComponents(type); 704 + return componentDescriptorService.getComponents(type, ruleChainType);
650 } catch (Exception e) { 705 } catch (Exception e) {
651 throw handleException(e, false); 706 throw handleException(e, false);
652 } 707 }
653 } 708 }
654 709
655 - List<ComponentDescriptor> checkComponentDescriptorsByTypes(Set<ComponentType> types) throws ThingsboardException { 710 + List<ComponentDescriptor> checkComponentDescriptorsByTypes(Set<ComponentType> types, RuleChainType ruleChainType) throws ThingsboardException {
656 try { 711 try {
657 log.debug("[{}] Lookup component descriptors", types); 712 log.debug("[{}] Lookup component descriptors", types);
658 - return componentDescriptorService.getComponents(types); 713 + return componentDescriptorService.getComponents(types, ruleChainType);
659 } catch (Exception e) { 714 } catch (Exception e) {
660 throw handleException(e, false); 715 throw handleException(e, false);
661 } 716 }
@@ -776,6 +831,12 @@ public abstract class BaseController { @@ -776,6 +831,12 @@ public abstract class BaseController {
776 case TIMESERIES_DELETED: 831 case TIMESERIES_DELETED:
777 msgType = DataConstants.TIMESERIES_DELETED; 832 msgType = DataConstants.TIMESERIES_DELETED;
778 break; 833 break;
  834 + case ASSIGNED_TO_EDGE:
  835 + msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE;
  836 + break;
  837 + case UNASSIGNED_FROM_EDGE:
  838 + msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE;
  839 + break;
779 } 840 }
780 if (!StringUtils.isEmpty(msgType)) { 841 if (!StringUtils.isEmpty(msgType)) {
781 try { 842 try {
@@ -805,6 +866,16 @@ public abstract class BaseController { @@ -805,6 +866,16 @@ public abstract class BaseController {
805 String strTenantName = extractParameter(String.class, 1, additionalInfo); 866 String strTenantName = extractParameter(String.class, 1, additionalInfo);
806 metaData.putValue("assignedToTenantId", strTenantId); 867 metaData.putValue("assignedToTenantId", strTenantId);
807 metaData.putValue("assignedToTenantName", strTenantName); 868 metaData.putValue("assignedToTenantName", strTenantName);
  869 + } else if (actionType == ActionType.ASSIGNED_TO_EDGE) {
  870 + String strEdgeId = extractParameter(String.class, 1, additionalInfo);
  871 + String strEdgeName = extractParameter(String.class, 2, additionalInfo);
  872 + metaData.putValue("assignedEdgeId", strEdgeId);
  873 + metaData.putValue("assignedEdgeName", strEdgeName);
  874 + } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) {
  875 + String strEdgeId = extractParameter(String.class, 1, additionalInfo);
  876 + String strEdgeName = extractParameter(String.class, 2, additionalInfo);
  877 + metaData.putValue("unassignedEdgeId", strEdgeId);
  878 + metaData.putValue("unassignedEdgeName", strEdgeName);
808 } 879 }
809 ObjectNode entityNode; 880 ObjectNode entityNode;
810 if (entity != null) { 881 if (entity != null) {
@@ -898,6 +969,93 @@ public abstract class BaseController { @@ -898,6 +969,93 @@ public abstract class BaseController {
898 return null; 969 return null;
899 } 970 }
900 971
  972 + protected void sendRelationNotificationMsg(TenantId tenantId, EntityRelation relation, EdgeEventActionType action) {
  973 + try {
  974 + if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
  975 + !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
  976 + sendNotificationMsgToEdgeService(tenantId, null, null, json.writeValueAsString(relation), EdgeEventType.RELATION, action);
  977 + }
  978 + } catch (Exception e) {
  979 + log.warn("Failed to push relation to core: {}", relation, e);
  980 + }
  981 + }
  982 +
  983 + protected void sendDeleteNotificationMsg(TenantId tenantId, EntityId entityId, List<EdgeId> edgeIds) {
  984 + if (edgeIds != null && !edgeIds.isEmpty()) {
  985 + for (EdgeId edgeId : edgeIds) {
  986 + sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, null, EdgeEventActionType.DELETED);
  987 + }
  988 + }
  989 + }
  990 +
  991 + protected void sendEntityAssignToCustomerNotificationMsg(TenantId tenantId, EntityId entityId, CustomerId customerId, EdgeEventActionType action) {
  992 + try {
  993 + sendNotificationMsgToEdgeService(tenantId, null, entityId, json.writeValueAsString(customerId), null, action);
  994 + } catch (Exception e) {
  995 + log.warn("Failed to push assign/unassign to/from customer to core: {}", customerId, e);
  996 + }
  997 + }
  998 +
  999 + protected void sendEntityNotificationMsg(TenantId tenantId, EntityId entityId, EdgeEventActionType action) {
  1000 + sendNotificationMsgToEdgeService(tenantId, null, entityId, null, null, action);
  1001 + }
  1002 +
  1003 + protected void sendEntityAssignToEdgeNotificationMsg(TenantId tenantId, EdgeId edgeId, EntityId entityId, EdgeEventActionType action) {
  1004 + sendNotificationMsgToEdgeService(tenantId, edgeId, entityId, null, null, action);
  1005 + }
  1006 +
  1007 + private void sendNotificationMsgToEdgeService(TenantId tenantId, EdgeId edgeId, EntityId entityId, String body, EdgeEventType type, EdgeEventActionType action) {
  1008 + if (!edgesEnabled) {
  1009 + return;
  1010 + }
  1011 + if (type == null) {
  1012 + if (entityId != null) {
  1013 + type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType());
  1014 + } else {
  1015 + log.trace("[{}] entity id and type are null. Ignoring this notification", tenantId);
  1016 + return;
  1017 + }
  1018 + if (type == null) {
  1019 + log.trace("[{}] edge event type is null. Ignoring this notification [{}]", tenantId, entityId);
  1020 + return;
  1021 + }
  1022 + }
  1023 + TransportProtos.EdgeNotificationMsgProto.Builder builder = TransportProtos.EdgeNotificationMsgProto.newBuilder();
  1024 + builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits());
  1025 + builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits());
  1026 + builder.setType(type.name());
  1027 + builder.setAction(action.name());
  1028 + if (entityId != null) {
  1029 + builder.setEntityIdMSB(entityId.getId().getMostSignificantBits());
  1030 + builder.setEntityIdLSB(entityId.getId().getLeastSignificantBits());
  1031 + builder.setEntityType(entityId.getEntityType().name());
  1032 + }
  1033 + if (edgeId != null) {
  1034 + builder.setEdgeIdMSB(edgeId.getId().getMostSignificantBits());
  1035 + builder.setEdgeIdLSB(edgeId.getId().getLeastSignificantBits());
  1036 + }
  1037 + if (body != null) {
  1038 + builder.setBody(body);
  1039 + }
  1040 + TransportProtos.EdgeNotificationMsgProto msg = builder.build();
  1041 + log.trace("[{}] sending notification to edge service {}", tenantId.getId(), msg);
  1042 + tbClusterService.pushMsgToCore(tenantId, entityId != null ? entityId : tenantId,
  1043 + TransportProtos.ToCoreMsg.newBuilder().setEdgeNotificationMsg(msg).build(), null);
  1044 + }
  1045 +
  1046 + protected List<EdgeId> findRelatedEdgeIds(TenantId tenantId, EntityId entityId) {
  1047 + if (!edgesEnabled) {
  1048 + return null;
  1049 + }
  1050 + List<EdgeId> result = null;
  1051 + try {
  1052 + result = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId).get();
  1053 + } catch (Exception e) {
  1054 + log.error("[{}] can't find related edge ids for entity [{}]", tenantId, entityId, e);
  1055 + }
  1056 + return result;
  1057 + }
  1058 +
901 private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception { 1059 private void addTimeseries(ObjectNode entityNode, List<TsKvEntry> timeseries) throws Exception {
902 if (timeseries != null && !timeseries.isEmpty()) { 1060 if (timeseries != null && !timeseries.isEmpty()) {
903 ArrayNode result = entityNode.putArray("timeseries"); 1061 ArrayNode result = entityNode.putArray("timeseries");
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import org.apache.commons.lang3.StringUtils;
18 import org.springframework.security.access.prepost.PreAuthorize; 19 import org.springframework.security.access.prepost.PreAuthorize;
19 import org.springframework.web.bind.annotation.PathVariable; 20 import org.springframework.web.bind.annotation.PathVariable;
20 import org.springframework.web.bind.annotation.RequestMapping; 21 import org.springframework.web.bind.annotation.RequestMapping;
@@ -25,6 +26,7 @@ import org.springframework.web.bind.annotation.RestController; @@ -25,6 +26,7 @@ import org.springframework.web.bind.annotation.RestController;
25 import org.thingsboard.server.common.data.exception.ThingsboardException; 26 import org.thingsboard.server.common.data.exception.ThingsboardException;
26 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 27 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
27 import org.thingsboard.server.common.data.plugin.ComponentType; 28 import org.thingsboard.server.common.data.plugin.ComponentType;
  29 +import org.thingsboard.server.common.data.rule.RuleChainType;
28 import org.thingsboard.server.queue.util.TbCoreComponent; 30 import org.thingsboard.server.queue.util.TbCoreComponent;
29 31
30 import java.util.HashSet; 32 import java.util.HashSet;
@@ -51,10 +53,11 @@ public class ComponentDescriptorController extends BaseController { @@ -51,10 +53,11 @@ public class ComponentDescriptorController extends BaseController {
51 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") 53 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
52 @RequestMapping(value = "/components/{componentType}", method = RequestMethod.GET) 54 @RequestMapping(value = "/components/{componentType}", method = RequestMethod.GET)
53 @ResponseBody 55 @ResponseBody
54 - public List<ComponentDescriptor> getComponentDescriptorsByType(@PathVariable("componentType") String strComponentType) throws ThingsboardException { 56 + public List<ComponentDescriptor> getComponentDescriptorsByType(@PathVariable("componentType") String strComponentType,
  57 + @RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException {
55 checkParameter("componentType", strComponentType); 58 checkParameter("componentType", strComponentType);
56 try { 59 try {
57 - return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType)); 60 + return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType), getRuleChainType(strRuleChainType));
58 } catch (Exception e) { 61 } catch (Exception e) {
59 throw handleException(e); 62 throw handleException(e);
60 } 63 }
@@ -63,17 +66,28 @@ public class ComponentDescriptorController extends BaseController { @@ -63,17 +66,28 @@ public class ComponentDescriptorController extends BaseController {
63 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") 66 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
64 @RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET) 67 @RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET)
65 @ResponseBody 68 @ResponseBody
66 - public List<ComponentDescriptor> getComponentDescriptorsByTypes(@RequestParam("componentTypes") String[] strComponentTypes) throws ThingsboardException { 69 + public List<ComponentDescriptor> getComponentDescriptorsByTypes(@RequestParam("componentTypes") String[] strComponentTypes,
  70 + @RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException {
67 checkArrayParameter("componentTypes", strComponentTypes); 71 checkArrayParameter("componentTypes", strComponentTypes);
68 try { 72 try {
69 Set<ComponentType> componentTypes = new HashSet<>(); 73 Set<ComponentType> componentTypes = new HashSet<>();
70 for (String strComponentType : strComponentTypes) { 74 for (String strComponentType : strComponentTypes) {
71 componentTypes.add(ComponentType.valueOf(strComponentType)); 75 componentTypes.add(ComponentType.valueOf(strComponentType));
72 } 76 }
73 - return checkComponentDescriptorsByTypes(componentTypes); 77 + return checkComponentDescriptorsByTypes(componentTypes, getRuleChainType(strRuleChainType));
74 } catch (Exception e) { 78 } catch (Exception e) {
75 throw handleException(e); 79 throw handleException(e);
76 } 80 }
77 } 81 }
78 82
  83 + private RuleChainType getRuleChainType(String strRuleChainType) {
  84 + RuleChainType ruleChainType;
  85 + if (StringUtils.isEmpty(strRuleChainType)) {
  86 + ruleChainType = RuleChainType.CORE;
  87 + } else {
  88 + ruleChainType = RuleChainType.valueOf(strRuleChainType);
  89 + }
  90 + return ruleChainType;
  91 + }
  92 +
79 } 93 }
@@ -31,8 +31,10 @@ import org.springframework.web.bind.annotation.RestController; @@ -31,8 +31,10 @@ import org.springframework.web.bind.annotation.RestController;
31 import org.thingsboard.server.common.data.Customer; 31 import org.thingsboard.server.common.data.Customer;
32 import org.thingsboard.server.common.data.EntityType; 32 import org.thingsboard.server.common.data.EntityType;
33 import org.thingsboard.server.common.data.audit.ActionType; 33 import org.thingsboard.server.common.data.audit.ActionType;
  34 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
34 import org.thingsboard.server.common.data.exception.ThingsboardException; 35 import org.thingsboard.server.common.data.exception.ThingsboardException;
35 import org.thingsboard.server.common.data.id.CustomerId; 36 import org.thingsboard.server.common.data.id.CustomerId;
  37 +import org.thingsboard.server.common.data.id.EdgeId;
36 import org.thingsboard.server.common.data.id.TenantId; 38 import org.thingsboard.server.common.data.id.TenantId;
37 import org.thingsboard.server.common.data.page.PageData; 39 import org.thingsboard.server.common.data.page.PageData;
38 import org.thingsboard.server.common.data.page.PageLink; 40 import org.thingsboard.server.common.data.page.PageLink;
@@ -40,6 +42,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent; @@ -40,6 +42,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
40 import org.thingsboard.server.service.security.permission.Operation; 42 import org.thingsboard.server.service.security.permission.Operation;
41 import org.thingsboard.server.service.security.permission.Resource; 43 import org.thingsboard.server.service.security.permission.Resource;
42 44
  45 +import java.util.List;
  46 +
43 @RestController 47 @RestController
44 @TbCoreComponent 48 @TbCoreComponent
45 @RequestMapping("/api") 49 @RequestMapping("/api")
@@ -112,6 +116,10 @@ public class CustomerController extends BaseController { @@ -112,6 +116,10 @@ public class CustomerController extends BaseController {
112 savedCustomer.getId(), 116 savedCustomer.getId(),
113 customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 117 customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
114 118
  119 + if (customer.getId() != null) {
  120 + sendEntityNotificationMsg(savedCustomer.getTenantId(), savedCustomer.getId(), EdgeEventActionType.UPDATED);
  121 + }
  122 +
115 return savedCustomer; 123 return savedCustomer;
116 } catch (Exception e) { 124 } catch (Exception e) {
117 125
@@ -130,12 +138,16 @@ public class CustomerController extends BaseController { @@ -130,12 +138,16 @@ public class CustomerController extends BaseController {
130 try { 138 try {
131 CustomerId customerId = new CustomerId(toUUID(strCustomerId)); 139 CustomerId customerId = new CustomerId(toUUID(strCustomerId));
132 Customer customer = checkCustomerId(customerId, Operation.DELETE); 140 Customer customer = checkCustomerId(customerId, Operation.DELETE);
  141 +
  142 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), customerId);
  143 +
133 customerService.deleteCustomer(getTenantId(), customerId); 144 customerService.deleteCustomer(getTenantId(), customerId);
134 145
135 logEntityAction(customerId, customer, 146 logEntityAction(customerId, customer,
136 customer.getId(), 147 customer.getId(),
137 ActionType.DELETED, null, strCustomerId); 148 ActionType.DELETED, null, strCustomerId);
138 149
  150 + sendDeleteNotificationMsg(getTenantId(), customerId, relatedEdgeIds);
139 } catch (Exception e) { 151 } catch (Exception e) {
140 152
141 logEntityAction(emptyId(EntityType.CUSTOMER), 153 logEntityAction(emptyId(EntityType.CUSTOMER),
@@ -38,20 +38,26 @@ import org.thingsboard.server.common.data.ShortCustomerInfo; @@ -38,20 +38,26 @@ import org.thingsboard.server.common.data.ShortCustomerInfo;
38 import org.thingsboard.server.common.data.Tenant; 38 import org.thingsboard.server.common.data.Tenant;
39 import org.thingsboard.server.common.data.User; 39 import org.thingsboard.server.common.data.User;
40 import org.thingsboard.server.common.data.audit.ActionType; 40 import org.thingsboard.server.common.data.audit.ActionType;
  41 +import org.thingsboard.server.common.data.edge.Edge;
  42 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
41 import org.thingsboard.server.common.data.exception.ThingsboardException; 43 import org.thingsboard.server.common.data.exception.ThingsboardException;
42 import org.thingsboard.server.common.data.id.CustomerId; 44 import org.thingsboard.server.common.data.id.CustomerId;
43 import org.thingsboard.server.common.data.id.DashboardId; 45 import org.thingsboard.server.common.data.id.DashboardId;
  46 +import org.thingsboard.server.common.data.id.EdgeId;
44 import org.thingsboard.server.common.data.id.TenantId; 47 import org.thingsboard.server.common.data.id.TenantId;
45 import org.thingsboard.server.common.data.page.PageData; 48 import org.thingsboard.server.common.data.page.PageData;
46 import org.thingsboard.server.common.data.page.PageLink; 49 import org.thingsboard.server.common.data.page.PageLink;
47 import org.thingsboard.common.util.JacksonUtil; 50 import org.thingsboard.common.util.JacksonUtil;
  51 +import org.thingsboard.server.common.data.page.TimePageLink;
48 import org.thingsboard.server.queue.util.TbCoreComponent; 52 import org.thingsboard.server.queue.util.TbCoreComponent;
49 import org.thingsboard.server.service.security.model.SecurityUser; 53 import org.thingsboard.server.service.security.model.SecurityUser;
50 import org.thingsboard.server.service.security.permission.Operation; 54 import org.thingsboard.server.service.security.permission.Operation;
51 import org.thingsboard.server.service.security.permission.Resource; 55 import org.thingsboard.server.service.security.permission.Resource;
52 56
53 import java.util.HashSet; 57 import java.util.HashSet;
  58 +import java.util.List;
54 import java.util.Set; 59 import java.util.Set;
  60 +import java.util.stream.Collectors;
55 61
56 @RestController 62 @RestController
57 @TbCoreComponent 63 @TbCoreComponent
@@ -122,6 +128,10 @@ public class DashboardController extends BaseController { @@ -122,6 +128,10 @@ public class DashboardController extends BaseController {
122 null, 128 null,
123 dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 129 dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
124 130
  131 + if (dashboard.getId() != null) {
  132 + sendEntityNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), EdgeEventActionType.UPDATED);
  133 + }
  134 +
125 return savedDashboard; 135 return savedDashboard;
126 } catch (Exception e) { 136 } catch (Exception e) {
127 logEntityAction(emptyId(EntityType.DASHBOARD), dashboard, 137 logEntityAction(emptyId(EntityType.DASHBOARD), dashboard,
@@ -139,12 +149,16 @@ public class DashboardController extends BaseController { @@ -139,12 +149,16 @@ public class DashboardController extends BaseController {
139 try { 149 try {
140 DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); 150 DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
141 Dashboard dashboard = checkDashboardId(dashboardId, Operation.DELETE); 151 Dashboard dashboard = checkDashboardId(dashboardId, Operation.DELETE);
  152 +
  153 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), dashboardId);
  154 +
142 dashboardService.deleteDashboard(getCurrentUser().getTenantId(), dashboardId); 155 dashboardService.deleteDashboard(getCurrentUser().getTenantId(), dashboardId);
143 156
144 logEntityAction(dashboardId, dashboard, 157 logEntityAction(dashboardId, dashboard,
145 null, 158 null,
146 ActionType.DELETED, null, strDashboardId); 159 ActionType.DELETED, null, strDashboardId);
147 160
  161 + sendDeleteNotificationMsg(getTenantId(), dashboardId, relatedEdgeIds);
148 } catch (Exception e) { 162 } catch (Exception e) {
149 163
150 logEntityAction(emptyId(EntityType.DASHBOARD), 164 logEntityAction(emptyId(EntityType.DASHBOARD),
@@ -176,6 +190,7 @@ public class DashboardController extends BaseController { @@ -176,6 +190,7 @@ public class DashboardController extends BaseController {
176 customerId, 190 customerId,
177 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName()); 191 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, strCustomerId, customer.getName());
178 192
  193 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
179 194
180 return savedDashboard; 195 return savedDashboard;
181 } catch (Exception e) { 196 } catch (Exception e) {
@@ -207,6 +222,8 @@ public class DashboardController extends BaseController { @@ -207,6 +222,8 @@ public class DashboardController extends BaseController {
207 customerId, 222 customerId,
208 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName()); 223 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customer.getId().toString(), customer.getName());
209 224
  225 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
  226 +
210 return savedDashboard; 227 return savedDashboard;
211 } catch (Exception e) { 228 } catch (Exception e) {
212 229
@@ -262,6 +279,7 @@ public class DashboardController extends BaseController { @@ -262,6 +279,7 @@ public class DashboardController extends BaseController {
262 logEntityAction(dashboardId, savedDashboard, 279 logEntityAction(dashboardId, savedDashboard,
263 customerId, 280 customerId,
264 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); 281 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
  282 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
265 } 283 }
266 for (CustomerId customerId : removedCustomerIds) { 284 for (CustomerId customerId : removedCustomerIds) {
267 ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); 285 ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId);
@@ -269,7 +287,7 @@ public class DashboardController extends BaseController { @@ -269,7 +287,7 @@ public class DashboardController extends BaseController {
269 logEntityAction(dashboardId, dashboard, 287 logEntityAction(dashboardId, dashboard,
270 customerId, 288 customerId,
271 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); 289 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
272 - 290 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
273 } 291 }
274 return savedDashboard; 292 return savedDashboard;
275 } 293 }
@@ -313,6 +331,7 @@ public class DashboardController extends BaseController { @@ -313,6 +331,7 @@ public class DashboardController extends BaseController {
313 logEntityAction(dashboardId, savedDashboard, 331 logEntityAction(dashboardId, savedDashboard,
314 customerId, 332 customerId,
315 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); 333 ActionType.ASSIGNED_TO_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
  334 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
316 } 335 }
317 return savedDashboard; 336 return savedDashboard;
318 } 337 }
@@ -356,7 +375,7 @@ public class DashboardController extends BaseController { @@ -356,7 +375,7 @@ public class DashboardController extends BaseController {
356 logEntityAction(dashboardId, dashboard, 375 logEntityAction(dashboardId, dashboard,
357 customerId, 376 customerId,
358 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle()); 377 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDashboardId, customerId.toString(), customerInfo.getTitle());
359 - 378 + sendEntityAssignToCustomerNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), customerId, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
360 } 379 }
361 return savedDashboard; 380 return savedDashboard;
362 } 381 }
@@ -578,4 +597,106 @@ public class DashboardController extends BaseController { @@ -578,4 +597,106 @@ public class DashboardController extends BaseController {
578 } catch (Exception e) {} 597 } catch (Exception e) {}
579 return null; 598 return null;
580 } 599 }
  600 +
  601 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  602 + @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST)
  603 + @ResponseBody
  604 + public Dashboard assignDashboardToEdge(@PathVariable("edgeId") String strEdgeId,
  605 + @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
  606 + checkParameter("edgeId", strEdgeId);
  607 + checkParameter(DASHBOARD_ID, strDashboardId);
  608 + try {
  609 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  610 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  611 +
  612 + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
  613 + checkDashboardId(dashboardId, Operation.ASSIGN_TO_EDGE);
  614 +
  615 + Dashboard savedDashboard = checkNotNull(dashboardService.assignDashboardToEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
  616 +
  617 + logEntityAction(dashboardId, savedDashboard,
  618 + null,
  619 + ActionType.ASSIGNED_TO_EDGE, null, strDashboardId, strEdgeId, edge.getName());
  620 +
  621 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDashboard.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
  622 +
  623 + return savedDashboard;
  624 + } catch (Exception e) {
  625 +
  626 + logEntityAction(emptyId(EntityType.DASHBOARD), null,
  627 + null,
  628 + ActionType.ASSIGNED_TO_EDGE, e, strDashboardId, strEdgeId);
  629 +
  630 + throw handleException(e);
  631 + }
  632 + }
  633 +
  634 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  635 + @RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.DELETE)
  636 + @ResponseBody
  637 + public Dashboard unassignDashboardFromEdge(@PathVariable("edgeId") String strEdgeId,
  638 + @PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
  639 + checkParameter("edgeId", strEdgeId);
  640 + checkParameter(DASHBOARD_ID, strDashboardId);
  641 + try {
  642 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  643 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  644 + DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
  645 + Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_EDGE);
  646 +
  647 + Dashboard savedDashboard = checkNotNull(dashboardService.unassignDashboardFromEdge(getCurrentUser().getTenantId(), dashboardId, edgeId));
  648 +
  649 + logEntityAction(dashboardId, dashboard,
  650 + null,
  651 + ActionType.UNASSIGNED_FROM_EDGE, null, strDashboardId, strEdgeId, edge.getName());
  652 +
  653 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDashboard.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
  654 +
  655 + return savedDashboard;
  656 + } catch (Exception e) {
  657 +
  658 + logEntityAction(emptyId(EntityType.DASHBOARD), null,
  659 + null,
  660 + ActionType.UNASSIGNED_FROM_EDGE, e, strDashboardId, strEdgeId);
  661 +
  662 + throw handleException(e);
  663 + }
  664 + }
  665 +
  666 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  667 + @RequestMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
  668 + @ResponseBody
  669 + public PageData<DashboardInfo> getEdgeDashboards(
  670 + @PathVariable("edgeId") String strEdgeId,
  671 + @RequestParam int pageSize,
  672 + @RequestParam int page,
  673 + @RequestParam(required = false) String textSearch,
  674 + @RequestParam(required = false) String sortProperty,
  675 + @RequestParam(required = false) String sortOrder,
  676 + @RequestParam(required = false) Long startTime,
  677 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  678 + checkParameter("edgeId", strEdgeId);
  679 + try {
  680 + TenantId tenantId = getCurrentUser().getTenantId();
  681 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  682 + checkEdgeId(edgeId, Operation.READ);
  683 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  684 + PageData<DashboardInfo> nonFilteredResult = dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
  685 + List<DashboardInfo> filteredDashboards = nonFilteredResult.getData().stream().filter(dashboardInfo -> {
  686 + try {
  687 + accessControlService.checkPermission(getCurrentUser(), Resource.DASHBOARD, Operation.READ, dashboardInfo.getId(), dashboardInfo);
  688 + return true;
  689 + } catch (ThingsboardException e) {
  690 + return false;
  691 + }
  692 + }).collect(Collectors.toList());
  693 + PageData<DashboardInfo> filteredResult = new PageData<>(filteredDashboards,
  694 + nonFilteredResult.getTotalPages(),
  695 + nonFilteredResult.getTotalElements(),
  696 + nonFilteredResult.hasNext());
  697 + return checkNotNull(filteredResult);
  698 + } catch (Exception e) {
  699 + throw handleException(e);
  700 + }
  701 + }
581 } 702 }
@@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ResponseStatus; @@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
32 import org.springframework.web.bind.annotation.RestController; 32 import org.springframework.web.bind.annotation.RestController;
33 import org.springframework.web.context.request.async.DeferredResult; 33 import org.springframework.web.context.request.async.DeferredResult;
34 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg; 34 import org.thingsboard.rule.engine.api.msg.DeviceCredentialsUpdateNotificationMsg;
  35 +import org.thingsboard.rule.engine.api.msg.DeviceEdgeUpdateMsg;
35 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; 36 import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
36 import org.thingsboard.server.common.data.ClaimRequest; 37 import org.thingsboard.server.common.data.ClaimRequest;
37 import org.thingsboard.server.common.data.Customer; 38 import org.thingsboard.server.common.data.Customer;
@@ -43,14 +44,18 @@ import org.thingsboard.server.common.data.EntityType; @@ -43,14 +44,18 @@ import org.thingsboard.server.common.data.EntityType;
43 import org.thingsboard.server.common.data.Tenant; 44 import org.thingsboard.server.common.data.Tenant;
44 import org.thingsboard.server.common.data.audit.ActionType; 45 import org.thingsboard.server.common.data.audit.ActionType;
45 import org.thingsboard.server.common.data.device.DeviceSearchQuery; 46 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
  47 +import org.thingsboard.server.common.data.edge.Edge;
  48 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
46 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 49 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
47 import org.thingsboard.server.common.data.exception.ThingsboardException; 50 import org.thingsboard.server.common.data.exception.ThingsboardException;
48 import org.thingsboard.server.common.data.id.CustomerId; 51 import org.thingsboard.server.common.data.id.CustomerId;
49 import org.thingsboard.server.common.data.id.DeviceId; 52 import org.thingsboard.server.common.data.id.DeviceId;
50 import org.thingsboard.server.common.data.id.DeviceProfileId; 53 import org.thingsboard.server.common.data.id.DeviceProfileId;
  54 +import org.thingsboard.server.common.data.id.EdgeId;
51 import org.thingsboard.server.common.data.id.TenantId; 55 import org.thingsboard.server.common.data.id.TenantId;
52 import org.thingsboard.server.common.data.page.PageData; 56 import org.thingsboard.server.common.data.page.PageData;
53 import org.thingsboard.server.common.data.page.PageLink; 57 import org.thingsboard.server.common.data.page.PageLink;
  58 +import org.thingsboard.server.common.data.page.TimePageLink;
54 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 59 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
55 import org.thingsboard.server.common.data.security.DeviceCredentials; 60 import org.thingsboard.server.common.data.security.DeviceCredentials;
56 import org.thingsboard.server.common.msg.TbMsg; 61 import org.thingsboard.server.common.msg.TbMsg;
@@ -72,6 +77,8 @@ import java.util.ArrayList; @@ -72,6 +77,8 @@ import java.util.ArrayList;
72 import java.util.List; 77 import java.util.List;
73 import java.util.stream.Collectors; 78 import java.util.stream.Collectors;
74 79
  80 +import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
  81 +
75 @RestController 82 @RestController
76 @TbCoreComponent 83 @TbCoreComponent
77 @RequestMapping("/api") 84 @RequestMapping("/api")
@@ -125,6 +132,10 @@ public class DeviceController extends BaseController { @@ -125,6 +132,10 @@ public class DeviceController extends BaseController {
125 tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), 132 tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(),
126 device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); 133 device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
127 134
  135 + if (device.getId() != null) {
  136 + sendEntityNotificationMsg(savedDevice.getTenantId(), savedDevice.getId(), EdgeEventActionType.UPDATED);
  137 + }
  138 +
128 logEntityAction(savedDevice.getId(), savedDevice, 139 logEntityAction(savedDevice.getId(), savedDevice,
129 savedDevice.getCustomerId(), 140 savedDevice.getCustomerId(),
130 device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 141 device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
@@ -150,6 +161,9 @@ public class DeviceController extends BaseController { @@ -150,6 +161,9 @@ public class DeviceController extends BaseController {
150 try { 161 try {
151 DeviceId deviceId = new DeviceId(toUUID(strDeviceId)); 162 DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
152 Device device = checkDeviceId(deviceId, Operation.DELETE); 163 Device device = checkDeviceId(deviceId, Operation.DELETE);
  164 +
  165 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), deviceId);
  166 +
153 deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId); 167 deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId);
154 168
155 tbClusterService.onDeviceDeleted(device, null); 169 tbClusterService.onDeviceDeleted(device, null);
@@ -159,6 +173,8 @@ public class DeviceController extends BaseController { @@ -159,6 +173,8 @@ public class DeviceController extends BaseController {
159 device.getCustomerId(), 173 device.getCustomerId(),
160 ActionType.DELETED, null, strDeviceId); 174 ActionType.DELETED, null, strDeviceId);
161 175
  176 + sendDeleteNotificationMsg(getTenantId(), deviceId, relatedEdgeIds);
  177 +
162 deviceStateService.onDeviceDeleted(device); 178 deviceStateService.onDeviceDeleted(device);
163 } catch (Exception e) { 179 } catch (Exception e) {
164 logEntityAction(emptyId(EntityType.DEVICE), 180 logEntityAction(emptyId(EntityType.DEVICE),
@@ -189,6 +205,9 @@ public class DeviceController extends BaseController { @@ -189,6 +205,9 @@ public class DeviceController extends BaseController {
189 savedDevice.getCustomerId(), 205 savedDevice.getCustomerId(),
190 ActionType.ASSIGNED_TO_CUSTOMER, null, strDeviceId, strCustomerId, customer.getName()); 206 ActionType.ASSIGNED_TO_CUSTOMER, null, strDeviceId, strCustomerId, customer.getName());
191 207
  208 + sendEntityAssignToCustomerNotificationMsg(savedDevice.getTenantId(), savedDevice.getId(),
  209 + customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
  210 +
192 return savedDevice; 211 return savedDevice;
193 } catch (Exception e) { 212 } catch (Exception e) {
194 logEntityAction(emptyId(EntityType.DEVICE), null, 213 logEntityAction(emptyId(EntityType.DEVICE), null,
@@ -217,6 +236,9 @@ public class DeviceController extends BaseController { @@ -217,6 +236,9 @@ public class DeviceController extends BaseController {
217 device.getCustomerId(), 236 device.getCustomerId(),
218 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDeviceId, customer.getId().toString(), customer.getName()); 237 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strDeviceId, customer.getId().toString(), customer.getName());
219 238
  239 + sendEntityAssignToCustomerNotificationMsg(savedDevice.getTenantId(), savedDevice.getId(),
  240 + customer.getId(), EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
  241 +
220 return savedDevice; 242 return savedDevice;
221 } catch (Exception e) { 243 } catch (Exception e) {
222 logEntityAction(emptyId(EntityType.DEVICE), null, 244 logEntityAction(emptyId(EntityType.DEVICE), null,
@@ -280,6 +302,9 @@ public class DeviceController extends BaseController { @@ -280,6 +302,9 @@ public class DeviceController extends BaseController {
280 Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS); 302 Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS);
281 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials)); 303 DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials));
282 tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null); 304 tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null);
  305 +
  306 + sendEntityNotificationMsg(getTenantId(), device.getId(), EdgeEventActionType.CREDENTIALS_UPDATED);
  307 +
283 logEntityAction(device.getId(), device, 308 logEntityAction(device.getId(), device,
284 device.getCustomerId(), 309 device.getCustomerId(),
285 ActionType.CREDENTIALS_UPDATED, null, deviceCredentials); 310 ActionType.CREDENTIALS_UPDATED, null, deviceCredentials);
@@ -631,4 +656,115 @@ public class DeviceController extends BaseController { @@ -631,4 +656,115 @@ public class DeviceController extends BaseController {
631 metaData.putValue("assignedFromTenantName", tenant.getName()); 656 metaData.putValue("assignedFromTenantName", tenant.getName());
632 return metaData; 657 return metaData;
633 } 658 }
  659 +
  660 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  661 + @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST)
  662 + @ResponseBody
  663 + public Device assignDeviceToEdge(@PathVariable(EDGE_ID) String strEdgeId,
  664 + @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
  665 + checkParameter(EDGE_ID, strEdgeId);
  666 + checkParameter(DEVICE_ID, strDeviceId);
  667 + try {
  668 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  669 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  670 +
  671 + DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
  672 + checkDeviceId(deviceId, Operation.ASSIGN_TO_EDGE);
  673 +
  674 + Device savedDevice = checkNotNull(deviceService.assignDeviceToEdge(getCurrentUser().getTenantId(), deviceId, edgeId));
  675 +
  676 + tbClusterService.pushMsgToCore(new DeviceEdgeUpdateMsg(savedDevice.getTenantId(),
  677 + savedDevice.getId(), edgeId), null);
  678 +
  679 + logEntityAction(deviceId, savedDevice,
  680 + savedDevice.getCustomerId(),
  681 + ActionType.ASSIGNED_TO_EDGE, null, strDeviceId, strEdgeId, edge.getName());
  682 +
  683 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDevice.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
  684 +
  685 + return savedDevice;
  686 + } catch (Exception e) {
  687 + logEntityAction(emptyId(EntityType.DEVICE), null,
  688 + null,
  689 + ActionType.ASSIGNED_TO_EDGE, e, strDeviceId, strEdgeId);
  690 + throw handleException(e);
  691 + }
  692 + }
  693 +
  694 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  695 + @RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE)
  696 + @ResponseBody
  697 + public Device unassignDeviceFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
  698 + @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
  699 + checkParameter(EDGE_ID, strEdgeId);
  700 + checkParameter(DEVICE_ID, strDeviceId);
  701 + try {
  702 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  703 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  704 +
  705 + DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
  706 + Device device = checkDeviceId(deviceId, Operation.UNASSIGN_FROM_EDGE);
  707 +
  708 + Device savedDevice = checkNotNull(deviceService.unassignDeviceFromEdge(getCurrentUser().getTenantId(), deviceId, edgeId));
  709 +
  710 + tbClusterService.pushMsgToCore(new DeviceEdgeUpdateMsg(savedDevice.getTenantId(),
  711 + savedDevice.getId(), null), null);
  712 +
  713 + logEntityAction(deviceId, device,
  714 + device.getCustomerId(),
  715 + ActionType.UNASSIGNED_FROM_EDGE, null, strDeviceId, strEdgeId, edge.getName());
  716 +
  717 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedDevice.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
  718 +
  719 + return savedDevice;
  720 + } catch (Exception e) {
  721 + logEntityAction(emptyId(EntityType.DEVICE), null,
  722 + null,
  723 + ActionType.UNASSIGNED_FROM_EDGE, e, strDeviceId, strEdgeId);
  724 + throw handleException(e);
  725 + }
  726 + }
  727 +
  728 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  729 + @RequestMapping(value = "/edge/{edgeId}/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
  730 + @ResponseBody
  731 + public PageData<Device> getEdgeDevices(
  732 + @PathVariable(EDGE_ID) String strEdgeId,
  733 + @RequestParam int pageSize,
  734 + @RequestParam int page,
  735 + @RequestParam(required = false) String type,
  736 + @RequestParam(required = false) String textSearch,
  737 + @RequestParam(required = false) String sortProperty,
  738 + @RequestParam(required = false) String sortOrder,
  739 + @RequestParam(required = false) Long startTime,
  740 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  741 + checkParameter(EDGE_ID, strEdgeId);
  742 + try {
  743 + TenantId tenantId = getCurrentUser().getTenantId();
  744 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  745 + checkEdgeId(edgeId, Operation.READ);
  746 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  747 + PageData<Device> nonFilteredResult;
  748 + if (type != null && type.trim().length() > 0) {
  749 + nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink);
  750 + } else {
  751 + nonFilteredResult = deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
  752 + }
  753 + List<Device> filteredDevices = nonFilteredResult.getData().stream().filter(device -> {
  754 + try {
  755 + accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, Operation.READ, device.getId(), device);
  756 + return true;
  757 + } catch (ThingsboardException e) {
  758 + return false;
  759 + }
  760 + }).collect(Collectors.toList());
  761 + PageData<Device> filteredResult = new PageData<>(filteredDevices,
  762 + nonFilteredResult.getTotalPages(),
  763 + nonFilteredResult.getTotalElements(),
  764 + nonFilteredResult.hasNext());
  765 + return checkNotNull(filteredResult);
  766 + } catch (Exception e) {
  767 + throw handleException(e);
  768 + }
  769 + }
634 } 770 }
@@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.DeviceProfile; @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
32 import org.thingsboard.server.common.data.DeviceProfileInfo; 32 import org.thingsboard.server.common.data.DeviceProfileInfo;
33 import org.thingsboard.server.common.data.EntityType; 33 import org.thingsboard.server.common.data.EntityType;
34 import org.thingsboard.server.common.data.audit.ActionType; 34 import org.thingsboard.server.common.data.audit.ActionType;
  35 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
35 import org.thingsboard.server.common.data.exception.ThingsboardException; 36 import org.thingsboard.server.common.data.exception.ThingsboardException;
36 import org.thingsboard.server.common.data.id.DeviceProfileId; 37 import org.thingsboard.server.common.data.id.DeviceProfileId;
37 import org.thingsboard.server.common.data.page.PageData; 38 import org.thingsboard.server.common.data.page.PageData;
@@ -153,6 +154,9 @@ public class DeviceProfileController extends BaseController { @@ -153,6 +154,9 @@ public class DeviceProfileController extends BaseController {
153 null, 154 null,
154 created ? ActionType.ADDED : ActionType.UPDATED, null); 155 created ? ActionType.ADDED : ActionType.UPDATED, null);
155 156
  157 + sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
  158 + deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
  159 +
156 return savedDeviceProfile; 160 return savedDeviceProfile;
157 } catch (Exception e) { 161 } catch (Exception e) {
158 logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile, 162 logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile,
@@ -178,6 +182,7 @@ public class DeviceProfileController extends BaseController { @@ -178,6 +182,7 @@ public class DeviceProfileController extends BaseController {
178 null, 182 null,
179 ActionType.DELETED, null, strDeviceProfileId); 183 ActionType.DELETED, null, strDeviceProfileId);
180 184
  185 + sendEntityNotificationMsg(getTenantId(), deviceProfile.getId(), EdgeEventActionType.DELETED);
181 } catch (Exception e) { 186 } catch (Exception e) {
182 logEntityAction(emptyId(EntityType.DEVICE_PROFILE), 187 logEntityAction(emptyId(EntityType.DEVICE_PROFILE),
183 null, 188 null,
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.springframework.http.HttpStatus;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.web.bind.annotation.PathVariable;
  22 +import org.springframework.web.bind.annotation.RequestBody;
  23 +import org.springframework.web.bind.annotation.RequestMapping;
  24 +import org.springframework.web.bind.annotation.RequestMethod;
  25 +import org.springframework.web.bind.annotation.RequestParam;
  26 +import org.springframework.web.bind.annotation.ResponseBody;
  27 +import org.springframework.web.bind.annotation.ResponseStatus;
  28 +import org.springframework.web.bind.annotation.RestController;
  29 +import org.thingsboard.server.common.data.Customer;
  30 +import org.thingsboard.server.common.data.EntitySubtype;
  31 +import org.thingsboard.server.common.data.EntityType;
  32 +import org.thingsboard.server.common.data.audit.ActionType;
  33 +import org.thingsboard.server.common.data.edge.Edge;
  34 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  35 +import org.thingsboard.server.common.data.edge.EdgeInfo;
  36 +import org.thingsboard.server.common.data.edge.EdgeSearchQuery;
  37 +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
  38 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  39 +import org.thingsboard.server.common.data.id.CustomerId;
  40 +import org.thingsboard.server.common.data.id.EdgeId;
  41 +import org.thingsboard.server.common.data.id.RuleChainId;
  42 +import org.thingsboard.server.common.data.id.TenantId;
  43 +import org.thingsboard.server.common.data.page.PageData;
  44 +import org.thingsboard.server.common.data.page.PageLink;
  45 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  46 +import org.thingsboard.server.common.data.rule.RuleChain;
  47 +import org.thingsboard.server.common.data.security.Authority;
  48 +import org.thingsboard.server.dao.exception.DataValidationException;
  49 +import org.thingsboard.server.dao.exception.IncorrectParameterException;
  50 +import org.thingsboard.server.dao.model.ModelConstants;
  51 +import org.thingsboard.server.queue.util.TbCoreComponent;
  52 +import org.thingsboard.server.service.edge.rpc.EdgeGrpcSession;
  53 +import org.thingsboard.server.service.security.model.SecurityUser;
  54 +import org.thingsboard.server.service.security.permission.Operation;
  55 +import org.thingsboard.server.service.security.permission.Resource;
  56 +
  57 +import java.util.ArrayList;
  58 +import java.util.List;
  59 +import java.util.stream.Collectors;
  60 +
  61 +@RestController
  62 +@TbCoreComponent
  63 +@RequestMapping("/api")
  64 +public class EdgeController extends BaseController {
  65 +
  66 + public static final String EDGE_ID = "edgeId";
  67 +
  68 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  69 + @RequestMapping(value = "/edges/enabled", method = RequestMethod.GET)
  70 + @ResponseBody
  71 + public boolean isEdgesSupportEnabled() {
  72 + return edgesEnabled;
  73 + }
  74 +
  75 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  76 + @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET)
  77 + @ResponseBody
  78 + public Edge getEdgeById(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  79 + checkParameter(EDGE_ID, strEdgeId);
  80 + try {
  81 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  82 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  83 + if (Authority.CUSTOMER_USER.equals(getCurrentUser().getAuthority())) {
  84 + cleanUpSensitiveData(edge);
  85 + }
  86 + return edge;
  87 + } catch (Exception e) {
  88 + throw handleException(e);
  89 + }
  90 + }
  91 +
  92 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  93 + @RequestMapping(value = "/edge/info/{edgeId}", method = RequestMethod.GET)
  94 + @ResponseBody
  95 + public EdgeInfo getEdgeInfoById(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  96 + checkParameter(EDGE_ID, strEdgeId);
  97 + try {
  98 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  99 + EdgeInfo edgeInfo = checkEdgeInfoId(edgeId, Operation.READ);
  100 + if (Authority.CUSTOMER_USER.equals(getCurrentUser().getAuthority())) {
  101 + cleanUpSensitiveData(edgeInfo);
  102 + }
  103 + return edgeInfo;
  104 + } catch (Exception e) {
  105 + throw handleException(e);
  106 + }
  107 + }
  108 +
  109 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  110 + @RequestMapping(value = "/edge", method = RequestMethod.POST)
  111 + @ResponseBody
  112 + public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException {
  113 + try {
  114 + TenantId tenantId = getCurrentUser().getTenantId();
  115 + edge.setTenantId(tenantId);
  116 + boolean created = edge.getId() == null;
  117 +
  118 + RuleChain edgeTemplateRootRuleChain = null;
  119 + if (created) {
  120 + edgeTemplateRootRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(tenantId);
  121 + if (edgeTemplateRootRuleChain == null) {
  122 + throw new DataValidationException("Root edge rule chain is not available!");
  123 + }
  124 + }
  125 +
  126 + Operation operation = created ? Operation.CREATE : Operation.WRITE;
  127 +
  128 + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation,
  129 + edge.getId(), edge);
  130 +
  131 + Edge savedEdge = checkNotNull(edgeService.saveEdge(edge));
  132 +
  133 + if (created) {
  134 + ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), savedEdge.getId());
  135 + edgeNotificationService.setEdgeRootRuleChain(tenantId, savedEdge, edgeTemplateRootRuleChain.getId());
  136 + edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId());
  137 + }
  138 +
  139 + tbClusterService.onEntityStateChange(savedEdge.getTenantId(), savedEdge.getId(),
  140 + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
  141 +
  142 + logEntityAction(savedEdge.getId(), savedEdge, null, created ? ActionType.ADDED : ActionType.UPDATED, null);
  143 + return savedEdge;
  144 + } catch (Exception e) {
  145 + logEntityAction(emptyId(EntityType.EDGE), edge,
  146 + null, edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
  147 + throw handleException(e);
  148 + }
  149 + }
  150 +
  151 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  152 + @RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.DELETE)
  153 + @ResponseStatus(value = HttpStatus.OK)
  154 + public void deleteEdge(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  155 + checkParameter(EDGE_ID, strEdgeId);
  156 + try {
  157 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  158 + Edge edge = checkEdgeId(edgeId, Operation.DELETE);
  159 + edgeService.deleteEdge(getTenantId(), edgeId);
  160 +
  161 + tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  162 + ComponentLifecycleEvent.DELETED);
  163 +
  164 + logEntityAction(edgeId, edge,
  165 + null,
  166 + ActionType.DELETED, null, strEdgeId);
  167 +
  168 + } catch (Exception e) {
  169 +
  170 + logEntityAction(emptyId(EntityType.EDGE),
  171 + null,
  172 + null,
  173 + ActionType.DELETED, e, strEdgeId);
  174 +
  175 + throw handleException(e);
  176 + }
  177 + }
  178 +
  179 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  180 + @RequestMapping(value = "/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
  181 + @ResponseBody
  182 + public PageData<Edge> getEdges(@RequestParam int pageSize,
  183 + @RequestParam int page,
  184 + @RequestParam(required = false) String textSearch,
  185 + @RequestParam(required = false) String sortProperty,
  186 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  187 + try {
  188 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  189 + TenantId tenantId = getCurrentUser().getTenantId();
  190 + return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink));
  191 + } catch (Exception e) {
  192 + throw handleException(e);
  193 + }
  194 + }
  195 +
  196 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  197 + @RequestMapping(value = "/customer/{customerId}/edge/{edgeId}", method = RequestMethod.POST)
  198 + @ResponseBody
  199 + public Edge assignEdgeToCustomer(@PathVariable("customerId") String strCustomerId,
  200 + @PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  201 + checkParameter("customerId", strCustomerId);
  202 + checkParameter(EDGE_ID, strEdgeId);
  203 + try {
  204 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  205 + Customer customer = checkCustomerId(customerId, Operation.READ);
  206 +
  207 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  208 + checkEdgeId(edgeId, Operation.ASSIGN_TO_CUSTOMER);
  209 +
  210 + Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, customerId));
  211 +
  212 + tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  213 + ComponentLifecycleEvent.UPDATED);
  214 +
  215 + logEntityAction(edgeId, savedEdge,
  216 + savedEdge.getCustomerId(),
  217 + ActionType.ASSIGNED_TO_CUSTOMER, null, strEdgeId, strCustomerId, customer.getName());
  218 +
  219 + sendEntityAssignToCustomerNotificationMsg(savedEdge.getTenantId(), savedEdge.getId(),
  220 + customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
  221 +
  222 + return savedEdge;
  223 + } catch (Exception e) {
  224 + logEntityAction(emptyId(EntityType.EDGE), null,
  225 + null,
  226 + ActionType.ASSIGNED_TO_CUSTOMER, e, strEdgeId, strCustomerId);
  227 + throw handleException(e);
  228 + }
  229 + }
  230 +
  231 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  232 + @RequestMapping(value = "/customer/edge/{edgeId}", method = RequestMethod.DELETE)
  233 + @ResponseBody
  234 + public Edge unassignEdgeFromCustomer(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  235 + checkParameter(EDGE_ID, strEdgeId);
  236 + try {
  237 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  238 + Edge edge = checkEdgeId(edgeId, Operation.UNASSIGN_FROM_CUSTOMER);
  239 + if (edge.getCustomerId() == null || edge.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) {
  240 + throw new IncorrectParameterException("Edge isn't assigned to any customer!");
  241 + }
  242 + Customer customer = checkCustomerId(edge.getCustomerId(), Operation.READ);
  243 +
  244 + Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(getCurrentUser().getTenantId(), edgeId));
  245 +
  246 + tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  247 + ComponentLifecycleEvent.UPDATED);
  248 +
  249 + logEntityAction(edgeId, edge,
  250 + edge.getCustomerId(),
  251 + ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEdgeId, customer.getId().toString(), customer.getName());
  252 +
  253 + sendEntityAssignToCustomerNotificationMsg(savedEdge.getTenantId(), savedEdge.getId(),
  254 + customer.getId(), EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
  255 +
  256 + return savedEdge;
  257 + } catch (Exception e) {
  258 + logEntityAction(emptyId(EntityType.EDGE), null,
  259 + null,
  260 + ActionType.UNASSIGNED_FROM_CUSTOMER, e, strEdgeId);
  261 + throw handleException(e);
  262 + }
  263 + }
  264 +
  265 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  266 + @RequestMapping(value = "/customer/public/edge/{edgeId}", method = RequestMethod.POST)
  267 + @ResponseBody
  268 + public Edge assignEdgeToPublicCustomer(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
  269 + checkParameter(EDGE_ID, strEdgeId);
  270 + try {
  271 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  272 + Edge edge = checkEdgeId(edgeId, Operation.ASSIGN_TO_CUSTOMER);
  273 + Customer publicCustomer = customerService.findOrCreatePublicCustomer(edge.getTenantId());
  274 + Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, publicCustomer.getId()));
  275 +
  276 + tbClusterService.onEntityStateChange(getTenantId(), edgeId,
  277 + ComponentLifecycleEvent.UPDATED);
  278 +
  279 + logEntityAction(edgeId, savedEdge,
  280 + savedEdge.getCustomerId(),
  281 + ActionType.ASSIGNED_TO_CUSTOMER, null, strEdgeId, publicCustomer.getId().toString(), publicCustomer.getName());
  282 +
  283 + return savedEdge;
  284 + } catch (Exception e) {
  285 + logEntityAction(emptyId(EntityType.EDGE), null,
  286 + null,
  287 + ActionType.ASSIGNED_TO_CUSTOMER, e, strEdgeId);
  288 + throw handleException(e);
  289 + }
  290 + }
  291 +
  292 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  293 + @RequestMapping(value = "/tenant/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
  294 + @ResponseBody
  295 + public PageData<Edge> getTenantEdges(
  296 + @RequestParam int pageSize,
  297 + @RequestParam int page,
  298 + @RequestParam(required = false) String type,
  299 + @RequestParam(required = false) String textSearch,
  300 + @RequestParam(required = false) String sortProperty,
  301 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  302 + try {
  303 + TenantId tenantId = getCurrentUser().getTenantId();
  304 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  305 + if (type != null && type.trim().length() > 0) {
  306 + return checkNotNull(edgeService.findEdgesByTenantIdAndType(tenantId, type, pageLink));
  307 + } else {
  308 + return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink));
  309 + }
  310 + } catch (Exception e) {
  311 + throw handleException(e);
  312 + }
  313 + }
  314 +
  315 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  316 + @RequestMapping(value = "/tenant/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
  317 + @ResponseBody
  318 + public PageData<EdgeInfo> getTenantEdgeInfos(
  319 + @RequestParam int pageSize,
  320 + @RequestParam int page,
  321 + @RequestParam(required = false) String type,
  322 + @RequestParam(required = false) String textSearch,
  323 + @RequestParam(required = false) String sortProperty,
  324 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  325 + try {
  326 + TenantId tenantId = getCurrentUser().getTenantId();
  327 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  328 + if (type != null && type.trim().length() > 0) {
  329 + return checkNotNull(edgeService.findEdgeInfosByTenantIdAndType(tenantId, type, pageLink));
  330 + } else {
  331 + return checkNotNull(edgeService.findEdgeInfosByTenantId(tenantId, pageLink));
  332 + }
  333 + } catch (Exception e) {
  334 + throw handleException(e);
  335 + }
  336 + }
  337 +
  338 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  339 + @RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET)
  340 + @ResponseBody
  341 + public Edge getTenantEdge(@RequestParam String edgeName) throws ThingsboardException {
  342 + try {
  343 + TenantId tenantId = getCurrentUser().getTenantId();
  344 + return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName));
  345 + } catch (Exception e) {
  346 + throw handleException(e);
  347 + }
  348 + }
  349 +
  350 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  351 + @RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST)
  352 + @ResponseBody
  353 + public Edge setRootRuleChain(@PathVariable(EDGE_ID) String strEdgeId,
  354 + @PathVariable("ruleChainId") String strRuleChainId) throws ThingsboardException {
  355 + checkParameter(EDGE_ID, strEdgeId);
  356 + checkParameter("ruleChainId", strRuleChainId);
  357 + try {
  358 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  359 + checkRuleChain(ruleChainId, Operation.WRITE);
  360 +
  361 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  362 + Edge edge = checkEdgeId(edgeId, Operation.WRITE);
  363 + accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.WRITE,
  364 + edge.getId(), edge);
  365 +
  366 + Edge updatedEdge = edgeNotificationService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId);
  367 +
  368 + tbClusterService.onEntityStateChange(updatedEdge.getTenantId(), updatedEdge.getId(), ComponentLifecycleEvent.UPDATED);
  369 +
  370 + logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null);
  371 +
  372 + return updatedEdge;
  373 + } catch (Exception e) {
  374 + logEntityAction(emptyId(EntityType.EDGE),
  375 + null,
  376 + null,
  377 + ActionType.UPDATED, e, strEdgeId);
  378 + throw handleException(e);
  379 + }
  380 + }
  381 +
  382 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  383 + @RequestMapping(value = "/customer/{customerId}/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
  384 + @ResponseBody
  385 + public PageData<Edge> getCustomerEdges(
  386 + @PathVariable("customerId") String strCustomerId,
  387 + @RequestParam int pageSize,
  388 + @RequestParam int page,
  389 + @RequestParam(required = false) String type,
  390 + @RequestParam(required = false) String textSearch,
  391 + @RequestParam(required = false) String sortProperty,
  392 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  393 + checkParameter("customerId", strCustomerId);
  394 + try {
  395 + SecurityUser user = getCurrentUser();
  396 + TenantId tenantId = user.getTenantId();
  397 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  398 + checkCustomerId(customerId, Operation.READ);
  399 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  400 + PageData<Edge> result;
  401 + if (type != null && type.trim().length() > 0) {
  402 + result = edgeService.findEdgesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink);
  403 + } else {
  404 + result = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  405 + }
  406 + if (Authority.CUSTOMER_USER.equals(user.getAuthority())) {
  407 + for (Edge edge : result.getData()) {
  408 + cleanUpSensitiveData(edge);
  409 + }
  410 + }
  411 + return checkNotNull(result);
  412 + } catch (Exception e) {
  413 + throw handleException(e);
  414 + }
  415 + }
  416 +
  417 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  418 + @RequestMapping(value = "/customer/{customerId}/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
  419 + @ResponseBody
  420 + public PageData<EdgeInfo> getCustomerEdgeInfos(
  421 + @PathVariable("customerId") String strCustomerId,
  422 + @RequestParam int pageSize,
  423 + @RequestParam int page,
  424 + @RequestParam(required = false) String type,
  425 + @RequestParam(required = false) String textSearch,
  426 + @RequestParam(required = false) String sortProperty,
  427 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  428 + checkParameter("customerId", strCustomerId);
  429 + try {
  430 + SecurityUser user = getCurrentUser();
  431 + TenantId tenantId = user.getTenantId();
  432 + CustomerId customerId = new CustomerId(toUUID(strCustomerId));
  433 + checkCustomerId(customerId, Operation.READ);
  434 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  435 + PageData<EdgeInfo> result;
  436 + if (type != null && type.trim().length() > 0) {
  437 + result = edgeService.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink);
  438 + } else {
  439 + result = edgeService.findEdgeInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  440 + }
  441 + if (Authority.CUSTOMER_USER.equals(user.getAuthority())) {
  442 + for (Edge edge : result.getData()) {
  443 + cleanUpSensitiveData(edge);
  444 + }
  445 + }
  446 + return checkNotNull(result);
  447 + } catch (Exception e) {
  448 + throw handleException(e);
  449 + }
  450 + }
  451 +
  452 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  453 + @RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET)
  454 + @ResponseBody
  455 + public List<Edge> getEdgesByIds(
  456 + @RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException {
  457 + checkArrayParameter("edgeIds", strEdgeIds);
  458 + try {
  459 + SecurityUser user = getCurrentUser();
  460 + TenantId tenantId = user.getTenantId();
  461 + CustomerId customerId = user.getCustomerId();
  462 + List<EdgeId> edgeIds = new ArrayList<>();
  463 + for (String strEdgeId : strEdgeIds) {
  464 + edgeIds.add(new EdgeId(toUUID(strEdgeId)));
  465 + }
  466 + ListenableFuture<List<Edge>> edgesFuture;
  467 + if (customerId == null || customerId.isNullUid()) {
  468 + edgesFuture = edgeService.findEdgesByTenantIdAndIdsAsync(tenantId, edgeIds);
  469 + } else {
  470 + edgesFuture = edgeService.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, edgeIds);
  471 + }
  472 + List<Edge> edges = edgesFuture.get();
  473 + if (Authority.CUSTOMER_USER.equals(user.getAuthority())) {
  474 + for (Edge edge : edges) {
  475 + cleanUpSensitiveData(edge);
  476 + }
  477 + }
  478 + return checkNotNull(edges);
  479 + } catch (Exception e) {
  480 + throw handleException(e);
  481 + }
  482 + }
  483 +
  484 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  485 + @RequestMapping(value = "/edges", method = RequestMethod.POST)
  486 + @ResponseBody
  487 + public List<Edge> findByQuery(@RequestBody EdgeSearchQuery query) throws ThingsboardException {
  488 + checkNotNull(query);
  489 + checkNotNull(query.getParameters());
  490 + checkNotNull(query.getEdgeTypes());
  491 + checkEntityId(query.getParameters().getEntityId(), Operation.READ);
  492 + try {
  493 + SecurityUser user = getCurrentUser();
  494 + TenantId tenantId = user.getTenantId();
  495 + List<Edge> edges = checkNotNull(edgeService.findEdgesByQuery(tenantId, query).get());
  496 + edges = edges.stream().filter(edge -> {
  497 + try {
  498 + accessControlService.checkPermission(user, Resource.EDGE, Operation.READ, edge.getId(), edge);
  499 + return true;
  500 + } catch (ThingsboardException e) {
  501 + return false;
  502 + }
  503 + }).collect(Collectors.toList());
  504 + if (Authority.CUSTOMER_USER.equals(user.getAuthority())) {
  505 + for (Edge edge : edges) {
  506 + cleanUpSensitiveData(edge);
  507 + }
  508 + }
  509 + return edges;
  510 + } catch (Exception e) {
  511 + throw handleException(e);
  512 + }
  513 + }
  514 +
  515 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  516 + @RequestMapping(value = "/edge/types", method = RequestMethod.GET)
  517 + @ResponseBody
  518 + public List<EntitySubtype> getEdgeTypes() throws ThingsboardException {
  519 + try {
  520 + SecurityUser user = getCurrentUser();
  521 + TenantId tenantId = user.getTenantId();
  522 + ListenableFuture<List<EntitySubtype>> edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId);
  523 + return checkNotNull(edgeTypes.get());
  524 + } catch (Exception e) {
  525 + throw handleException(e);
  526 + }
  527 + }
  528 +
  529 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  530 + @RequestMapping(value = "/edge/sync/{edgeId}", method = RequestMethod.POST)
  531 + public void syncEdge(@PathVariable("edgeId") String strEdgeId) throws ThingsboardException {
  532 + checkParameter("edgeId", strEdgeId);
  533 + try {
  534 + if (isEdgesEnabled()) {
  535 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  536 + edgeId = checkNotNull(edgeId);
  537 + SecurityUser user = getCurrentUser();
  538 + TenantId tenantId = user.getTenantId();
  539 + EdgeGrpcSession session = edgeGrpcService.getEdgeGrpcSessionById(tenantId, edgeId);
  540 + Edge edge = session.getEdge();
  541 + syncEdgeService.sync(tenantId, edge);
  542 + } else {
  543 + throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL);
  544 + }
  545 + } catch (Exception e) {
  546 + throw handleException(e);
  547 + }
  548 + }
  549 +
  550 + @RequestMapping(value = "/license/checkInstance", method = RequestMethod.POST)
  551 + @ResponseBody
  552 + public Object checkInstance(@RequestBody Object request) throws ThingsboardException {
  553 + try {
  554 + return edgeService.checkInstance(request);
  555 + } catch (Exception e) {
  556 + throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION);
  557 + }
  558 + }
  559 +
  560 + @RequestMapping(value = "/license/activateInstance", params = {"licenseSecret", "releaseDate"}, method = RequestMethod.POST)
  561 + @ResponseBody
  562 + public Object activateInstance(@RequestParam String licenseSecret,
  563 + @RequestParam String releaseDate) throws ThingsboardException {
  564 + try {
  565 + return edgeService.activateInstance(licenseSecret, releaseDate);
  566 + } catch (Exception e) {
  567 + throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION);
  568 + }
  569 + }
  570 +
  571 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  572 + @RequestMapping(value = "/edge/missingToRelatedRuleChains/{edgeId}", method = RequestMethod.GET)
  573 + @ResponseBody
  574 + public String findMissingToRelatedRuleChains(@PathVariable("edgeId") String strEdgeId) throws ThingsboardException {
  575 + try {
  576 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  577 + edgeId = checkNotNull(edgeId);
  578 + SecurityUser user = getCurrentUser();
  579 + TenantId tenantId = user.getTenantId();
  580 + return edgeService.findMissingToRelatedRuleChains(tenantId, edgeId);
  581 + } catch (Exception e) {
  582 + throw handleException(e);
  583 + }
  584 + }
  585 +
  586 + private void cleanUpSensitiveData(Edge edge) {
  587 + edge.setEdgeLicenseKey(null);
  588 + edge.setRoutingKey(null);
  589 + edge.setSecret(null);
  590 + edge.setCloudEndpoint(null);
  591 + edge.setRootRuleChainId(null);
  592 + }
  593 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.springframework.security.access.prepost.PreAuthorize;
  21 +import org.springframework.web.bind.annotation.PathVariable;
  22 +import org.springframework.web.bind.annotation.RequestMapping;
  23 +import org.springframework.web.bind.annotation.RequestMethod;
  24 +import org.springframework.web.bind.annotation.RequestParam;
  25 +import org.springframework.web.bind.annotation.ResponseBody;
  26 +import org.springframework.web.bind.annotation.RestController;
  27 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  28 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  29 +import org.thingsboard.server.common.data.id.EdgeId;
  30 +import org.thingsboard.server.common.data.id.TenantId;
  31 +import org.thingsboard.server.common.data.page.PageData;
  32 +import org.thingsboard.server.common.data.page.TimePageLink;
  33 +import org.thingsboard.server.dao.edge.EdgeEventService;
  34 +import org.thingsboard.server.queue.util.TbCoreComponent;
  35 +import org.thingsboard.server.service.security.permission.Operation;
  36 +
  37 +@Slf4j
  38 +@RestController
  39 +@TbCoreComponent
  40 +@RequestMapping("/api")
  41 +public class EdgeEventController extends BaseController {
  42 +
  43 + @Autowired
  44 + private EdgeEventService edgeEventService;
  45 +
  46 + public static final String EDGE_ID = "edgeId";
  47 +
  48 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  49 + @RequestMapping(value = "/edge/{edgeId}/events", method = RequestMethod.GET)
  50 + @ResponseBody
  51 + public PageData<EdgeEvent> getEdgeEvents(
  52 + @PathVariable(EDGE_ID) String strEdgeId,
  53 + @RequestParam int pageSize,
  54 + @RequestParam int page,
  55 + @RequestParam(required = false) String textSearch,
  56 + @RequestParam(required = false) String sortProperty,
  57 + @RequestParam(required = false) String sortOrder,
  58 + @RequestParam(required = false) Long startTime,
  59 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  60 + checkParameter(EDGE_ID, strEdgeId);
  61 + try {
  62 + TenantId tenantId = getCurrentUser().getTenantId();
  63 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  64 + checkEdgeId(edgeId, Operation.READ);
  65 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  66 + return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false));
  67 + } catch (Exception e) {
  68 + throw handleException(e);
  69 + }
  70 + }
  71 +}
@@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.ResponseBody; @@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
25 import org.springframework.web.bind.annotation.ResponseStatus; 25 import org.springframework.web.bind.annotation.ResponseStatus;
26 import org.springframework.web.bind.annotation.RestController; 26 import org.springframework.web.bind.annotation.RestController;
27 import org.thingsboard.server.common.data.audit.ActionType; 27 import org.thingsboard.server.common.data.audit.ActionType;
  28 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
28 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 29 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
29 import org.thingsboard.server.common.data.exception.ThingsboardException; 30 import org.thingsboard.server.common.data.exception.ThingsboardException;
30 import org.thingsboard.server.common.data.id.EntityId; 31 import org.thingsboard.server.common.data.id.EntityId;
@@ -63,10 +64,13 @@ public class EntityRelationController extends BaseController { @@ -63,10 +64,13 @@ public class EntityRelationController extends BaseController {
63 relation.setTypeGroup(RelationTypeGroup.COMMON); 64 relation.setTypeGroup(RelationTypeGroup.COMMON);
64 } 65 }
65 relationService.saveRelation(getTenantId(), relation); 66 relationService.saveRelation(getTenantId(), relation);
  67 +
66 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), 68 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
67 ActionType.RELATION_ADD_OR_UPDATE, null, relation); 69 ActionType.RELATION_ADD_OR_UPDATE, null, relation);
68 logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(), 70 logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
69 ActionType.RELATION_ADD_OR_UPDATE, null, relation); 71 ActionType.RELATION_ADD_OR_UPDATE, null, relation);
  72 +
  73 + sendRelationNotificationMsg(getTenantId(), relation, EdgeEventActionType.RELATION_ADD_OR_UPDATE);
70 } catch (Exception e) { 74 } catch (Exception e) {
71 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), 75 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
72 ActionType.RELATION_ADD_OR_UPDATE, e, relation); 76 ActionType.RELATION_ADD_OR_UPDATE, e, relation);
@@ -104,6 +108,8 @@ public class EntityRelationController extends BaseController { @@ -104,6 +108,8 @@ public class EntityRelationController extends BaseController {
104 ActionType.RELATION_DELETED, null, relation); 108 ActionType.RELATION_DELETED, null, relation);
105 logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(), 109 logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
106 ActionType.RELATION_DELETED, null, relation); 110 ActionType.RELATION_DELETED, null, relation);
  111 +
  112 + sendRelationNotificationMsg(getTenantId(), relation, EdgeEventActionType.RELATION_DELETED);
107 } catch (Exception e) { 113 } catch (Exception e) {
108 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), 114 logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
109 ActionType.RELATION_DELETED, e, relation); 115 ActionType.RELATION_DELETED, e, relation);
@@ -32,11 +32,20 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -32,11 +32,20 @@ import org.springframework.web.bind.annotation.RequestParam;
32 import org.springframework.web.bind.annotation.ResponseBody; 32 import org.springframework.web.bind.annotation.ResponseBody;
33 import org.springframework.web.bind.annotation.ResponseStatus; 33 import org.springframework.web.bind.annotation.ResponseStatus;
34 import org.springframework.web.bind.annotation.RestController; 34 import org.springframework.web.bind.annotation.RestController;
35 -import org.thingsboard.server.common.data.*; 35 +import org.thingsboard.server.common.data.Customer;
  36 +import org.thingsboard.server.common.data.DataConstants;
  37 +import org.thingsboard.server.common.data.EntitySubtype;
  38 +import org.thingsboard.server.common.data.EntityType;
  39 +import org.thingsboard.server.common.data.EntityView;
  40 +import org.thingsboard.server.common.data.EntityViewInfo;
  41 +import org.thingsboard.server.common.data.asset.Asset;
36 import org.thingsboard.server.common.data.audit.ActionType; 42 import org.thingsboard.server.common.data.audit.ActionType;
  43 +import org.thingsboard.server.common.data.edge.Edge;
  44 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
37 import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; 45 import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
38 import org.thingsboard.server.common.data.exception.ThingsboardException; 46 import org.thingsboard.server.common.data.exception.ThingsboardException;
39 import org.thingsboard.server.common.data.id.CustomerId; 47 import org.thingsboard.server.common.data.id.CustomerId;
  48 +import org.thingsboard.server.common.data.id.EdgeId;
40 import org.thingsboard.server.common.data.id.EntityId; 49 import org.thingsboard.server.common.data.id.EntityId;
41 import org.thingsboard.server.common.data.id.EntityViewId; 50 import org.thingsboard.server.common.data.id.EntityViewId;
42 import org.thingsboard.server.common.data.id.TenantId; 51 import org.thingsboard.server.common.data.id.TenantId;
@@ -47,6 +56,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery; @@ -47,6 +56,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
47 import org.thingsboard.server.common.data.kv.TsKvEntry; 56 import org.thingsboard.server.common.data.kv.TsKvEntry;
48 import org.thingsboard.server.common.data.page.PageData; 57 import org.thingsboard.server.common.data.page.PageData;
49 import org.thingsboard.server.common.data.page.PageLink; 58 import org.thingsboard.server.common.data.page.PageLink;
  59 +import org.thingsboard.server.common.data.page.TimePageLink;
50 import org.thingsboard.server.dao.exception.IncorrectParameterException; 60 import org.thingsboard.server.dao.exception.IncorrectParameterException;
51 import org.thingsboard.server.dao.model.ModelConstants; 61 import org.thingsboard.server.dao.model.ModelConstants;
52 import org.thingsboard.server.dao.timeseries.TimeseriesService; 62 import org.thingsboard.server.dao.timeseries.TimeseriesService;
@@ -65,6 +75,7 @@ import java.util.stream.Collectors; @@ -65,6 +75,7 @@ import java.util.stream.Collectors;
65 75
66 import static org.apache.commons.lang3.StringUtils.isBlank; 76 import static org.apache.commons.lang3.StringUtils.isBlank;
67 import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID; 77 import static org.thingsboard.server.controller.CustomerController.CUSTOMER_ID;
  78 +import static org.thingsboard.server.controller.EdgeController.EDGE_ID;
68 79
69 /** 80 /**
70 * Created by Victor Basanets on 8/28/2017. 81 * Created by Victor Basanets on 8/28/2017.
@@ -150,6 +161,11 @@ public class EntityViewController extends BaseController { @@ -150,6 +161,11 @@ public class EntityViewController extends BaseController {
150 161
151 logEntityAction(savedEntityView.getId(), savedEntityView, null, 162 logEntityAction(savedEntityView.getId(), savedEntityView, null,
152 entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 163 entityView.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
  164 +
  165 + if (entityView.getId() != null) {
  166 + sendEntityNotificationMsg(savedEntityView.getTenantId(), savedEntityView.getId(), EdgeEventActionType.UPDATED);
  167 + }
  168 +
153 return savedEntityView; 169 return savedEntityView;
154 } catch (Exception e) { 170 } catch (Exception e) {
155 logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null, 171 logEntityAction(emptyId(EntityType.ENTITY_VIEW), entityView, null,
@@ -243,7 +259,7 @@ public class EntityViewController extends BaseController { @@ -243,7 +259,7 @@ public class EntityViewController extends BaseController {
243 private ListenableFuture<List<Void>> copyLatestFromEntityToEntityView(EntityView entityView, SecurityUser user) { 259 private ListenableFuture<List<Void>> copyLatestFromEntityToEntityView(EntityView entityView, SecurityUser user) {
244 EntityViewId entityId = entityView.getId(); 260 EntityViewId entityId = entityView.getId();
245 List<String> keys = entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null ? 261 List<String> keys = entityView.getKeys() != null && entityView.getKeys().getTimeseries() != null ?
246 - entityView.getKeys().getTimeseries() : Collections.emptyList(); 262 + entityView.getKeys().getTimeseries() : Collections.emptyList();
247 long startTs = entityView.getStartTimeMs(); 263 long startTs = entityView.getStartTimeMs();
248 long endTs = entityView.getEndTimeMs() == 0 ? Long.MAX_VALUE : entityView.getEndTimeMs(); 264 long endTs = entityView.getEndTimeMs() == 0 ? Long.MAX_VALUE : entityView.getEndTimeMs();
249 ListenableFuture<List<String>> keysFuture; 265 ListenableFuture<List<String>> keysFuture;
@@ -345,9 +361,14 @@ public class EntityViewController extends BaseController { @@ -345,9 +361,14 @@ public class EntityViewController extends BaseController {
345 try { 361 try {
346 EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId)); 362 EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
347 EntityView entityView = checkEntityViewId(entityViewId, Operation.DELETE); 363 EntityView entityView = checkEntityViewId(entityViewId, Operation.DELETE);
  364 +
  365 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), entityViewId);
  366 +
348 entityViewService.deleteEntityView(getTenantId(), entityViewId); 367 entityViewService.deleteEntityView(getTenantId(), entityViewId);
349 logEntityAction(entityViewId, entityView, entityView.getCustomerId(), 368 logEntityAction(entityViewId, entityView, entityView.getCustomerId(),
350 ActionType.DELETED, null, strEntityViewId); 369 ActionType.DELETED, null, strEntityViewId);
  370 +
  371 + sendDeleteNotificationMsg(getTenantId(), entityViewId, relatedEdgeIds);
351 } catch (Exception e) { 372 } catch (Exception e) {
352 logEntityAction(emptyId(EntityType.ENTITY_VIEW), 373 logEntityAction(emptyId(EntityType.ENTITY_VIEW),
353 null, 374 null,
@@ -388,6 +409,10 @@ public class EntityViewController extends BaseController { @@ -388,6 +409,10 @@ public class EntityViewController extends BaseController {
388 logEntityAction(entityViewId, savedEntityView, 409 logEntityAction(entityViewId, savedEntityView,
389 savedEntityView.getCustomerId(), 410 savedEntityView.getCustomerId(),
390 ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName()); 411 ActionType.ASSIGNED_TO_CUSTOMER, null, strEntityViewId, strCustomerId, customer.getName());
  412 +
  413 + sendEntityAssignToCustomerNotificationMsg(savedEntityView.getTenantId(), savedEntityView.getId(),
  414 + customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER);
  415 +
391 return savedEntityView; 416 return savedEntityView;
392 } catch (Exception e) { 417 } catch (Exception e) {
393 logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, 418 logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
@@ -414,6 +439,9 @@ public class EntityViewController extends BaseController { @@ -414,6 +439,9 @@ public class EntityViewController extends BaseController {
414 entityView.getCustomerId(), 439 entityView.getCustomerId(),
415 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName()); 440 ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEntityViewId, customer.getId().toString(), customer.getName());
416 441
  442 + sendEntityAssignToCustomerNotificationMsg(savedEntityView.getTenantId(), savedEntityView.getId(),
  443 + customer.getId(), EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER);
  444 +
417 return savedEntityView; 445 return savedEntityView;
418 } catch (Exception e) { 446 } catch (Exception e) {
419 logEntityAction(emptyId(EntityType.ENTITY_VIEW), null, 447 logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
@@ -585,4 +613,107 @@ public class EntityViewController extends BaseController { @@ -585,4 +613,107 @@ public class EntityViewController extends BaseController {
585 throw handleException(e); 613 throw handleException(e);
586 } 614 }
587 } 615 }
  616 +
  617 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  618 + @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.POST)
  619 + @ResponseBody
  620 + public EntityView assignEntityViewToEdge(@PathVariable(EDGE_ID) String strEdgeId,
  621 + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
  622 + checkParameter(EDGE_ID, strEdgeId);
  623 + checkParameter(ENTITY_VIEW_ID, strEntityViewId);
  624 + try {
  625 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  626 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  627 +
  628 + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
  629 + checkEntityViewId(entityViewId, Operation.ASSIGN_TO_EDGE);
  630 +
  631 + EntityView savedEntityView = checkNotNull(entityViewService.assignEntityViewToEdge(getTenantId(), entityViewId, edgeId));
  632 + logEntityAction(entityViewId, savedEntityView,
  633 + savedEntityView.getCustomerId(),
  634 + ActionType.ASSIGNED_TO_EDGE, null, strEntityViewId, strEdgeId, edge.getName());
  635 +
  636 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedEntityView.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
  637 +
  638 + return savedEntityView;
  639 + } catch (Exception e) {
  640 + logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
  641 + null,
  642 + ActionType.ASSIGNED_TO_EDGE, e, strEntityViewId, strEdgeId);
  643 + throw handleException(e);
  644 + }
  645 + }
  646 +
  647 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  648 + @RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE)
  649 + @ResponseBody
  650 + public EntityView unassignEntityViewFromEdge(@PathVariable(EDGE_ID) String strEdgeId,
  651 + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
  652 + checkParameter(EDGE_ID, strEdgeId);
  653 + checkParameter(ENTITY_VIEW_ID, strEntityViewId);
  654 + try {
  655 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  656 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  657 +
  658 + EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
  659 + EntityView entityView = checkEntityViewId(entityViewId, Operation.UNASSIGN_FROM_EDGE);
  660 +
  661 + EntityView savedEntityView = checkNotNull(entityViewService.unassignEntityViewFromEdge(getTenantId(), entityViewId, edgeId));
  662 + logEntityAction(entityViewId, entityView,
  663 + entityView.getCustomerId(),
  664 + ActionType.UNASSIGNED_FROM_EDGE, null, strEntityViewId, strEdgeId, edge.getName());
  665 +
  666 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedEntityView.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
  667 +
  668 + return savedEntityView;
  669 + } catch (Exception e) {
  670 + logEntityAction(emptyId(EntityType.ENTITY_VIEW), null,
  671 + null,
  672 + ActionType.UNASSIGNED_FROM_EDGE, e, strEntityViewId, strEdgeId);
  673 + throw handleException(e);
  674 + }
  675 + }
  676 +
  677 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  678 + @RequestMapping(value = "/edge/{edgeId}/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET)
  679 + @ResponseBody
  680 + public PageData<EntityView> getEdgeEntityViews(
  681 + @PathVariable(EDGE_ID) String strEdgeId,
  682 + @RequestParam int pageSize,
  683 + @RequestParam int page,
  684 + @RequestParam(required = false) String type,
  685 + @RequestParam(required = false) String textSearch,
  686 + @RequestParam(required = false) String sortProperty,
  687 + @RequestParam(required = false) String sortOrder,
  688 + @RequestParam(required = false) Long startTime,
  689 + @RequestParam(required = false) Long endTime) throws ThingsboardException {
  690 + checkParameter(EDGE_ID, strEdgeId);
  691 + try {
  692 + TenantId tenantId = getCurrentUser().getTenantId();
  693 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  694 + checkEdgeId(edgeId, Operation.READ);
  695 + TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
  696 + PageData<EntityView> nonFilteredResult;
  697 + if (type != null && type.trim().length() > 0) {
  698 + nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeIdAndType(tenantId, edgeId, type, pageLink);
  699 + } else {
  700 + nonFilteredResult = entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
  701 + }
  702 + List<EntityView> filteredEntityViews = nonFilteredResult.getData().stream().filter(entityView -> {
  703 + try {
  704 + accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, Operation.READ, entityView.getId(), entityView);
  705 + return true;
  706 + } catch (ThingsboardException e) {
  707 + return false;
  708 + }
  709 + }).collect(Collectors.toList());
  710 + PageData<EntityView> filteredResult = new PageData<>(filteredEntityViews,
  711 + nonFilteredResult.getTotalPages(),
  712 + nonFilteredResult.getTotalElements(),
  713 + nonFilteredResult.hasNext());
  714 + return checkNotNull(filteredResult);
  715 + } catch (Exception e) {
  716 + throw handleException(e);
  717 + }
  718 + }
588 } 719 }
@@ -42,7 +42,10 @@ import org.thingsboard.server.common.data.DataConstants; @@ -42,7 +42,10 @@ import org.thingsboard.server.common.data.DataConstants;
42 import org.thingsboard.server.common.data.EntityType; 42 import org.thingsboard.server.common.data.EntityType;
43 import org.thingsboard.server.common.data.Event; 43 import org.thingsboard.server.common.data.Event;
44 import org.thingsboard.server.common.data.audit.ActionType; 44 import org.thingsboard.server.common.data.audit.ActionType;
  45 +import org.thingsboard.server.common.data.edge.Edge;
  46 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
45 import org.thingsboard.server.common.data.exception.ThingsboardException; 47 import org.thingsboard.server.common.data.exception.ThingsboardException;
  48 +import org.thingsboard.server.common.data.id.EdgeId;
46 import org.thingsboard.server.common.data.id.RuleChainId; 49 import org.thingsboard.server.common.data.id.RuleChainId;
47 import org.thingsboard.server.common.data.id.RuleNodeId; 50 import org.thingsboard.server.common.data.id.RuleNodeId;
48 import org.thingsboard.server.common.data.id.TenantId; 51 import org.thingsboard.server.common.data.id.TenantId;
@@ -54,6 +57,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; @@ -54,6 +57,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
54 import org.thingsboard.server.common.data.rule.RuleChainData; 57 import org.thingsboard.server.common.data.rule.RuleChainData;
55 import org.thingsboard.server.common.data.rule.RuleChainImportResult; 58 import org.thingsboard.server.common.data.rule.RuleChainImportResult;
56 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 59 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  60 +import org.thingsboard.server.common.data.rule.RuleChainType;
57 import org.thingsboard.server.common.data.rule.RuleNode; 61 import org.thingsboard.server.common.data.rule.RuleNode;
58 import org.thingsboard.server.common.msg.TbMsg; 62 import org.thingsboard.server.common.msg.TbMsg;
59 import org.thingsboard.server.common.msg.TbMsgDataType; 63 import org.thingsboard.server.common.msg.TbMsgDataType;
@@ -138,13 +142,21 @@ public class RuleChainController extends BaseController { @@ -138,13 +142,21 @@ public class RuleChainController extends BaseController {
138 142
139 RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); 143 RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
140 144
141 - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),  
142 - created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); 145 + if (RuleChainType.CORE.equals(savedRuleChain.getType())) {
  146 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), savedRuleChain.getId(),
  147 + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
  148 + }
143 149
144 logEntityAction(savedRuleChain.getId(), savedRuleChain, 150 logEntityAction(savedRuleChain.getId(), savedRuleChain,
145 null, 151 null,
146 created ? ActionType.ADDED : ActionType.UPDATED, null); 152 created ? ActionType.ADDED : ActionType.UPDATED, null);
147 153
  154 + if (RuleChainType.EDGE.equals(savedRuleChain.getType())) {
  155 + if (!created) {
  156 + sendEntityNotificationMsg(savedRuleChain.getTenantId(), savedRuleChain.getId(), EdgeEventActionType.UPDATED);
  157 + }
  158 + }
  159 +
148 return savedRuleChain; 160 return savedRuleChain;
149 } catch (Exception e) { 161 } catch (Exception e) {
150 162
@@ -234,12 +246,19 @@ public class RuleChainController extends BaseController { @@ -234,12 +246,19 @@ public class RuleChainController extends BaseController {
234 RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE); 246 RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE);
235 RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData)); 247 RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData));
236 248
237 - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED); 249 + if (RuleChainType.CORE.equals(ruleChain.getType())) {
  250 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
  251 + }
238 252
239 logEntityAction(ruleChain.getId(), ruleChain, 253 logEntityAction(ruleChain.getId(), ruleChain,
240 null, 254 null,
241 ActionType.UPDATED, null, ruleChainMetaData); 255 ActionType.UPDATED, null, ruleChainMetaData);
242 256
  257 + if (RuleChainType.EDGE.equals(ruleChain.getType())) {
  258 + sendEntityNotificationMsg(ruleChain.getTenantId(),
  259 + ruleChain.getId(), EdgeEventActionType.UPDATED);
  260 + }
  261 +
243 return savedRuleChainMetaData; 262 return savedRuleChainMetaData;
244 } catch (Exception e) { 263 } catch (Exception e) {
245 264
@@ -256,13 +275,18 @@ public class RuleChainController extends BaseController { @@ -256,13 +275,18 @@ public class RuleChainController extends BaseController {
256 public PageData<RuleChain> getRuleChains( 275 public PageData<RuleChain> getRuleChains(
257 @RequestParam int pageSize, 276 @RequestParam int pageSize,
258 @RequestParam int page, 277 @RequestParam int page,
  278 + @RequestParam(value = "type", required = false) String typeStr,
259 @RequestParam(required = false) String textSearch, 279 @RequestParam(required = false) String textSearch,
260 @RequestParam(required = false) String sortProperty, 280 @RequestParam(required = false) String sortProperty,
261 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 281 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
262 try { 282 try {
263 TenantId tenantId = getCurrentUser().getTenantId(); 283 TenantId tenantId = getCurrentUser().getTenantId();
264 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 284 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
265 - return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink)); 285 + RuleChainType type = RuleChainType.CORE;
  286 + if (typeStr != null && typeStr.trim().length() > 0) {
  287 + type = RuleChainType.valueOf(typeStr);
  288 + }
  289 + return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink));
266 } catch (Exception e) { 290 } catch (Exception e) {
267 throw handleException(e); 291 throw handleException(e);
268 } 292 }
@@ -281,19 +305,30 @@ public class RuleChainController extends BaseController { @@ -281,19 +305,30 @@ public class RuleChainController extends BaseController {
281 305
282 Set<RuleChainId> referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet()); 306 Set<RuleChainId> referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet());
283 307
  308 + List<EdgeId> relatedEdgeIds = null;
  309 + if (RuleChainType.EDGE.equals(ruleChain.getType())) {
  310 + relatedEdgeIds = findRelatedEdgeIds(getTenantId(), ruleChainId);
  311 + }
  312 +
284 ruleChainService.deleteRuleChainById(getTenantId(), ruleChainId); 313 ruleChainService.deleteRuleChainById(getTenantId(), ruleChainId);
285 314
286 referencingRuleChainIds.remove(ruleChain.getId()); 315 referencingRuleChainIds.remove(ruleChain.getId());
287 316
288 - referencingRuleChainIds.forEach(referencingRuleChainId ->  
289 - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED)); 317 + if (RuleChainType.CORE.equals(ruleChain.getType())) {
  318 + referencingRuleChainIds.forEach(referencingRuleChainId ->
  319 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), referencingRuleChainId, ComponentLifecycleEvent.UPDATED));
290 320
291 - tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED); 321 + tbClusterService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
  322 + }
292 323
293 logEntityAction(ruleChainId, ruleChain, 324 logEntityAction(ruleChainId, ruleChain,
294 null, 325 null,
295 ActionType.DELETED, null, strRuleChainId); 326 ActionType.DELETED, null, strRuleChainId);
296 327
  328 + if (RuleChainType.EDGE.equals(ruleChain.getType())) {
  329 + sendDeleteNotificationMsg(ruleChain.getTenantId(), ruleChain.getId(), relatedEdgeIds);
  330 + }
  331 +
297 } catch (Exception e) { 332 } catch (Exception e) {
298 logEntityAction(emptyId(EntityType.RULE_CHAIN), 333 logEntityAction(emptyId(EntityType.RULE_CHAIN),
299 null, 334 null,
@@ -411,7 +446,7 @@ public class RuleChainController extends BaseController { @@ -411,7 +446,7 @@ public class RuleChainController extends BaseController {
411 public void importRuleChains(@RequestBody RuleChainData ruleChainData, @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException { 446 public void importRuleChains(@RequestBody RuleChainData ruleChainData, @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException {
412 try { 447 try {
413 TenantId tenantId = getCurrentUser().getTenantId(); 448 TenantId tenantId = getCurrentUser().getTenantId();
414 - List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite); 449 + List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, RuleChainType.CORE, overwrite);
415 if (!CollectionUtils.isEmpty(importResults)) { 450 if (!CollectionUtils.isEmpty(importResults)) {
416 for (RuleChainImportResult importResult : importResults) { 451 for (RuleChainImportResult importResult : importResults) {
417 tbClusterService.onEntityStateChange(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent()); 452 tbClusterService.onEntityStateChange(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent());
@@ -452,5 +487,160 @@ public class RuleChainController extends BaseController { @@ -452,5 +487,160 @@ public class RuleChainController extends BaseController {
452 return msgData; 487 return msgData;
453 } 488 }
454 489
  490 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  491 + @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.POST)
  492 + @ResponseBody
  493 + public RuleChain assignRuleChainToEdge(@PathVariable("edgeId") String strEdgeId,
  494 + @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
  495 + checkParameter("edgeId", strEdgeId);
  496 + checkParameter(RULE_CHAIN_ID, strRuleChainId);
  497 + try {
  498 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  499 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  500 +
  501 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  502 + checkRuleChain(ruleChainId, Operation.ASSIGN_TO_EDGE);
  503 +
  504 + RuleChain savedRuleChain = checkNotNull(ruleChainService.assignRuleChainToEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId));
  505 +
  506 + logEntityAction(ruleChainId, savedRuleChain,
  507 + null,
  508 + ActionType.ASSIGNED_TO_EDGE, null, strRuleChainId, strEdgeId, edge.getName());
  509 +
  510 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedRuleChain.getId(), EdgeEventActionType.ASSIGNED_TO_EDGE);
  511 +
  512 + return savedRuleChain;
  513 + } catch (Exception e) {
  514 +
  515 + logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
  516 + null,
  517 + ActionType.ASSIGNED_TO_EDGE, e, strRuleChainId, strEdgeId);
  518 +
  519 + throw handleException(e);
  520 + }
  521 + }
  522 +
  523 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  524 + @RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.DELETE)
  525 + @ResponseBody
  526 + public RuleChain unassignRuleChainFromEdge(@PathVariable("edgeId") String strEdgeId,
  527 + @PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
  528 + checkParameter("edgeId", strEdgeId);
  529 + checkParameter(RULE_CHAIN_ID, strRuleChainId);
  530 + try {
  531 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  532 + Edge edge = checkEdgeId(edgeId, Operation.READ);
  533 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  534 + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.UNASSIGN_FROM_EDGE);
  535 +
  536 + RuleChain savedRuleChain = checkNotNull(ruleChainService.unassignRuleChainFromEdge(getCurrentUser().getTenantId(), ruleChainId, edgeId, false));
  537 +
  538 + logEntityAction(ruleChainId, ruleChain,
  539 + null,
  540 + ActionType.UNASSIGNED_FROM_EDGE, null, strRuleChainId, strEdgeId, edge.getName());
  541 +
  542 + sendEntityAssignToEdgeNotificationMsg(getTenantId(), edgeId, savedRuleChain.getId(), EdgeEventActionType.UNASSIGNED_FROM_EDGE);
  543 +
  544 + return savedRuleChain;
  545 + } catch (Exception e) {
  546 +
  547 + logEntityAction(emptyId(EntityType.RULE_CHAIN), null,
  548 + null,
  549 + ActionType.UNASSIGNED_FROM_EDGE, e, strRuleChainId, strEdgeId);
  550 +
  551 + throw handleException(e);
  552 + }
  553 + }
  554 +
  555 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  556 + @RequestMapping(value = "/edge/{edgeId}/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
  557 + @ResponseBody
  558 + public PageData<RuleChain> getEdgeRuleChains(
  559 + @PathVariable("edgeId") String strEdgeId,
  560 + @RequestParam int pageSize,
  561 + @RequestParam int page,
  562 + @RequestParam(required = false) String textSearch,
  563 + @RequestParam(required = false) String sortProperty,
  564 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  565 + checkParameter("edgeId", strEdgeId);
  566 + try {
  567 + TenantId tenantId = getCurrentUser().getTenantId();
  568 + EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
  569 + checkEdgeId(edgeId, Operation.READ);
  570 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  571 + return checkNotNull(ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink));
  572 + } catch (Exception e) {
  573 + throw handleException(e);
  574 + }
  575 + }
  576 +
  577 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  578 + @RequestMapping(value = "/ruleChain/{ruleChainId}/edgeTemplateRoot", method = RequestMethod.POST)
  579 + @ResponseBody
  580 + public RuleChain setEdgeTemplateRootRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
  581 + checkParameter(RULE_CHAIN_ID, strRuleChainId);
  582 + try {
  583 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  584 + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
  585 + ruleChainService.setEdgeTemplateRootRuleChain(getTenantId(), ruleChainId);
  586 + return ruleChain;
  587 + } catch (Exception e) {
  588 + logEntityAction(emptyId(EntityType.RULE_CHAIN),
  589 + null,
  590 + null,
  591 + ActionType.UPDATED, e, strRuleChainId);
  592 + throw handleException(e);
  593 + }
  594 + }
  595 +
  596 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  597 + @RequestMapping(value = "/ruleChain/{ruleChainId}/autoAssignToEdge", method = RequestMethod.POST)
  598 + @ResponseBody
  599 + public RuleChain setAutoAssignToEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
  600 + checkParameter(RULE_CHAIN_ID, strRuleChainId);
  601 + try {
  602 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  603 + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
  604 + ruleChainService.setAutoAssignToEdgeRuleChain(getTenantId(), ruleChainId);
  605 + return ruleChain;
  606 + } catch (Exception e) {
  607 + logEntityAction(emptyId(EntityType.RULE_CHAIN),
  608 + null,
  609 + null,
  610 + ActionType.UPDATED, e, strRuleChainId);
  611 + throw handleException(e);
  612 + }
  613 + }
  614 +
  615 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  616 + @RequestMapping(value = "/ruleChain/{ruleChainId}/autoAssignToEdge", method = RequestMethod.DELETE)
  617 + @ResponseBody
  618 + public RuleChain unsetAutoAssignToEdgeRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
  619 + checkParameter(RULE_CHAIN_ID, strRuleChainId);
  620 + try {
  621 + RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
  622 + RuleChain ruleChain = checkRuleChain(ruleChainId, Operation.WRITE);
  623 + ruleChainService.unsetAutoAssignToEdgeRuleChain(getTenantId(), ruleChainId);
  624 + return ruleChain;
  625 + } catch (Exception e) {
  626 + logEntityAction(emptyId(EntityType.RULE_CHAIN),
  627 + null,
  628 + null,
  629 + ActionType.UPDATED, e, strRuleChainId);
  630 + throw handleException(e);
  631 + }
  632 + }
  633 +
  634 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  635 + @RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET)
  636 + @ResponseBody
  637 + public List<RuleChain> getAutoAssignToEdgeRuleChains() throws ThingsboardException {
  638 + try {
  639 + TenantId tenantId = getCurrentUser().getTenantId();
  640 + return checkNotNull(ruleChainService.findAutoAssignToEdgeRuleChainsByTenantId(tenantId)).get();
  641 + } catch (Exception e) {
  642 + throw handleException(e);
  643 + }
  644 + }
455 645
456 } 646 }
@@ -95,6 +95,9 @@ public class TenantController extends BaseController { @@ -95,6 +95,9 @@ public class TenantController extends BaseController {
95 tenant = checkNotNull(tenantService.saveTenant(tenant)); 95 tenant = checkNotNull(tenantService.saveTenant(tenant));
96 if (newTenant) { 96 if (newTenant) {
97 installScripts.createDefaultRuleChains(tenant.getId()); 97 installScripts.createDefaultRuleChains(tenant.getId());
  98 + if (edgesEnabled) {
  99 + installScripts.createDefaultEdgeRuleChains(tenant.getId());
  100 + }
98 } 101 }
99 tenantProfileCache.evict(tenant.getId()); 102 tenantProfileCache.evict(tenant.getId());
100 tbClusterService.onTenantChange(tenant, null); 103 tbClusterService.onTenantChange(tenant, null);
@@ -36,9 +36,11 @@ import org.thingsboard.rule.engine.api.MailService; @@ -36,9 +36,11 @@ import org.thingsboard.rule.engine.api.MailService;
36 import org.thingsboard.server.common.data.EntityType; 36 import org.thingsboard.server.common.data.EntityType;
37 import org.thingsboard.server.common.data.User; 37 import org.thingsboard.server.common.data.User;
38 import org.thingsboard.server.common.data.audit.ActionType; 38 import org.thingsboard.server.common.data.audit.ActionType;
  39 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
39 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 40 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
40 import org.thingsboard.server.common.data.exception.ThingsboardException; 41 import org.thingsboard.server.common.data.exception.ThingsboardException;
41 import org.thingsboard.server.common.data.id.CustomerId; 42 import org.thingsboard.server.common.data.id.CustomerId;
  43 +import org.thingsboard.server.common.data.id.EdgeId;
42 import org.thingsboard.server.common.data.id.TenantId; 44 import org.thingsboard.server.common.data.id.TenantId;
43 import org.thingsboard.server.common.data.id.UserId; 45 import org.thingsboard.server.common.data.id.UserId;
44 import org.thingsboard.server.common.data.page.PageData; 46 import org.thingsboard.server.common.data.page.PageData;
@@ -57,6 +59,7 @@ import org.thingsboard.server.service.security.permission.Resource; @@ -57,6 +59,7 @@ import org.thingsboard.server.service.security.permission.Resource;
57 import org.thingsboard.server.service.security.system.SystemSecurityService; 59 import org.thingsboard.server.service.security.system.SystemSecurityService;
58 60
59 import javax.servlet.http.HttpServletRequest; 61 import javax.servlet.http.HttpServletRequest;
  62 +import java.util.List;
60 63
61 @RequiredArgsConstructor 64 @RequiredArgsConstructor
62 @RestController 65 @RestController
@@ -178,6 +181,9 @@ public class UserController extends BaseController { @@ -178,6 +181,9 @@ public class UserController extends BaseController {
178 savedUser.getCustomerId(), 181 savedUser.getCustomerId(),
179 user.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 182 user.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
180 183
  184 + sendEntityNotificationMsg(getTenantId(), savedUser.getId(),
  185 + user.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
  186 +
181 return savedUser; 187 return savedUser;
182 } catch (Exception e) { 188 } catch (Exception e) {
183 189
@@ -247,12 +253,17 @@ public class UserController extends BaseController { @@ -247,12 +253,17 @@ public class UserController extends BaseController {
247 try { 253 try {
248 UserId userId = new UserId(toUUID(strUserId)); 254 UserId userId = new UserId(toUUID(strUserId));
249 User user = checkUserId(userId, Operation.DELETE); 255 User user = checkUserId(userId, Operation.DELETE);
  256 +
  257 + List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), userId);
  258 +
250 userService.deleteUser(getCurrentUser().getTenantId(), userId); 259 userService.deleteUser(getCurrentUser().getTenantId(), userId);
251 260
252 logEntityAction(userId, user, 261 logEntityAction(userId, user,
253 user.getCustomerId(), 262 user.getCustomerId(),
254 ActionType.DELETED, null, strUserId); 263 ActionType.DELETED, null, strUserId);
255 264
  265 + sendDeleteNotificationMsg(getTenantId(), userId, relatedEdgeIds);
  266 +
256 } catch (Exception e) { 267 } catch (Exception e) {
257 logEntityAction(emptyId(EntityType.USER), 268 logEntityAction(emptyId(EntityType.USER),
258 null, 269 null,
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import lombok.extern.slf4j.Slf4j;
18 import org.springframework.http.HttpStatus; 19 import org.springframework.http.HttpStatus;
19 import org.springframework.security.access.prepost.PreAuthorize; 20 import org.springframework.security.access.prepost.PreAuthorize;
20 import org.springframework.web.bind.annotation.PathVariable; 21 import org.springframework.web.bind.annotation.PathVariable;
@@ -25,6 +26,8 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -25,6 +26,8 @@ import org.springframework.web.bind.annotation.RequestParam;
25 import org.springframework.web.bind.annotation.ResponseBody; 26 import org.springframework.web.bind.annotation.ResponseBody;
26 import org.springframework.web.bind.annotation.ResponseStatus; 27 import org.springframework.web.bind.annotation.ResponseStatus;
27 import org.springframework.web.bind.annotation.RestController; 28 import org.springframework.web.bind.annotation.RestController;
  29 +import org.thingsboard.server.common.data.EntityType;
  30 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
28 import org.thingsboard.server.common.data.exception.ThingsboardException; 31 import org.thingsboard.server.common.data.exception.ThingsboardException;
29 import org.thingsboard.server.common.data.id.TenantId; 32 import org.thingsboard.server.common.data.id.TenantId;
30 import org.thingsboard.server.common.data.id.WidgetTypeId; 33 import org.thingsboard.server.common.data.id.WidgetTypeId;
@@ -39,6 +42,7 @@ import org.thingsboard.server.service.security.permission.Resource; @@ -39,6 +42,7 @@ import org.thingsboard.server.service.security.permission.Resource;
39 42
40 import java.util.List; 43 import java.util.List;
41 44
  45 +@Slf4j
42 @RestController 46 @RestController
43 @TbCoreComponent 47 @TbCoreComponent
44 @RequestMapping("/api") 48 @RequestMapping("/api")
@@ -69,8 +73,12 @@ public class WidgetTypeController extends BaseController { @@ -69,8 +73,12 @@ public class WidgetTypeController extends BaseController {
69 } 73 }
70 74
71 checkEntity(widgetTypeDetails.getId(), widgetTypeDetails, Resource.WIDGET_TYPE); 75 checkEntity(widgetTypeDetails.getId(), widgetTypeDetails, Resource.WIDGET_TYPE);
  76 + WidgetTypeDetails savedWidgetTypeDetails = widgetTypeService.saveWidgetType(widgetTypeDetails);
72 77
73 - return checkNotNull(widgetTypeService.saveWidgetType(widgetTypeDetails)); 78 + sendEntityNotificationMsg(getTenantId(), savedWidgetTypeDetails.getId(),
  79 + widgetTypeDetails.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
  80 +
  81 + return checkNotNull(savedWidgetTypeDetails);
74 } catch (Exception e) { 82 } catch (Exception e) {
75 throw handleException(e); 83 throw handleException(e);
76 } 84 }
@@ -85,6 +93,9 @@ public class WidgetTypeController extends BaseController { @@ -85,6 +93,9 @@ public class WidgetTypeController extends BaseController {
85 WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId)); 93 WidgetTypeId widgetTypeId = new WidgetTypeId(toUUID(strWidgetTypeId));
86 checkWidgetTypeId(widgetTypeId, Operation.DELETE); 94 checkWidgetTypeId(widgetTypeId, Operation.DELETE);
87 widgetTypeService.deleteWidgetType(getCurrentUser().getTenantId(), widgetTypeId); 95 widgetTypeService.deleteWidgetType(getCurrentUser().getTenantId(), widgetTypeId);
  96 +
  97 + sendEntityNotificationMsg(getTenantId(), widgetTypeId, EdgeEventActionType.DELETED);
  98 +
88 } catch (Exception e) { 99 } catch (Exception e) {
89 throw handleException(e); 100 throw handleException(e);
90 } 101 }
@@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestParam;
25 import org.springframework.web.bind.annotation.ResponseBody; 25 import org.springframework.web.bind.annotation.ResponseBody;
26 import org.springframework.web.bind.annotation.ResponseStatus; 26 import org.springframework.web.bind.annotation.ResponseStatus;
27 import org.springframework.web.bind.annotation.RestController; 27 import org.springframework.web.bind.annotation.RestController;
  28 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
28 import org.thingsboard.server.common.data.exception.ThingsboardException; 29 import org.thingsboard.server.common.data.exception.ThingsboardException;
29 import org.thingsboard.server.common.data.id.TenantId; 30 import org.thingsboard.server.common.data.id.TenantId;
30 import org.thingsboard.server.common.data.id.WidgetsBundleId; 31 import org.thingsboard.server.common.data.id.WidgetsBundleId;
@@ -68,7 +69,12 @@ public class WidgetsBundleController extends BaseController { @@ -68,7 +69,12 @@ public class WidgetsBundleController extends BaseController {
68 } 69 }
69 70
70 checkEntity(widgetsBundle.getId(), widgetsBundle, Resource.WIDGETS_BUNDLE); 71 checkEntity(widgetsBundle.getId(), widgetsBundle, Resource.WIDGETS_BUNDLE);
71 - return checkNotNull(widgetsBundleService.saveWidgetsBundle(widgetsBundle)); 72 + WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle);
  73 +
  74 + sendEntityNotificationMsg(getTenantId(), savedWidgetsBundle.getId(),
  75 + widgetsBundle.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
  76 +
  77 + return checkNotNull(savedWidgetsBundle);
72 } catch (Exception e) { 78 } catch (Exception e) {
73 throw handleException(e); 79 throw handleException(e);
74 } 80 }
@@ -83,6 +89,9 @@ public class WidgetsBundleController extends BaseController { @@ -83,6 +89,9 @@ public class WidgetsBundleController extends BaseController {
83 WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId)); 89 WidgetsBundleId widgetsBundleId = new WidgetsBundleId(toUUID(strWidgetsBundleId));
84 checkWidgetsBundleId(widgetsBundleId, Operation.DELETE); 90 checkWidgetsBundleId(widgetsBundleId, Operation.DELETE);
85 widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId); 91 widgetsBundleService.deleteWidgetsBundle(getTenantId(), widgetsBundleId);
  92 +
  93 + sendEntityNotificationMsg(getTenantId(), widgetsBundleId, EdgeEventActionType.DELETED);
  94 +
86 } catch (Exception e) { 95 } catch (Exception e) {
87 throw handleException(e); 96 throw handleException(e);
88 } 97 }
@@ -27,6 +27,7 @@ import org.springframework.security.authentication.LockedException; @@ -27,6 +27,7 @@ import org.springframework.security.authentication.LockedException;
27 import org.springframework.security.core.AuthenticationException; 27 import org.springframework.security.core.AuthenticationException;
28 import org.springframework.security.web.access.AccessDeniedHandler; 28 import org.springframework.security.web.access.AccessDeniedHandler;
29 import org.springframework.stereotype.Component; 29 import org.springframework.stereotype.Component;
  30 +import org.springframework.web.client.HttpClientErrorException;
30 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 31 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
31 import org.thingsboard.server.common.data.exception.ThingsboardException; 32 import org.thingsboard.server.common.data.exception.ThingsboardException;
32 import org.thingsboard.server.common.msg.tools.TbRateLimitsException; 33 import org.thingsboard.server.common.msg.tools.TbRateLimitsException;
@@ -66,7 +67,12 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { @@ -66,7 +67,12 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
66 response.setContentType(MediaType.APPLICATION_JSON_VALUE); 67 response.setContentType(MediaType.APPLICATION_JSON_VALUE);
67 68
68 if (exception instanceof ThingsboardException) { 69 if (exception instanceof ThingsboardException) {
69 - handleThingsboardException((ThingsboardException) exception, response); 70 + ThingsboardException thingsboardException = (ThingsboardException) exception;
  71 + if (thingsboardException.getErrorCode() == ThingsboardErrorCode.SUBSCRIPTION_VIOLATION) {
  72 + handleSubscriptionException((ThingsboardException) exception, response);
  73 + } else {
  74 + handleThingsboardException((ThingsboardException) exception, response);
  75 + }
70 } else if (exception instanceof TbRateLimitsException) { 76 } else if (exception instanceof TbRateLimitsException) {
71 handleRateLimitException(response, (TbRateLimitsException) exception); 77 handleRateLimitException(response, (TbRateLimitsException) exception);
72 } else if (exception instanceof AccessDeniedException) { 78 } else if (exception instanceof AccessDeniedException) {
@@ -126,6 +132,11 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler { @@ -126,6 +132,11 @@ public class ThingsboardErrorResponseHandler implements AccessDeniedHandler {
126 ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS)); 132 ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS));
127 } 133 }
128 134
  135 + private void handleSubscriptionException(ThingsboardException subscriptionException, HttpServletResponse response) throws IOException {
  136 + response.setStatus(HttpStatus.FORBIDDEN.value());
  137 + mapper.writeValue(response.getWriter(),
  138 + (new ObjectMapper()).readValue(((HttpClientErrorException) subscriptionException.getCause()).getResponseBodyAsByteArray(), Object.class));
  139 + }
129 140
130 private void handleAccessDeniedException(HttpServletResponse response) throws IOException { 141 private void handleAccessDeniedException(HttpServletResponse response) throws IOException {
131 response.setStatus(HttpStatus.FORBIDDEN.value()); 142 response.setStatus(HttpStatus.FORBIDDEN.value());
@@ -193,7 +193,9 @@ public class ThingsboardInstallService { @@ -193,7 +193,9 @@ public class ThingsboardInstallService {
193 databaseEntitiesUpgradeService.upgradeDatabase("3.2.1"); 193 databaseEntitiesUpgradeService.upgradeDatabase("3.2.1");
194 case "3.2.2": 194 case "3.2.2":
195 log.info("Upgrading ThingsBoard from version 3.2.2 to 3.3.0 ..."); 195 log.info("Upgrading ThingsBoard from version 3.2.2 to 3.3.0 ...");
196 - databaseEntitiesUpgradeService.upgradeDatabase("3.2.2"); 196 + databaseEntitiesUpgradeService.upgradeDatabase("3.2.2");
  197 +
  198 + dataUpdateService.updateData("3.2.2");
197 199
198 log.info("Updating system data..."); 200 log.info("Updating system data...");
199 systemDataLoaderService.updateSystemWidgets(); 201 systemDataLoaderService.updateSystemWidgets();
@@ -34,13 +34,13 @@ import org.thingsboard.rule.engine.api.TbRelationTypes; @@ -34,13 +34,13 @@ import org.thingsboard.rule.engine.api.TbRelationTypes;
34 import org.thingsboard.server.common.data.id.TenantId; 34 import org.thingsboard.server.common.data.id.TenantId;
35 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 35 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
36 import org.thingsboard.server.common.data.plugin.ComponentType; 36 import org.thingsboard.server.common.data.plugin.ComponentType;
  37 +import org.thingsboard.server.common.data.rule.RuleChainType;
37 import org.thingsboard.server.dao.component.ComponentDescriptorService; 38 import org.thingsboard.server.dao.component.ComponentDescriptorService;
38 39
39 import javax.annotation.PostConstruct; 40 import javax.annotation.PostConstruct;
40 import java.lang.annotation.Annotation; 41 import java.lang.annotation.Annotation;
41 import java.util.ArrayList; 42 import java.util.ArrayList;
42 import java.util.Arrays; 43 import java.util.Arrays;
43 -import java.util.Collection;  
44 import java.util.Collections; 44 import java.util.Collections;
45 import java.util.HashMap; 45 import java.util.HashMap;
46 import java.util.HashSet; 46 import java.util.HashSet;
@@ -65,7 +65,9 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -65,7 +65,9 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
65 65
66 private Map<String, ComponentDescriptor> components = new HashMap<>(); 66 private Map<String, ComponentDescriptor> components = new HashMap<>();
67 67
68 - private Map<ComponentType, List<ComponentDescriptor>> componentsMap = new HashMap<>(); 68 + private Map<ComponentType, List<ComponentDescriptor>> coreComponentsMap = new HashMap<>();
  69 +
  70 + private Map<ComponentType, List<ComponentDescriptor>> edgeComponentsMap = new HashMap<>();
69 71
70 private ObjectMapper mapper = new ObjectMapper(); 72 private ObjectMapper mapper = new ObjectMapper();
71 73
@@ -93,7 +95,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -93,7 +95,7 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
93 ComponentType type = ruleNodeAnnotation.type(); 95 ComponentType type = ruleNodeAnnotation.type();
94 ComponentDescriptor component = scanAndPersistComponent(def, type); 96 ComponentDescriptor component = scanAndPersistComponent(def, type);
95 components.put(component.getClazz(), component); 97 components.put(component.getClazz(), component);
96 - componentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component); 98 + putComponentIntoMaps(type, ruleNodeAnnotation, component);
97 break; 99 break;
98 } catch (Exception e) { 100 } catch (Exception e) {
99 log.trace("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e); 101 log.trace("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e);
@@ -113,22 +115,35 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -113,22 +115,35 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
113 } 115 }
114 } 116 }
115 117
116 - private void registerComponents(ComponentType type, Class<? extends Annotation> annotation) {  
117 - List<ComponentDescriptor> components = persist(getBeanDefinitions(annotation), type);  
118 - componentsMap.put(type, components);  
119 - registerComponents(components);  
120 - }  
121 -  
122 - private void registerComponents(Collection<ComponentDescriptor> comps) {  
123 - comps.forEach(c -> components.put(c.getClazz(), c)); 118 + private void putComponentIntoMaps(ComponentType type, RuleNode ruleNodeAnnotation, ComponentDescriptor component) {
  119 + boolean ruleChainTypesMethodAvailable;
  120 + try {
  121 + ruleNodeAnnotation.getClass().getMethod("ruleChainTypes");
  122 + ruleChainTypesMethodAvailable = true;
  123 + } catch (NoSuchMethodException exception) {
  124 + log.warn("[{}] does not have ruleChainTypes. Probably extension class compiled before 3.3 release. " +
  125 + "Please update your extensions and compile using latest 3.3 release dependency", ruleNodeAnnotation.name());
  126 + ruleChainTypesMethodAvailable = false;
  127 + }
  128 + if (ruleChainTypesMethodAvailable) {
  129 + if (ruleChainTypeContainsArray(RuleChainType.CORE, ruleNodeAnnotation.ruleChainTypes())) {
  130 + coreComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
  131 + }
  132 + if (ruleChainTypeContainsArray(RuleChainType.EDGE, ruleNodeAnnotation.ruleChainTypes())) {
  133 + edgeComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
  134 + }
  135 + } else {
  136 + coreComponentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
  137 + }
124 } 138 }
125 139
126 - private List<ComponentDescriptor> persist(Set<BeanDefinition> filterDefs, ComponentType type) {  
127 - List<ComponentDescriptor> result = new ArrayList<>();  
128 - for (BeanDefinition def : filterDefs) {  
129 - result.add(scanAndPersistComponent(def, type)); 140 + private boolean ruleChainTypeContainsArray(RuleChainType ruleChainType, RuleChainType[] array) {
  141 + for (RuleChainType tmp : array) {
  142 + if (ruleChainType.equals(tmp)) {
  143 + return true;
  144 + }
130 } 145 }
131 - return result; 146 + return false;
132 } 147 }
133 148
134 private ComponentDescriptor scanAndPersistComponent(BeanDefinition def, ComponentType type) { 149 private ComponentDescriptor scanAndPersistComponent(BeanDefinition def, ComponentType type) {
@@ -222,25 +237,47 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -222,25 +237,47 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
222 } 237 }
223 238
224 @Override 239 @Override
225 - public List<ComponentDescriptor> getComponents(ComponentType type) {  
226 - if (componentsMap.containsKey(type)) {  
227 - return Collections.unmodifiableList(componentsMap.get(type)); 240 + public List<ComponentDescriptor> getComponents(ComponentType type, RuleChainType ruleChainType) {
  241 + if (RuleChainType.CORE.equals(ruleChainType)) {
  242 + if (coreComponentsMap.containsKey(type)) {
  243 + return Collections.unmodifiableList(coreComponentsMap.get(type));
  244 + } else {
  245 + return Collections.emptyList();
  246 + }
  247 + } else if (RuleChainType.EDGE.equals(ruleChainType)) {
  248 + if (edgeComponentsMap.containsKey(type)) {
  249 + return Collections.unmodifiableList(edgeComponentsMap.get(type));
  250 + } else {
  251 + return Collections.emptyList();
  252 + }
228 } else { 253 } else {
229 - return Collections.emptyList(); 254 + log.error("Unsupported rule chain type {}", ruleChainType);
  255 + throw new RuntimeException("Unsupported rule chain type " + ruleChainType);
230 } 256 }
231 } 257 }
232 258
233 @Override 259 @Override
234 - public List<ComponentDescriptor> getComponents(Set<ComponentType> types) {  
235 - List<ComponentDescriptor> result = new ArrayList<>();  
236 - types.stream().filter(type -> componentsMap.containsKey(type)).forEach(type -> {  
237 - result.addAll(componentsMap.get(type));  
238 - });  
239 - return Collections.unmodifiableList(result); 260 + public List<ComponentDescriptor> getComponents(Set<ComponentType> types, RuleChainType ruleChainType) {
  261 + if (RuleChainType.CORE.equals(ruleChainType)) {
  262 + return getComponents(types, coreComponentsMap);
  263 + } else if (RuleChainType.EDGE.equals(ruleChainType)) {
  264 + return getComponents(types, edgeComponentsMap);
  265 + } else {
  266 + log.error("Unsupported rule chain type {}", ruleChainType);
  267 + throw new RuntimeException("Unsupported rule chain type " + ruleChainType);
  268 + }
240 } 269 }
241 270
242 @Override 271 @Override
243 public Optional<ComponentDescriptor> getComponent(String clazz) { 272 public Optional<ComponentDescriptor> getComponent(String clazz) {
244 return Optional.ofNullable(components.get(clazz)); 273 return Optional.ofNullable(components.get(clazz));
245 } 274 }
  275 +
  276 + private List<ComponentDescriptor> getComponents(Set<ComponentType> types, Map<ComponentType, List<ComponentDescriptor>> componentsMap) {
  277 + List<ComponentDescriptor> result = new ArrayList<>();
  278 + types.stream().filter(componentsMap::containsKey).forEach(type -> {
  279 + result.addAll(componentsMap.get(type));
  280 + });
  281 + return Collections.unmodifiableList(result);
  282 + }
246 } 283 }
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.component; @@ -17,6 +17,7 @@ package org.thingsboard.server.service.component;
17 17
18 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 18 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
19 import org.thingsboard.server.common.data.plugin.ComponentType; 19 import org.thingsboard.server.common.data.plugin.ComponentType;
  20 +import org.thingsboard.server.common.data.rule.RuleChainType;
20 21
21 import java.util.List; 22 import java.util.List;
22 import java.util.Optional; 23 import java.util.Optional;
@@ -29,10 +30,9 @@ public interface ComponentDiscoveryService { @@ -29,10 +30,9 @@ public interface ComponentDiscoveryService {
29 30
30 void discoverComponents(); 31 void discoverComponents();
31 32
32 - List<ComponentDescriptor> getComponents(ComponentType type); 33 + List<ComponentDescriptor> getComponents(ComponentType type, RuleChainType ruleChainType);
33 34
34 - List<ComponentDescriptor> getComponents(Set<ComponentType> types); 35 + List<ComponentDescriptor> getComponents(Set<ComponentType> types, RuleChainType ruleChainType);
35 36
36 Optional<ComponentDescriptor> getComponent(String clazz); 37 Optional<ComponentDescriptor> getComponent(String clazz);
37 -  
38 } 38 }
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.edge;
  17 +
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.databind.JsonNode;
  20 +import com.fasterxml.jackson.databind.ObjectMapper;
  21 +import com.google.common.util.concurrent.FutureCallback;
  22 +import com.google.common.util.concurrent.Futures;
  23 +import com.google.common.util.concurrent.ListenableFuture;
  24 +import lombok.extern.slf4j.Slf4j;
  25 +import org.checkerframework.checker.nullness.qual.Nullable;
  26 +import org.springframework.beans.factory.annotation.Autowired;
  27 +import org.springframework.stereotype.Service;
  28 +import org.thingsboard.server.common.data.EdgeUtils;
  29 +import org.thingsboard.server.common.data.EntityType;
  30 +import org.thingsboard.server.common.data.User;
  31 +import org.thingsboard.server.common.data.alarm.Alarm;
  32 +import org.thingsboard.server.common.data.edge.Edge;
  33 +import org.thingsboard.server.common.data.edge.EdgeEvent;
  34 +import org.thingsboard.server.common.data.edge.EdgeEventActionType;
  35 +import org.thingsboard.server.common.data.edge.EdgeEventType;
  36 +import org.thingsboard.server.common.data.id.AlarmId;
  37 +import org.thingsboard.server.common.data.id.CustomerId;
  38 +import org.thingsboard.server.common.data.id.EdgeId;
  39 +import org.thingsboard.server.common.data.id.EntityId;
  40 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  41 +import org.thingsboard.server.common.data.id.RuleChainId;
  42 +import org.thingsboard.server.common.data.id.TenantId;
  43 +import org.thingsboard.server.common.data.page.PageData;
  44 +import org.thingsboard.server.common.data.page.PageLink;
  45 +import org.thingsboard.server.common.data.page.TimePageLink;
  46 +import org.thingsboard.server.common.data.relation.EntityRelation;
  47 +import org.thingsboard.server.common.data.rule.RuleChain;
  48 +import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
  49 +import org.thingsboard.server.common.msg.queue.TbCallback;
  50 +import org.thingsboard.server.dao.alarm.AlarmService;
  51 +import org.thingsboard.server.dao.edge.EdgeEventService;
  52 +import org.thingsboard.server.dao.edge.EdgeService;
  53 +import org.thingsboard.server.dao.rule.RuleChainService;
  54 +import org.thingsboard.server.dao.user.UserService;
  55 +import org.thingsboard.server.gen.transport.TransportProtos;
  56 +import org.thingsboard.server.queue.util.TbCoreComponent;
  57 +import org.thingsboard.server.service.executors.DbCallbackExecutorService;
  58 +import org.thingsboard.server.service.queue.TbClusterService;
  59 +
  60 +import javax.annotation.PostConstruct;
  61 +import javax.annotation.PreDestroy;
  62 +import java.io.IOException;
  63 +import java.util.ArrayList;
  64 +import java.util.HashSet;
  65 +import java.util.List;
  66 +import java.util.Set;
  67 +import java.util.UUID;
  68 +import java.util.concurrent.ExecutorService;
  69 +import java.util.concurrent.Executors;
  70 +
  71 +@Service
  72 +@TbCoreComponent
  73 +@Slf4j
  74 +public class DefaultEdgeNotificationService implements EdgeNotificationService {
  75 +
  76 + private static final ObjectMapper mapper = new ObjectMapper();
  77 +
  78 + private static final int DEFAULT_LIMIT = 100;
  79 +
  80 + @Autowired
  81 + private EdgeService edgeService;
  82 +
  83 + @Autowired
  84 + private AlarmService alarmService;
  85 +
  86 + @Autowired
  87 + private UserService userService;
  88 +
  89 + @Autowired
  90 + private RuleChainService ruleChainService;
  91 +
  92 + @Autowired
  93 + private EdgeEventService edgeEventService;
  94 +
  95 + @Autowired
  96 + private TbClusterService clusterService;
  97 +
  98 + @Autowired
  99 + private DbCallbackExecutorService dbCallbackExecutorService;
  100 +
  101 + private ExecutorService tsCallBackExecutor;
  102 +
  103 + @PostConstruct
  104 + public void initExecutor() {
  105 + tsCallBackExecutor = Executors.newSingleThreadExecutor();
  106 + }
  107 +
  108 + @PreDestroy
  109 + public void shutdownExecutor() {
  110 + if (tsCallBackExecutor != null) {
  111 + tsCallBackExecutor.shutdownNow();
  112 + }
  113 + }
  114 +
  115 + @Override
  116 + public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException {
  117 + edge.setRootRuleChainId(ruleChainId);
  118 + Edge savedEdge = edgeService.saveEdge(edge);
  119 + saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null);
  120 + return savedEdge;
  121 + }
  122 +
  123 + private void saveEdgeEvent(TenantId tenantId,
  124 + EdgeId edgeId,
  125 + EdgeEventType type,
  126 + EdgeEventActionType action,
  127 + EntityId entityId,
  128 + JsonNode body) {
  129 + log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]",
  130 + tenantId, edgeId, type, action, entityId, body);
  131 +
  132 + EdgeEvent edgeEvent = new EdgeEvent();
  133 + edgeEvent.setEdgeId(edgeId);
  134 + edgeEvent.setTenantId(tenantId);
  135 + edgeEvent.setType(type);
  136 + edgeEvent.setAction(action);
  137 + if (entityId != null) {
  138 + edgeEvent.setEntityId(entityId.getId());
  139 + }
  140 + edgeEvent.setBody(body);
  141 + ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent);
  142 + Futures.addCallback(future, new FutureCallback<EdgeEvent>() {
  143 + @Override
  144 + public void onSuccess(@Nullable EdgeEvent result) {
  145 + clusterService.onEdgeEventUpdate(tenantId, edgeId);
  146 + }
  147 +
  148 + @Override
  149 + public void onFailure(Throwable t) {
  150 + log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t);
  151 + }
  152 + }, dbCallbackExecutorService);
  153 +
  154 + }
  155 +
  156 + @Override
  157 + public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) {
  158 + try {
  159 + TenantId tenantId = new TenantId(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB()));
  160 + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
  161 + switch (type) {
  162 + case EDGE:
  163 + processEdge(tenantId, edgeNotificationMsg);
  164 + break;
  165 + case USER:
  166 + case ASSET:
  167 + case DEVICE:
  168 + case DEVICE_PROFILE:
  169 + case ENTITY_VIEW:
  170 + case DASHBOARD:
  171 + case RULE_CHAIN:
  172 + processEntity(tenantId, edgeNotificationMsg);
  173 + break;
  174 + case CUSTOMER:
  175 + processCustomer(tenantId, edgeNotificationMsg);
  176 + break;
  177 + case WIDGETS_BUNDLE:
  178 + case WIDGET_TYPE:
  179 + processWidgetBundleOrWidgetType(tenantId, edgeNotificationMsg);
  180 + break;
  181 + case ALARM:
  182 + processAlarm(tenantId, edgeNotificationMsg);
  183 + break;
  184 + case RELATION:
  185 + processRelation(tenantId, edgeNotificationMsg);
  186 + break;
  187 + default:
  188 + log.debug("Edge event type [{}] is not designed to be pushed to edge", type);
  189 + }
  190 + } catch (Exception e) {
  191 + callback.onFailure(e);
  192 + log.error("Can't push to edge updates, edgeNotificationMsg [{}]", edgeNotificationMsg, e);
  193 + } finally {
  194 + callback.onSuccess();
  195 + }
  196 + }
  197 +
  198 + private void processEdge(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  199 + try {
  200 + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
  201 + EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
  202 + ListenableFuture<Edge> edgeFuture;
  203 + switch (actionType) {
  204 + case ASSIGNED_TO_CUSTOMER:
  205 + CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
  206 + edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
  207 + Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
  208 + @Override
  209 + public void onSuccess(@Nullable Edge edge) {
  210 + if (edge != null && !customerId.isNullUid()) {
  211 + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null);
  212 + PageLink pageLink = new PageLink(DEFAULT_LIMIT);
  213 + PageData<User> pageData;
  214 + do {
  215 + pageData = userService.findCustomerUsers(tenantId, customerId, pageLink);
  216 + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
  217 + log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size());
  218 + for (User user : pageData.getData()) {
  219 + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null);
  220 + }
  221 + if (pageData.hasNext()) {
  222 + pageLink = pageLink.nextPageLink();
  223 + }
  224 + }
  225 + } while (pageData != null && pageData.hasNext());
  226 + }
  227 + }
  228 +
  229 + @Override
  230 + public void onFailure(Throwable t) {
  231 + log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
  232 + }
  233 + }, dbCallbackExecutorService);
  234 + break;
  235 + case UNASSIGNED_FROM_CUSTOMER:
  236 + CustomerId customerIdToDelete = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
  237 + edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId);
  238 + Futures.addCallback(edgeFuture, new FutureCallback<Edge>() {
  239 + @Override
  240 + public void onSuccess(@Nullable Edge edge) {
  241 + if (edge != null && !customerIdToDelete.isNullUid()) {
  242 + saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.DELETED, customerIdToDelete, null);
  243 + }
  244 + }
  245 +
  246 + @Override
  247 + public void onFailure(Throwable t) {
  248 + log.error("Can't find edge by id [{}]", edgeNotificationMsg, t);
  249 + }
  250 + }, dbCallbackExecutorService);
  251 + break;
  252 + }
  253 + } catch (Exception e) {
  254 + log.error("Exception during processing edge event", e);
  255 + }
  256 + }
  257 +
  258 + private void processWidgetBundleOrWidgetType(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  259 + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
  260 + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
  261 + EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
  262 + switch (actionType) {
  263 + case ADDED:
  264 + case UPDATED:
  265 + case DELETED:
  266 + processActionForAllEdges(tenantId, type, actionType, entityId);
  267 + break;
  268 + }
  269 + }
  270 +
  271 + private void processCustomer(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  272 + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
  273 + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
  274 + UUID uuid = new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB());
  275 + CustomerId customerId = new CustomerId(EntityIdFactory.getByEdgeEventTypeAndUuid(type, uuid).getId());
  276 + switch (actionType) {
  277 + case UPDATED:
  278 + PageLink pageLink = new PageLink(DEFAULT_LIMIT);
  279 + PageData<Edge> pageData;
  280 + do {
  281 + pageData = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  282 + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
  283 + for (Edge edge : pageData.getData()) {
  284 + saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null);
  285 + }
  286 + if (pageData.hasNext()) {
  287 + pageLink = pageLink.nextPageLink();
  288 + }
  289 + }
  290 + } while (pageData != null && pageData.hasNext());
  291 + break;
  292 + case DELETED:
  293 + EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
  294 + saveEdgeEvent(tenantId, edgeId, type, actionType, customerId, null);
  295 + break;
  296 + }
  297 + }
  298 +
  299 + private void processEntity(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  300 + EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction());
  301 + EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType());
  302 + EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type,
  303 + new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
  304 + EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB()));
  305 + ListenableFuture<List<EdgeId>> edgeIdsFuture;
  306 + switch (actionType) {
  307 + case ADDED: // used only for USER entity
  308 + case UPDATED:
  309 + case CREDENTIALS_UPDATED:
  310 + edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
  311 + Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
  312 + @Override
  313 + public void onSuccess(@Nullable List<EdgeId> edgeIds) {
  314 + if (edgeIds != null && !edgeIds.isEmpty()) {
  315 + for (EdgeId edgeId : edgeIds) {
  316 + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
  317 + }
  318 + }
  319 + }
  320 + @Override
  321 + public void onFailure(Throwable throwable) {
  322 + log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);
  323 + }
  324 + }, dbCallbackExecutorService);
  325 + break;
  326 + case ASSIGNED_TO_CUSTOMER:
  327 + case UNASSIGNED_FROM_CUSTOMER:
  328 + edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId);
  329 + Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() {
  330 + @Override
  331 + public void onSuccess(@Nullable List<EdgeId> edgeIds) {
  332 + if (edgeIds != null && !edgeIds.isEmpty()) {
  333 + for (EdgeId edgeId : edgeIds) {
  334 + try {
  335 + CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class);
  336 + ListenableFuture<Edge> future = edgeService.findEdgeByIdAsync(tenantId, edgeId);
  337 + Futures.addCallback(future, new FutureCallback<Edge>() {
  338 + @Override
  339 + public void onSuccess(@Nullable Edge edge) {
  340 + if (edge != null && edge.getCustomerId() != null &&
  341 + !edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) {
  342 + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
  343 + }
  344 + }
  345 + @Override
  346 + public void onFailure(Throwable throwable) {
  347 + log.error("Failed to find edge by id [{}]", edgeNotificationMsg, throwable);
  348 + }
  349 + }, dbCallbackExecutorService);
  350 + } catch (Exception e) {
  351 + log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e);
  352 + }
  353 + }
  354 + }
  355 + }
  356 +
  357 + @Override
  358 + public void onFailure(Throwable throwable) {
  359 + log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable);
  360 + }
  361 + }, dbCallbackExecutorService);
  362 + break;
  363 + case DELETED:
  364 + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
  365 + break;
  366 + case ASSIGNED_TO_EDGE:
  367 + case UNASSIGNED_FROM_EDGE:
  368 + saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null);
  369 + if (type.equals(EdgeEventType.RULE_CHAIN)) {
  370 + updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId);
  371 + }
  372 + break;
  373 + }
  374 + }
  375 +
  376 + private void updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) {
  377 + PageLink pageLink = new PageLink(DEFAULT_LIMIT);
  378 + PageData<RuleChain> pageData;
  379 + do {
  380 + pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink);
  381 + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
  382 + for (RuleChain ruleChain : pageData.getData()) {
  383 + if (!ruleChain.getId().equals(processingRuleChainId)) {
  384 + List<RuleChainConnectionInfo> connectionInfos =
  385 + ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections();
  386 + if (connectionInfos != null && !connectionInfos.isEmpty()) {
  387 + for (RuleChainConnectionInfo connectionInfo : connectionInfos) {
  388 + if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) {
  389 + saveEdgeEvent(tenantId,
  390 + edgeId,
  391 + EdgeEventType.RULE_CHAIN_METADATA,
  392 + EdgeEventActionType.UPDATED,
  393 + ruleChain.getId(),
  394 + null);
  395 + }
  396 + }
  397 + }
  398 + }
  399 + }
  400 + if (pageData.hasNext()) {
  401 + pageLink = pageLink.nextPageLink();
  402 + }
  403 + }
  404 + } while (pageData != null && pageData.hasNext());
  405 + }
  406 +
  407 + private void processAlarm(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) {
  408 + AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()));
  409 + ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId);
  410 + Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() {
  411 + @Override
  412 + public void onSuccess(@Nullable Alarm alarm) {
  413 + if (alarm != null) {
  414 + EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType());
  415 + if (type != null) {
  416 + ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator());
  417 + Futures.addCallback(relatedEdgeIdsByEntityIdFuture, new FutureCallback<List<EdgeId>>() {
  418 + @Override
  419 + public void onSuccess(@Nullable List<EdgeId> relatedEdgeIdsByEntityId) {
  420 + if (relatedEdgeIdsByEntityId != null) {
  421 + for (EdgeId edgeId : relatedEdgeIdsByEntityId) {
  422 + saveEdgeEvent(tenantId,
  423 + edgeId,
  424 + EdgeEventType.ALARM,
  425 + EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
  426 + alarmId,
  427 + null);
  428 + }
  429 + }
  430 + }
  431 +
  432 + @Override
  433 + public void onFailure(Throwable t) {
  434 + log.warn("[{}] can't find related edge ids by entity id [{}]", tenantId.getId(), alarm.getOriginator(), t);
  435 + }
  436 + }, dbCallbackExecutorService);
  437 + }
  438 + }
  439 + }
  440 +
  441 + @Override
  442 + public void onFailure(Throwable t) {
  443 + log.warn("[{}] can't find alarm by id [{}]", tenantId.getId(), alarmId.getId(), t);
  444 + }
  445 + }, dbCallbackExecutorService);
  446 + }
  447 +
  448 + private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException {
  449 + EntityRelation relation = mapper.readValue(edgeNotificationMsg.getBody(), EntityRelation.class);
  450 + if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) &&
  451 + !relation.getTo().getEntityType().equals(EntityType.EDGE)) {
  452 + List<ListenableFuture<List<EdgeId>>> futures = new ArrayList<>();
  453 + futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getTo()));
  454 + futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getFrom()));
  455 + ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures);
  456 + Futures.addCallback(combinedFuture, new FutureCallback<List<List<EdgeId>>>() {
  457 + @Override
  458 + public void onSuccess(@Nullable List<List<EdgeId>> listOfListsEdgeIds) {
  459 + Set<EdgeId> uniqueEdgeIds = new HashSet<>();
  460 + if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) {
  461 + for (List<EdgeId> listOfListsEdgeId : listOfListsEdgeIds) {
  462 + if (listOfListsEdgeId != null) {
  463 + uniqueEdgeIds.addAll(listOfListsEdgeId);
  464 + }
  465 + }
  466 + }
  467 + if (!uniqueEdgeIds.isEmpty()) {
  468 + for (EdgeId edgeId : uniqueEdgeIds) {
  469 + saveEdgeEvent(tenantId,
  470 + edgeId,
  471 + EdgeEventType.RELATION,
  472 + EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()),
  473 + null,
  474 + mapper.valueToTree(relation));
  475 + }
  476 + }
  477 + }
  478 +
  479 + @Override
  480 + public void onFailure(Throwable t) {
  481 + log.warn("[{}] can't find related edge ids by relation to id [{}] and relation from id [{}]" ,
  482 + tenantId.getId(), relation.getTo().getId(), relation.getFrom().getId(), t);
  483 + }
  484 + }, dbCallbackExecutorService);
  485 + }
  486 + }
  487 +
  488 + private void processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) {
  489 + PageLink pageLink = new PageLink(DEFAULT_LIMIT);
  490 + PageData<Edge> pageData;
  491 + do {
  492 + pageData = edgeService.findEdgesByTenantId(tenantId, pageLink);
  493 + if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) {
  494 + for (Edge edge : pageData.getData()) {
  495 + saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null);
  496 + }
  497 + if (pageData.hasNext()) {
  498 + pageLink = pageLink.nextPageLink();
  499 + }
  500 + }
  501 + } while (pageData != null && pageData.hasNext());
  502 + }
  503 +}
  504 +
  505 +
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.edge;
  17 +
  18 +import lombok.Data;
  19 +import lombok.Getter;
  20 +import org.springframework.beans.factory.annotation.Autowired;
  21 +import org.springframework.context.annotation.Lazy;
  22 +import org.springframework.stereotype.Component;
  23 +import org.thingsboard.server.actors.service.ActorService;
  24 +import org.thingsboard.server.dao.alarm.AlarmService;
  25 +import org.thingsboard.server.dao.asset.AssetService;
  26 +import org.thingsboard.server.dao.attributes.AttributesService;
  27 +import org.thingsboard.server.dao.customer.CustomerService;
  28 +import org.thingsboard.server.dao.dashboard.DashboardService;
  29 +import org.thingsboard.server.dao.device.DeviceCredentialsService;
  30 +import org.thingsboard.server.dao.device.DeviceProfileService;
  31 +import org.thingsboard.server.dao.device.DeviceService;
  32 +import org.thingsboard.server.dao.edge.EdgeEventService;
  33 +import org.thingsboard.server.dao.edge.EdgeService;
  34 +import org.thingsboard.server.dao.entityview.EntityViewService;
  35 +import org.thingsboard.server.dao.relation.RelationService;
  36 +import org.thingsboard.server.dao.rule.RuleChainService;
  37 +import org.thingsboard.server.dao.user.UserService;
  38 +import org.thingsboard.server.dao.widget.WidgetTypeService;
  39 +import org.thingsboard.server.dao.widget.WidgetsBundleService;
  40 +import org.thingsboard.server.queue.discovery.PartitionService;
  41 +import org.thingsboard.server.queue.util.TbCoreComponent;
  42 +import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings;
  43 +import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor;
  44 +import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor;
  45 +import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor;
  46 +import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor;
  47 +import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor;
  48 +import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor;
  49 +import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor;
  50 +import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor;
  51 +import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor;
  52 +import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor;
  53 +import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor;
  54 +import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor;
  55 +import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor;
  56 +import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor;
  57 +import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService;
  58 +import org.thingsboard.server.service.edge.rpc.processor.AlarmProcessor;
  59 +import org.thingsboard.server.service.edge.rpc.processor.DeviceProcessor;
  60 +import org.thingsboard.server.service.edge.rpc.processor.RelationProcessor;
  61 +import org.thingsboard.server.service.edge.rpc.processor.TelemetryProcessor;
  62 +import org.thingsboard.server.service.executors.DbCallbackExecutorService;
  63 +import org.thingsboard.server.service.queue.TbClusterService;
  64 +import org.thingsboard.server.service.state.DeviceStateService;
  65 +
  66 +@Component
  67 +@TbCoreComponent
  68 +@Data
  69 +public class EdgeContextComponent {
  70 +
  71 + @Lazy
  72 + @Autowired
  73 + private EdgeService edgeService;
  74 +
  75 + @Autowired
  76 + private PartitionService partitionService;
  77 +
  78 + @Lazy
  79 + @Autowired
  80 + private EdgeEventService edgeEventService;
  81 +
  82 + @Lazy
  83 + @Autowired
  84 + private AssetService assetService;
  85 +
  86 + @Lazy
  87 + @Autowired
  88 + private DeviceService deviceService;
  89 +
  90 + @Lazy
  91 + @Autowired
  92 + private DeviceProfileService deviceProfileService;
  93 +
  94 + @Lazy
  95 + @Autowired
  96 + private DeviceCredentialsService deviceCredentialsService;
  97 +
  98 + @Lazy
  99 + @Autowired
  100 + private EntityViewService entityViewService;
  101 +
  102 + @Lazy
  103 + @Autowired
  104 + private AttributesService attributesService;
  105 +
  106 + @Lazy
  107 + @Autowired
  108 + private CustomerService customerService;
  109 +
  110 + @Lazy
  111 + @Autowired
  112 + private RelationService relationService;
  113 +
  114 + @Lazy
  115 + @Autowired
  116 + private AlarmService alarmService;
  117 +
  118 + @Lazy
  119 + @Autowired
  120 + private DashboardService dashboardService;
  121 +
  122 + @Lazy
  123 + @Autowired
  124 + private RuleChainService ruleChainService;
  125 +
  126 + @Lazy
  127 + @Autowired
  128 + private UserService userService;
  129 +
  130 + @Lazy
  131 + @Autowired
  132 + private ActorService actorService;
  133 +
  134 + @Lazy
  135 + @Autowired
  136 + private WidgetsBundleService widgetsBundleService;
  137 +
  138 + @Lazy
  139 + @Autowired
  140 + private WidgetTypeService widgetTypeService;
  141 +
  142 + @Lazy
  143 + @Autowired
  144 + private DeviceStateService deviceStateService;
  145 +
  146 + @Lazy
  147 + @Autowired
  148 + private TbClusterService tbClusterService;
  149 +
  150 + @Lazy
  151 + @Autowired
  152 + private SyncEdgeService syncEdgeService;
  153 +
  154 + @Lazy
  155 + @Autowired
  156 + private RuleChainMsgConstructor ruleChainMsgConstructor;
  157 +
  158 + @Lazy
  159 + @Autowired
  160 + private AlarmMsgConstructor alarmMsgConstructor;
  161 +
  162 + @Lazy
  163 + @Autowired
  164 + private DeviceMsgConstructor deviceMsgConstructor;
  165 +
  166 + @Lazy
  167 + @Autowired
  168 + private DeviceProfileMsgConstructor deviceProfileMsgConstructor;
  169 +
  170 + @Lazy
  171 + @Autowired
  172 + private AssetMsgConstructor assetMsgConstructor;
  173 +
  174 + @Lazy
  175 + @Autowired
  176 + private EntityViewMsgConstructor entityViewMsgConstructor;
  177 +
  178 + @Lazy
  179 + @Autowired
  180 + private DashboardMsgConstructor dashboardMsgConstructor;
  181 +
  182 + @Lazy
  183 + @Autowired
  184 + private CustomerMsgConstructor customerMsgConstructor;
  185 +
  186 + @Lazy
  187 + @Autowired
  188 + private UserMsgConstructor userMsgConstructor;
  189 +
  190 + @Lazy
  191 + @Autowired
  192 + private RelationMsgConstructor relationMsgConstructor;
  193 +
  194 + @Lazy
  195 + @Autowired
  196 + private WidgetsBundleMsgConstructor widgetsBundleMsgConstructor;
  197 +
  198 + @Lazy
  199 + @Autowired
  200 + private WidgetTypeMsgConstructor widgetTypeMsgConstructor;
  201 +
  202 + @Lazy
  203 + @Autowired
  204 + private AdminSettingsMsgConstructor adminSettingsMsgConstructor;
  205 +
  206 + @Lazy
  207 + @Autowired
  208 + private EntityDataMsgConstructor entityDataMsgConstructor;
  209 +
  210 + @Lazy
  211 + @Autowired
  212 + private AlarmProcessor alarmProcessor;
  213 +
  214 + @Lazy
  215 + @Autowired
  216 + private DeviceProcessor deviceProcessor;
  217 +
  218 + @Lazy
  219 + @Autowired
  220 + private RelationProcessor relationProcessor;
  221 +
  222 + @Lazy
  223 + @Autowired
  224 + private TelemetryProcessor telemetryProcessor;
  225 +
  226 + @Lazy
  227 + @Autowired
  228 + private EdgeEventStorageSettings edgeEventStorageSettings;
  229 +
  230 + @Autowired
  231 + @Getter
  232 + private DbCallbackExecutorService dbCallbackExecutor;
  233 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.edge;
  17 +
  18 +import org.thingsboard.server.common.data.edge.Edge;
  19 +import org.thingsboard.server.common.data.id.RuleChainId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.msg.queue.TbCallback;
  22 +import org.thingsboard.server.gen.transport.TransportProtos;
  23 +
  24 +import java.io.IOException;
  25 +
  26 +public interface EdgeNotificationService {
  27 +
  28 + Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException;
  29 +
  30 + void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback);
  31 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.edge.rpc;
  17 +
  18 +
  19 +import lombok.Data;
  20 +import org.springframework.beans.factory.annotation.Value;
  21 +import org.springframework.stereotype.Component;
  22 +
  23 +@Component
  24 +@Data
  25 +public class EdgeEventStorageSettings {
  26 + @Value("${edges.storage.max_read_records_count}")
  27 + private int maxReadRecordsCount;
  28 + @Value("${edges.storage.no_read_records_sleep}")
  29 + private long noRecordsSleepInterval;
  30 + @Value("${edges.storage.sleep_between_batches}")
  31 + private long sleepIntervalBetweenBatches;
  32 +}
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.edge.rpc;
  17 +
  18 +import com.fasterxml.jackson.databind.ObjectMapper;
  19 +import com.google.common.io.Resources;
  20 +import com.google.common.util.concurrent.FutureCallback;
  21 +import io.grpc.Server;
  22 +import io.grpc.netty.NettyServerBuilder;
  23 +import io.grpc.stub.StreamObserver;
  24 +import lombok.extern.slf4j.Slf4j;
  25 +import org.springframework.beans.factory.annotation.Autowired;
  26 +import org.springframework.beans.factory.annotation.Value;
  27 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  28 +import org.springframework.stereotype.Service;
  29 +import org.thingsboard.common.util.ThingsBoardThreadFactory;
  30 +import org.thingsboard.server.common.data.DataConstants;
  31 +import org.thingsboard.server.common.data.Tenant;
  32 +import org.thingsboard.server.common.data.edge.Edge;
  33 +import org.thingsboard.server.common.data.id.EdgeId;
  34 +import org.thingsboard.server.common.data.id.TenantId;
  35 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  36 +import org.thingsboard.server.common.data.kv.BooleanDataEntry;
  37 +import org.thingsboard.server.common.data.kv.LongDataEntry;
  38 +import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc;
  39 +import org.thingsboard.server.gen.edge.RequestMsg;
  40 +import org.thingsboard.server.gen.edge.ResponseMsg;
  41 +import org.thingsboard.server.queue.util.TbCoreComponent;
  42 +import org.thingsboard.server.service.edge.EdgeContextComponent;
  43 +import org.thingsboard.server.service.state.DefaultDeviceStateService;
  44 +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
  45 +
  46 +import javax.annotation.Nullable;
  47 +import javax.annotation.PostConstruct;
  48 +import javax.annotation.PreDestroy;
  49 +import java.io.File;
  50 +import java.io.IOException;
  51 +import java.util.Collections;
  52 +import java.util.Map;
  53 +import java.util.UUID;
  54 +import java.util.concurrent.ConcurrentHashMap;
  55 +import java.util.concurrent.ConcurrentMap;
  56 +import java.util.concurrent.Executors;
  57 +import java.util.concurrent.ScheduledExecutorService;
  58 +import java.util.concurrent.ScheduledFuture;
  59 +import java.util.concurrent.TimeUnit;
  60 +
  61 +@Service
  62 +@Slf4j
  63 +@ConditionalOnProperty(prefix = "edges", value = "enabled", havingValue = "true")
  64 +@TbCoreComponent
  65 +public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase implements EdgeRpcService {
  66 +
  67 + private final ConcurrentMap<EdgeId, EdgeGrpcSession> sessions = new ConcurrentHashMap<>();
  68 + private final ConcurrentMap<EdgeId, Boolean> sessionNewEvents = new ConcurrentHashMap<>();
  69 + private final ConcurrentMap<EdgeId, ScheduledFuture<?>> sessionEdgeEventChecks = new ConcurrentHashMap<>();
  70 + private static final ObjectMapper mapper = new ObjectMapper();
  71 +
  72 + @Value("${edges.rpc.port}")
  73 + private int rpcPort;
  74 + @Value("${edges.rpc.ssl.enabled}")
  75 + private boolean sslEnabled;
  76 + @Value("${edges.rpc.ssl.cert}")
  77 + private String certFileResource;
  78 + @Value("${edges.rpc.ssl.private_key}")
  79 + private String privateKeyResource;
  80 + @Value("${edges.state.persistToTelemetry:false}")
  81 + private boolean persistToTelemetry;
  82 + @Value("${edges.rpc.client_max_keep_alive_time_sec}")
  83 + private int clientMaxKeepAliveTimeSec;
  84 +
  85 + @Value("${edges.scheduler_pool_size}")
  86 + private int schedulerPoolSize;
  87 +
  88 + @Autowired
  89 + private EdgeContextComponent ctx;
  90 +
  91 + @Autowired
  92 + private TelemetrySubscriptionService tsSubService;
  93 +
  94 + private Server server;
  95 +
  96 + private ScheduledExecutorService scheduler;
  97 +
  98 + @PostConstruct
  99 + public void init() {
  100 + log.info("Initializing Edge RPC service!");
  101 + NettyServerBuilder builder = NettyServerBuilder.forPort(rpcPort)
  102 + .permitKeepAliveTime(clientMaxKeepAliveTimeSec, TimeUnit.SECONDS)
  103 + .addService(this);
  104 + if (sslEnabled) {
  105 + try {
  106 + File certFile = new File(Resources.getResource(certFileResource).toURI());
  107 + File privateKeyFile = new File(Resources.getResource(privateKeyResource).toURI());
  108 + builder.useTransportSecurity(certFile, privateKeyFile);
  109 + } catch (Exception e) {
  110 + log.error("Unable to set up SSL context. Reason: " + e.getMessage(), e);
  111 + throw new RuntimeException("Unable to set up SSL context!", e);
  112 + }
  113 + }
  114 + server = builder.build();
  115 + log.info("Going to start Edge RPC server using port: {}", rpcPort);
  116 + try {
  117 + server.start();
  118 + } catch (IOException e) {
  119 + log.error("Failed to start Edge RPC server!", e);
  120 + throw new RuntimeException("Failed to start Edge RPC server!");
  121 + }
  122 + this.scheduler = Executors.newScheduledThreadPool(schedulerPoolSize, ThingsBoardThreadFactory.forName("edge-scheduler"));
  123 + log.info("Edge RPC service initialized!");
  124 + }
  125 +
  126 + @PreDestroy
  127 + public void destroy() {
  128 + if (server != null) {
  129 + server.shutdownNow();
  130 + }
  131 + for (Map.Entry<EdgeId, ScheduledFuture<?>> entry : sessionEdgeEventChecks.entrySet()) {
  132 + EdgeId edgeId = entry.getKey();
  133 + ScheduledFuture<?> sessionEdgeEventCheck = entry.getValue();
  134 + if (sessionEdgeEventCheck != null && !sessionEdgeEventCheck.isCancelled() && !sessionEdgeEventCheck.isDone()) {
  135 + sessionEdgeEventCheck.cancel(true);
  136 + sessionEdgeEventChecks.remove(edgeId);
  137 + }
  138 + }
  139 + if (scheduler != null) {
  140 + scheduler.shutdownNow();
  141 + }
  142 + }
  143 +
  144 + @Override
  145 + public StreamObserver<RequestMsg> handleMsgs(StreamObserver<ResponseMsg> outputStream) {
  146 + return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper).getInputStream();
  147 + }
  148 +
  149 + @Override
  150 + public void updateEdge(Edge edge) {
  151 + EdgeGrpcSession session = sessions.get(edge.getId());
  152 + if (session != null && session.isConnected()) {
  153 + log.debug("[{}] Updating configuration for edge [{}] [{}]", edge.getTenantId(), edge.getName(), edge.getId());
  154 + session.onConfigurationUpdate(edge);
  155 + } else {
  156 + log.debug("[{}] Session doesn't exist for edge [{}] [{}]", edge.getTenantId(), edge.getName(), edge.getId());
  157 + }
  158 + }
  159 +
  160 + @Override
  161 + public void deleteEdge(EdgeId edgeId) {
  162 + EdgeGrpcSession session = sessions.get(edgeId);
  163 + if (session != null && session.isConnected()) {
  164 + log.info("Closing and removing session for edge [{}]", edgeId);
  165 + session.close();
  166 + sessions.remove(edgeId);
  167 + sessionNewEvents.remove(edgeId);
  168 + cancelScheduleEdgeEventsCheck(edgeId);
  169 + }
  170 + }
  171 +
  172 + @Override
  173 + public void onEdgeEvent(EdgeId edgeId) {
  174 + log.trace("[{}] onEdgeEvent", edgeId.getId());
  175 + if (!sessionNewEvents.get(edgeId)) {
  176 + log.trace("[{}] set session new events flag to true", edgeId.getId());
  177 + sessionNewEvents.put(edgeId, true);
  178 + }
  179 + }
  180 +
  181 + private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) {
  182 + log.info("[{}] edge [{}] connected successfully.", edgeGrpcSession.getSessionId(), edgeId);
  183 + sessions.put(edgeId, edgeGrpcSession);
  184 + sessionNewEvents.put(edgeId, false);
  185 + save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true);
  186 + save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, System.currentTimeMillis());
  187 + cancelScheduleEdgeEventsCheck(edgeId);
  188 + scheduleEdgeEventsCheck(edgeGrpcSession);
  189 + }
  190 +
  191 + public EdgeGrpcSession getEdgeGrpcSessionById(TenantId tenantId, EdgeId edgeId) {
  192 + EdgeGrpcSession session = sessions.get(edgeId);
  193 + if (session != null && session.isConnected()) {
  194 + return session;
  195 + } else {
  196 + log.error("[{}] Edge is not connected [{}]", tenantId, edgeId);
  197 + throw new RuntimeException("Edge is not connected");
  198 + }
  199 + }
  200 +
  201 + private void scheduleEdgeEventsCheck(EdgeGrpcSession session) {
  202 + EdgeId edgeId = session.getEdge().getId();
  203 + UUID tenantId = session.getEdge().getTenantId().getId();
  204 + if (sessions.containsKey(edgeId)) {
  205 + ScheduledFuture<?> schedule = scheduler.schedule(() -> {
  206 + try {
  207 + if (sessionNewEvents.get(edgeId)) {
  208 + log.trace("[{}] Set session new events flag to false", edgeId.getId());
  209 + sessionNewEvents.put(edgeId, false);
  210 + session.processEdgeEvents();
  211 + }
  212 + } catch (Exception e) {
  213 + log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), e);
  214 + }
  215 + scheduleEdgeEventsCheck(session);
  216 + }, ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval(), TimeUnit.MILLISECONDS);
  217 + sessionEdgeEventChecks.put(edgeId, schedule);
  218 + log.trace("[{}] Check edge event scheduled for edge [{}]", tenantId, edgeId.getId());
  219 + } else {
  220 + log.debug("[{}] Session was removed and edge event check schedule must not be started [{}]",
  221 + tenantId, edgeId.getId());
  222 + }
  223 + }
  224 +
  225 + private void cancelScheduleEdgeEventsCheck(EdgeId edgeId) {
  226 + log.trace("[{}] cancelling edge event check for edge", edgeId);
  227 + if (sessionEdgeEventChecks.containsKey(edgeId)) {
  228 + ScheduledFuture<?> sessionEdgeEventCheck = sessionEdgeEventChecks.get(edgeId);
  229 + if (sessionEdgeEventCheck != null && !sessionEdgeEventCheck.isCancelled() && !sessionEdgeEventCheck.isDone()) {
  230 + sessionEdgeEventCheck.cancel(true);
  231 + sessionEdgeEventChecks.remove(edgeId);
  232 + }
  233 + }
  234 + }
  235 +
  236 + private void onEdgeDisconnect(EdgeId edgeId) {
  237 + log.info("[{}] edge disconnected!", edgeId);
  238 + sessions.remove(edgeId);
  239 + sessionNewEvents.remove(edgeId);
  240 + save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false);
  241 + save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, System.currentTimeMillis());
  242 + cancelScheduleEdgeEventsCheck(edgeId);
  243 + }
  244 +
  245 + private void save(EdgeId edgeId, String key, long value) {
  246 + log.debug("[{}] Updating long edge telemetry [{}] [{}]", edgeId, key, value);
  247 + if (persistToTelemetry) {
  248 + tsSubService.saveAndNotify(
  249 + TenantId.SYS_TENANT_ID, edgeId,
  250 + Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))),
  251 + new AttributeSaveCallback(edgeId, key, value));
  252 + } else {
  253 + tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value));
  254 + }
  255 + }
  256 +
  257 + private void save(EdgeId edgeId, String key, boolean value) {
  258 + log.debug("[{}] Updating boolean edge telemetry [{}] [{}]", edgeId, key, value);
  259 + if (persistToTelemetry) {
  260 + tsSubService.saveAndNotify(
  261 + TenantId.SYS_TENANT_ID, edgeId,
  262 + Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))),
  263 + new AttributeSaveCallback(edgeId, key, value));
  264 + } else {
  265 + tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value));
  266 + }
  267 + }
  268 +
  269 + private static class AttributeSaveCallback implements FutureCallback<Void> {
  270 + private final EdgeId edgeId;
  271 + private final String key;
  272 + private final Object value;
  273 +
  274 + AttributeSaveCallback(EdgeId edgeId, String key, Object value) {
  275 + this.edgeId = edgeId;
  276 + this.key = key;
  277 + this.value = value;
  278 + }
  279 +
  280 + @Override
  281 + public void onSuccess(@Nullable Void result) {
  282 + log.trace("[{}] Successfully updated attribute [{}] with value [{}]", edgeId, key, value);
  283 + }
  284 +
  285 + @Override
  286 + public void onFailure(Throwable t) {
  287 + log.warn("[{}] Failed to update attribute [{}] with value [{}]", edgeId, key, value, t);
  288 + }
  289 + }
  290 +}