Commit b02fbe860c7cf4d5541f709aca68ca6ec9589c84

Authored by vzikratyi
2 parents 91f05282 fd602dec

Merge branch 'develop/3.2' into feature/dynamic-oauth2-new-table

# Conflicts:
#	application/src/main/java/org/thingsboard/server/controller/BaseController.java
#	application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
#	application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
#	application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java
#	common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
#	common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
#	dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
#	dao/src/test/resources/sql/hsql/drop-all-tables.sql
#	dao/src/test/resources/sql/psql/drop-all-tables.sql
#	dao/src/test/resources/sql/timescale/drop-all-tables.sql
#	ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts
Showing 74 changed files with 3142 additions and 190 deletions

Too many changes to show.

To preserve performance only 74 of 594 files are displayed.

@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>3.1.1-SNAPSHOT</version> 23 + <version>3.2.0-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <artifactId>application</artifactId> 26 <artifactId>application</artifactId>
@@ -47,10 +47,6 @@ @@ -47,10 +47,6 @@
47 47
48 <dependencies> 48 <dependencies>
49 <dependency> 49 <dependency>
50 - <groupId>de.ruedigermoeller</groupId>  
51 - <artifactId>fst</artifactId>  
52 - </dependency>  
53 - <dependency>  
54 <groupId>io.netty</groupId> 50 <groupId>io.netty</groupId>
55 <artifactId>netty-transport-native-epoll</artifactId> 51 <artifactId>netty-transport-native-epoll</artifactId>
56 <version>${netty.version}</version> 52 <version>${netty.version}</version>
@@ -43,7 +43,8 @@ @@ -43,7 +43,8 @@
43 "name": "Save Client Attributes", 43 "name": "Save Client Attributes",
44 "debugMode": false, 44 "debugMode": false,
45 "configuration": { 45 "configuration": {
46 - "scope": "CLIENT_SCOPE" 46 + "scope": "CLIENT_SCOPE",
  47 + "notifyDevice": "false"
47 } 48 }
48 }, 49 },
49 { 50 {
@@ -166,7 +166,7 @@ @@ -166,7 +166,7 @@
166 "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", 166 "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema('graph');\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true, 'graph');\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n",
167 "settingsSchema": "{}", 167 "settingsSchema": "{}",
168 "dataKeySettingsSchema": "{}", 168 "dataKeySettingsSchema": "{}",
169 - "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" 169 + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"direction\":\"column\",\",position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}"
170 } 170 }
171 } 171 }
172 ] 172 ]
  1 +{
  2 + "ruleChain": {
  3 + "additionalInfo": {
  4 + "description": ""
  5 + },
  6 + "name": "Device Profile Rule Chain Template",
  7 + "firstRuleNodeId": null,
  8 + "root": false,
  9 + "debugMode": false,
  10 + "configuration": null
  11 + },
  12 + "metadata": {
  13 + "firstNodeIndex": 6,
  14 + "nodes": [
  15 + {
  16 + "additionalInfo": {
  17 + "layoutX": 822,
  18 + "layoutY": 294
  19 + },
  20 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
  21 + "name": "Save Timeseries",
  22 + "debugMode": false,
  23 + "configuration": {
  24 + "defaultTTL": 0
  25 + }
  26 + },
  27 + {
  28 + "additionalInfo": {
  29 + "layoutX": 824,
  30 + "layoutY": 221
  31 + },
  32 + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
  33 + "name": "Save Client Attributes",
  34 + "debugMode": false,
  35 + "configuration": {
  36 + "scope": "CLIENT_SCOPE"
  37 + }
  38 + },
  39 + {
  40 + "additionalInfo": {
  41 + "layoutX": 494,
  42 + "layoutY": 309
  43 + },
  44 + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
  45 + "name": "Message Type Switch",
  46 + "debugMode": false,
  47 + "configuration": {
  48 + "version": 0
  49 + }
  50 + },
  51 + {
  52 + "additionalInfo": {
  53 + "layoutX": 824,
  54 + "layoutY": 383
  55 + },
  56 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  57 + "name": "Log RPC from Device",
  58 + "debugMode": false,
  59 + "configuration": {
  60 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  61 + }
  62 + },
  63 + {
  64 + "additionalInfo": {
  65 + "layoutX": 823,
  66 + "layoutY": 444
  67 + },
  68 + "type": "org.thingsboard.rule.engine.action.TbLogNode",
  69 + "name": "Log Other",
  70 + "debugMode": false,
  71 + "configuration": {
  72 + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
  73 + }
  74 + },
  75 + {
  76 + "additionalInfo": {
  77 + "layoutX": 822,
  78 + "layoutY": 507
  79 + },
  80 + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
  81 + "name": "RPC Call Request",
  82 + "debugMode": false,
  83 + "configuration": {
  84 + "timeoutInSeconds": 60
  85 + }
  86 + },
  87 + {
  88 + "additionalInfo": {
  89 + "description": "",
  90 + "layoutX": 209,
  91 + "layoutY": 307
  92 + },
  93 + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
  94 + "name": "Device Profile Node",
  95 + "debugMode": false,
  96 + "configuration": {
  97 + "persistAlarmRulesState": false
  98 + }
  99 + }
  100 + ],
  101 + "connections": [
  102 + {
  103 + "fromIndex": 2,
  104 + "toIndex": 4,
  105 + "type": "Other"
  106 + },
  107 + {
  108 + "fromIndex": 2,
  109 + "toIndex": 1,
  110 + "type": "Post attributes"
  111 + },
  112 + {
  113 + "fromIndex": 2,
  114 + "toIndex": 0,
  115 + "type": "Post telemetry"
  116 + },
  117 + {
  118 + "fromIndex": 2,
  119 + "toIndex": 3,
  120 + "type": "RPC Request from Device"
  121 + },
  122 + {
  123 + "fromIndex": 2,
  124 + "toIndex": 5,
  125 + "type": "RPC Request to Device"
  126 + },
  127 + {
  128 + "fromIndex": 6,
  129 + "toIndex": 2,
  130 + "type": "Success"
  131 + }
  132 + ],
  133 + "ruleChainConnections": null
  134 + }
  135 +}
@@ -31,7 +31,8 @@ @@ -31,7 +31,8 @@
31 "name": "Save Client Attributes", 31 "name": "Save Client Attributes",
32 "debugMode": false, 32 "debugMode": false,
33 "configuration": { 33 "configuration": {
34 - "scope": "CLIENT_SCOPE" 34 + "scope": "CLIENT_SCOPE",
  35 + "notifyDevice": "false"
35 } 36 }
36 }, 37 },
37 { 38 {
@@ -64,6 +64,7 @@ BEGIN @@ -64,6 +64,7 @@ BEGIN
64 AND tablename like 'ts_kv_' || '%' 64 AND tablename like 'ts_kv_' || '%'
65 AND tablename != 'ts_kv_latest' 65 AND tablename != 'ts_kv_latest'
66 AND tablename != 'ts_kv_dictionary' 66 AND tablename != 'ts_kv_dictionary'
  67 + AND tablename != 'ts_kv_indefinite'
67 LOOP 68 LOOP
68 IF partition != partition_by_max_ttl_date THEN 69 IF partition != partition_by_max_ttl_date THEN
69 IF partition_year IS NOT NULL THEN 70 IF partition_year IS NOT NULL THEN
@@ -59,8 +59,8 @@ $$ @@ -59,8 +59,8 @@ $$
59 DECLARE 59 DECLARE
60 tenant_cursor CURSOR FOR select tenant.id as tenant_id 60 tenant_cursor CURSOR FOR select tenant.id as tenant_id
61 from tenant; 61 from tenant;
62 - tenant_id_record varchar;  
63 - customer_id_record varchar; 62 + tenant_id_record uuid;
  63 + customer_id_record uuid;
64 tenant_ttl bigint; 64 tenant_ttl bigint;
65 customer_ttl bigint; 65 customer_ttl bigint;
66 deleted_for_entities bigint; 66 deleted_for_entities bigint;
  1 +--
  2 +-- Copyright © 2016-2020 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 +DROP PROCEDURE IF EXISTS update_tenant_profiles;
  18 +DROP PROCEDURE IF EXISTS update_device_profiles;
  19 +
  20 +ALTER TABLE tenant ALTER COLUMN tenant_profile_id SET NOT NULL;
  21 +ALTER TABLE tenant DROP CONSTRAINT IF EXISTS fk_tenant_profile;
  22 +ALTER TABLE tenant ADD CONSTRAINT fk_tenant_profile FOREIGN KEY (tenant_profile_id) REFERENCES tenant_profile(id);
  23 +ALTER TABLE tenant DROP COLUMN IF EXISTS isolated_tb_core;
  24 +ALTER TABLE tenant DROP COLUMN IF EXISTS isolated_tb_rule_engine;
  25 +
  26 +ALTER TABLE device ALTER COLUMN device_profile_id SET NOT NULL;
  27 +ALTER TABLE device DROP CONSTRAINT IF EXISTS fk_device_profile;
  28 +ALTER TABLE device ADD CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id);
  1 +--
  2 +-- Copyright © 2016-2020 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 device_profile (
  18 + id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY,
  19 + created_time bigint NOT NULL,
  20 + name varchar(255),
  21 + type varchar(255),
  22 + transport_type varchar(255),
  23 + profile_data jsonb,
  24 + description varchar,
  25 + search_text varchar(255),
  26 + is_default boolean,
  27 + tenant_id uuid,
  28 + default_rule_chain_id uuid,
  29 + CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name),
  30 + CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id)
  31 +);
  32 +
  33 +CREATE TABLE IF NOT EXISTS tenant_profile (
  34 + id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY,
  35 + created_time bigint NOT NULL,
  36 + name varchar(255),
  37 + profile_data jsonb,
  38 + description varchar,
  39 + search_text varchar(255),
  40 + is_default boolean,
  41 + isolated_tb_core boolean,
  42 + isolated_tb_rule_engine boolean,
  43 + CONSTRAINT tenant_profile_name_unq_key UNIQUE (name)
  44 +);
  45 +
  46 +CREATE OR REPLACE PROCEDURE update_tenant_profiles()
  47 + LANGUAGE plpgsql AS
  48 +$$
  49 +BEGIN
  50 + UPDATE tenant as t SET tenant_profile_id = p.id
  51 + FROM
  52 + (SELECT id from tenant_profile WHERE isolated_tb_core = false AND isolated_tb_rule_engine = false) as p
  53 + WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = false AND t.isolated_tb_rule_engine = false;
  54 +
  55 + UPDATE tenant as t SET tenant_profile_id = p.id
  56 + FROM
  57 + (SELECT id from tenant_profile WHERE isolated_tb_core = true AND isolated_tb_rule_engine = false) as p
  58 + WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = true AND t.isolated_tb_rule_engine = false;
  59 +
  60 + UPDATE tenant as t SET tenant_profile_id = p.id
  61 + FROM
  62 + (SELECT id from tenant_profile WHERE isolated_tb_core = false AND isolated_tb_rule_engine = true) as p
  63 + WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = false AND t.isolated_tb_rule_engine = true;
  64 +
  65 + UPDATE tenant as t SET tenant_profile_id = p.id
  66 + FROM
  67 + (SELECT id from tenant_profile WHERE isolated_tb_core = true AND isolated_tb_rule_engine = true) as p
  68 + WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = true AND t.isolated_tb_rule_engine = true;
  69 +END;
  70 +$$;
  71 +
  72 +CREATE OR REPLACE PROCEDURE update_device_profiles()
  73 + LANGUAGE plpgsql AS
  74 +$$
  75 +BEGIN
  76 + UPDATE device as d SET device_profile_id = p.id, device_data = '{"configuration":{"type":"DEFAULT"}, "transportConfiguration":{"type":"DEFAULT"}}'
  77 + FROM
  78 + (SELECT id, tenant_id, name from device_profile) as p
  79 + WHERE d.device_profile_id IS NULL AND p.tenant_id = d.tenant_id AND d.type = p.name;
  80 +END;
  81 +$$;
@@ -32,6 +32,7 @@ import org.springframework.data.redis.core.RedisTemplate; @@ -32,6 +32,7 @@ import org.springframework.data.redis.core.RedisTemplate;
32 import org.springframework.scheduling.annotation.Scheduled; 32 import org.springframework.scheduling.annotation.Scheduled;
33 import org.springframework.stereotype.Component; 33 import org.springframework.stereotype.Component;
34 import org.thingsboard.rule.engine.api.MailService; 34 import org.thingsboard.rule.engine.api.MailService;
  35 +import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache;
35 import org.thingsboard.server.actors.service.ActorService; 36 import org.thingsboard.server.actors.service.ActorService;
36 import org.thingsboard.server.actors.tenant.DebugTbRateLimits; 37 import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
37 import org.thingsboard.server.common.data.DataConstants; 38 import org.thingsboard.server.common.data.DataConstants;
@@ -44,7 +45,6 @@ import org.thingsboard.server.common.msg.TbMsg; @@ -44,7 +45,6 @@ import org.thingsboard.server.common.msg.TbMsg;
44 import org.thingsboard.server.common.msg.queue.ServiceType; 45 import org.thingsboard.server.common.msg.queue.ServiceType;
45 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 46 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
46 import org.thingsboard.server.common.msg.tools.TbRateLimits; 47 import org.thingsboard.server.common.msg.tools.TbRateLimits;
47 -import org.thingsboard.server.dao.alarm.AlarmService;  
48 import org.thingsboard.server.dao.asset.AssetService; 48 import org.thingsboard.server.dao.asset.AssetService;
49 import org.thingsboard.server.dao.attributes.AttributesService; 49 import org.thingsboard.server.dao.attributes.AttributesService;
50 import org.thingsboard.server.dao.audit.AuditLogService; 50 import org.thingsboard.server.dao.audit.AuditLogService;
@@ -58,17 +58,20 @@ import org.thingsboard.server.dao.event.EventService; @@ -58,17 +58,20 @@ import org.thingsboard.server.dao.event.EventService;
58 import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; 58 import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor;
59 import org.thingsboard.server.dao.relation.RelationService; 59 import org.thingsboard.server.dao.relation.RelationService;
60 import org.thingsboard.server.dao.rule.RuleChainService; 60 import org.thingsboard.server.dao.rule.RuleChainService;
  61 +import org.thingsboard.server.dao.rule.RuleNodeStateService;
  62 +import org.thingsboard.server.dao.tenant.TenantProfileService;
61 import org.thingsboard.server.dao.tenant.TenantService; 63 import org.thingsboard.server.dao.tenant.TenantService;
62 import org.thingsboard.server.dao.timeseries.TimeseriesService; 64 import org.thingsboard.server.dao.timeseries.TimeseriesService;
63 import org.thingsboard.server.dao.user.UserService; 65 import org.thingsboard.server.dao.user.UserService;
64 import org.thingsboard.server.queue.discovery.PartitionService; 66 import org.thingsboard.server.queue.discovery.PartitionService;
65 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 67 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
66 import org.thingsboard.server.service.component.ComponentDiscoveryService; 68 import org.thingsboard.server.service.component.ComponentDiscoveryService;
67 -import org.thingsboard.server.service.encoding.DataDecodingEncodingService; 69 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
68 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 70 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
69 import org.thingsboard.server.service.executors.ExternalCallExecutorService; 71 import org.thingsboard.server.service.executors.ExternalCallExecutorService;
70 import org.thingsboard.server.service.executors.SharedEventLoopGroupService; 72 import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
71 import org.thingsboard.server.service.mail.MailExecutorService; 73 import org.thingsboard.server.service.mail.MailExecutorService;
  74 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
72 import org.thingsboard.server.service.queue.TbClusterService; 75 import org.thingsboard.server.service.queue.TbClusterService;
73 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; 76 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
74 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; 77 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
@@ -89,7 +92,6 @@ import java.util.concurrent.ConcurrentHashMap; @@ -89,7 +92,6 @@ import java.util.concurrent.ConcurrentHashMap;
89 import java.util.concurrent.ConcurrentMap; 92 import java.util.concurrent.ConcurrentMap;
90 import java.util.concurrent.ScheduledExecutorService; 93 import java.util.concurrent.ScheduledExecutorService;
91 import java.util.concurrent.TimeUnit; 94 import java.util.concurrent.TimeUnit;
92 -import java.util.concurrent.atomic.AtomicInteger;  
93 95
94 @Slf4j 96 @Slf4j
95 @Component 97 @Component
@@ -127,6 +129,10 @@ public class ActorSystemContext { @@ -127,6 +129,10 @@ public class ActorSystemContext {
127 129
128 @Autowired 130 @Autowired
129 @Getter 131 @Getter
  132 + private TbDeviceProfileCache deviceProfileCache;
  133 +
  134 + @Autowired
  135 + @Getter
130 private AssetService assetService; 136 private AssetService assetService;
131 137
132 @Autowired 138 @Autowired
@@ -139,6 +145,10 @@ public class ActorSystemContext { @@ -139,6 +145,10 @@ public class ActorSystemContext {
139 145
140 @Autowired 146 @Autowired
141 @Getter 147 @Getter
  148 + private TenantProfileService tenantProfileService;
  149 +
  150 + @Autowired
  151 + @Getter
142 private CustomerService customerService; 152 private CustomerService customerService;
143 153
144 @Autowired 154 @Autowired
@@ -150,6 +160,10 @@ public class ActorSystemContext { @@ -150,6 +160,10 @@ public class ActorSystemContext {
150 private RuleChainService ruleChainService; 160 private RuleChainService ruleChainService;
151 161
152 @Autowired 162 @Autowired
  163 + @Getter
  164 + private RuleNodeStateService ruleNodeStateService;
  165 +
  166 + @Autowired
153 private PartitionService partitionService; 167 private PartitionService partitionService;
154 168
155 @Autowired 169 @Autowired
@@ -527,4 +541,5 @@ public class ActorSystemContext { @@ -527,4 +541,5 @@ public class ActorSystemContext {
527 log.debug("Scheduling msg {} with delay {} ms", msg, delayInMs); 541 log.debug("Scheduling msg {} with delay {} ms", msg, delayInMs);
528 getScheduler().schedule(() -> ctx.tell(msg), delayInMs, TimeUnit.MILLISECONDS); 542 getScheduler().schedule(() -> ctx.tell(msg), delayInMs, TimeUnit.MILLISECONDS);
529 } 543 }
  544 +
530 } 545 }
@@ -27,6 +27,7 @@ import org.thingsboard.server.actors.service.DefaultActorService; @@ -27,6 +27,7 @@ import org.thingsboard.server.actors.service.DefaultActorService;
27 import org.thingsboard.server.actors.tenant.TenantActor; 27 import org.thingsboard.server.actors.tenant.TenantActor;
28 import org.thingsboard.server.common.data.EntityType; 28 import org.thingsboard.server.common.data.EntityType;
29 import org.thingsboard.server.common.data.Tenant; 29 import org.thingsboard.server.common.data.Tenant;
  30 +import org.thingsboard.server.common.data.TenantProfile;
30 import org.thingsboard.server.common.data.id.EntityId; 31 import org.thingsboard.server.common.data.id.EntityId;
31 import org.thingsboard.server.common.data.id.TenantId; 32 import org.thingsboard.server.common.data.id.TenantId;
32 import org.thingsboard.server.common.data.page.PageDataIterable; 33 import org.thingsboard.server.common.data.page.PageDataIterable;
@@ -116,7 +117,9 @@ public class AppActor extends ContextAwareActor { @@ -116,7 +117,9 @@ public class AppActor extends ContextAwareActor {
116 boolean isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE); 117 boolean isRuleEngine = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
117 boolean isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE); 118 boolean isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
118 for (Tenant tenant : tenantIterator) { 119 for (Tenant tenant : tenantIterator) {
119 - if (isCore || (isRuleEngine && !tenant.isIsolatedTbRuleEngine())) { 120 + // TODO: Tenant Profile from cache
  121 + TenantProfile tenantProfile = systemContext.getTenantProfileService().findTenantProfileById(TenantId.SYS_TENANT_ID, tenant.getTenantProfileId());
  122 + if (isCore || (isRuleEngine && !tenantProfile.isIsolatedTbRuleEngine())) {
120 log.debug("[{}] Creating tenant actor", tenant.getId()); 123 log.debug("[{}] Creating tenant actor", tenant.getId());
121 getOrCreateTenantActor(tenant.getId()); 124 getOrCreateTenantActor(tenant.getId());
122 log.debug("[{}] Tenant actor created.", tenant.getId()); 125 log.debug("[{}] Tenant actor created.", tenant.getId());
@@ -28,6 +28,7 @@ import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg; @@ -28,6 +28,7 @@ import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
28 import org.thingsboard.server.actors.ActorSystemContext; 28 import org.thingsboard.server.actors.ActorSystemContext;
29 import org.thingsboard.server.actors.TbActorCtx; 29 import org.thingsboard.server.actors.TbActorCtx;
30 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor; 30 import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
  31 +import org.thingsboard.server.common.data.DataConstants;
31 import org.thingsboard.server.common.data.Device; 32 import org.thingsboard.server.common.data.Device;
32 import org.thingsboard.server.common.data.id.DeviceId; 33 import org.thingsboard.server.common.data.id.DeviceId;
33 import org.thingsboard.server.common.data.id.TenantId; 34 import org.thingsboard.server.common.data.id.TenantId;
@@ -79,8 +80,6 @@ import java.util.UUID; @@ -79,8 +80,6 @@ import java.util.UUID;
79 import java.util.function.Consumer; 80 import java.util.function.Consumer;
80 import java.util.stream.Collectors; 81 import java.util.stream.Collectors;
81 82
82 -import static org.thingsboard.server.common.data.DataConstants.CLIENT_SCOPE;  
83 -import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE;  
84 83
85 /** 84 /**
86 * @author Andrew Shvayka 85 * @author Andrew Shvayka
@@ -279,17 +278,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -279,17 +278,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
279 ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture; 278 ListenableFuture<List<AttributeKvEntry>> clientAttributesFuture;
280 ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture; 279 ListenableFuture<List<AttributeKvEntry>> sharedAttributesFuture;
281 if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) { 280 if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
282 - clientAttributesFuture = findAllAttributesByScope(CLIENT_SCOPE);  
283 - sharedAttributesFuture = findAllAttributesByScope(SHARED_SCOPE); 281 + clientAttributesFuture = findAllAttributesByScope(DataConstants.CLIENT_SCOPE);
  282 + sharedAttributesFuture = findAllAttributesByScope(DataConstants.SHARED_SCOPE);
284 } else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) { 283 } else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
285 - clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), CLIENT_SCOPE);  
286 - sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), SHARED_SCOPE); 284 + clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);
  285 + sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);
287 } else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) { 286 } else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
288 clientAttributesFuture = Futures.immediateFuture(Collections.emptyList()); 287 clientAttributesFuture = Futures.immediateFuture(Collections.emptyList());
289 - sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), SHARED_SCOPE); 288 + sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);
290 } else { 289 } else {
291 sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList()); 290 sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList());
292 - clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), CLIENT_SCOPE); 291 + clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);
293 } 292 }
294 return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture)); 293 return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture));
295 } 294 }
@@ -316,7 +315,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -316,7 +315,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
316 AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder(); 315 AttributeUpdateNotificationMsg.Builder notification = AttributeUpdateNotificationMsg.newBuilder();
317 if (msg.isDeleted()) { 316 if (msg.isDeleted()) {
318 List<String> sharedKeys = msg.getDeletedKeys().stream() 317 List<String> sharedKeys = msg.getDeletedKeys().stream()
319 - .filter(key -> SHARED_SCOPE.equals(key.getScope())) 318 + .filter(key -> DataConstants.SHARED_SCOPE.equals(key.getScope()))
320 .map(AttributeKey::getAttributeKey) 319 .map(AttributeKey::getAttributeKey)
321 .collect(Collectors.toList()); 320 .collect(Collectors.toList());
322 if (!sharedKeys.isEmpty()) { 321 if (!sharedKeys.isEmpty()) {
@@ -324,7 +323,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -324,7 +323,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
324 hasNotificationData = true; 323 hasNotificationData = true;
325 } 324 }
326 } else { 325 } else {
327 - if (SHARED_SCOPE.equals(msg.getScope())) { 326 + if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {
328 List<AttributeKvEntry> attributes = new ArrayList<>(msg.getValues()); 327 List<AttributeKvEntry> attributes = new ArrayList<>(msg.getValues());
329 if (attributes.size() > 0) { 328 if (attributes.size() > 0) {
330 List<TsKvProto> sharedUpdated = msg.getValues().stream().map(this::toTsKvProto) 329 List<TsKvProto> sharedUpdated = msg.getValues().stream().map(this::toTsKvProto)
@@ -334,7 +333,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { @@ -334,7 +333,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
334 hasNotificationData = true; 333 hasNotificationData = true;
335 } 334 }
336 } else { 335 } else {
337 - log.debug("[{}] No public server side attributes changed!", deviceId); 336 + log.debug("[{}] No public shared side attributes changed!", deviceId);
338 } 337 }
339 } 338 }
340 } 339 }
@@ -23,6 +23,7 @@ import org.springframework.data.redis.core.RedisTemplate; @@ -23,6 +23,7 @@ import org.springframework.data.redis.core.RedisTemplate;
23 import org.thingsboard.common.util.ListeningExecutor; 23 import org.thingsboard.common.util.ListeningExecutor;
24 import org.thingsboard.rule.engine.api.MailService; 24 import org.thingsboard.rule.engine.api.MailService;
25 import org.thingsboard.rule.engine.api.RuleEngineAlarmService; 25 import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
  26 +import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache;
26 import org.thingsboard.rule.engine.api.RuleEngineRpcService; 27 import org.thingsboard.rule.engine.api.RuleEngineRpcService;
27 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; 28 import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
28 import org.thingsboard.rule.engine.api.ScriptEngine; 29 import org.thingsboard.rule.engine.api.ScriptEngine;
@@ -39,13 +40,15 @@ import org.thingsboard.server.common.data.id.EntityId; @@ -39,13 +40,15 @@ import org.thingsboard.server.common.data.id.EntityId;
39 import org.thingsboard.server.common.data.id.RuleChainId; 40 import org.thingsboard.server.common.data.id.RuleChainId;
40 import org.thingsboard.server.common.data.id.RuleNodeId; 41 import org.thingsboard.server.common.data.id.RuleNodeId;
41 import org.thingsboard.server.common.data.id.TenantId; 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;
42 import org.thingsboard.server.common.data.rule.RuleNode; 45 import org.thingsboard.server.common.data.rule.RuleNode;
  46 +import org.thingsboard.server.common.data.rule.RuleNodeState;
43 import org.thingsboard.server.common.msg.TbActorMsg; 47 import org.thingsboard.server.common.msg.TbActorMsg;
44 import org.thingsboard.server.common.msg.TbMsg; 48 import org.thingsboard.server.common.msg.TbMsg;
45 import org.thingsboard.server.common.msg.TbMsgMetaData; 49 import org.thingsboard.server.common.msg.TbMsgMetaData;
46 import org.thingsboard.server.common.msg.queue.ServiceType; 50 import org.thingsboard.server.common.msg.queue.ServiceType;
47 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 51 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
48 -import org.thingsboard.server.dao.alarm.AlarmService;  
49 import org.thingsboard.server.dao.asset.AssetService; 52 import org.thingsboard.server.dao.asset.AssetService;
50 import org.thingsboard.server.dao.attributes.AttributesService; 53 import org.thingsboard.server.dao.attributes.AttributesService;
51 import org.thingsboard.server.dao.cassandra.CassandraCluster; 54 import org.thingsboard.server.dao.cassandra.CassandraCluster;
@@ -67,7 +70,6 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; @@ -67,7 +70,6 @@ import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
67 70
68 import java.util.Collections; 71 import java.util.Collections;
69 import java.util.Set; 72 import java.util.Set;
70 -import java.util.concurrent.TimeUnit;  
71 import java.util.function.Consumer; 73 import java.util.function.Consumer;
72 74
73 /** 75 /**
@@ -105,6 +107,7 @@ class DefaultTbContext implements TbContext { @@ -105,6 +107,7 @@ class DefaultTbContext implements TbContext {
105 if (nodeCtx.getSelf().isDebugMode()) { 107 if (nodeCtx.getSelf().isDebugMode()) {
106 relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th)); 108 relationTypes.forEach(relationType -> mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), msg, relationType, th));
107 } 109 }
  110 + msg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId());
108 nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null)); 111 nodeCtx.getChainActor().tell(new RuleNodeToRuleChainTellNextMsg(nodeCtx.getSelf().getId(), relationTypes, msg, th != null ? th.getMessage() : null));
109 } 112 }
110 113
@@ -203,6 +206,7 @@ class DefaultTbContext implements TbContext { @@ -203,6 +206,7 @@ class DefaultTbContext implements TbContext {
203 if (nodeCtx.getSelf().isDebugMode()) { 206 if (nodeCtx.getSelf().isDebugMode()) {
204 mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), tbMsg, "ACK", null); 207 mainCtx.persistDebugOutput(nodeCtx.getTenantId(), nodeCtx.getSelf().getId(), tbMsg, "ACK", null);
205 } 208 }
  209 + tbMsg.getCallback().onProcessingEnd(nodeCtx.getSelf().getId());
206 tbMsg.getCallback().onSuccess(); 210 tbMsg.getCallback().onSuccess();
207 } 211 }
208 212
@@ -389,6 +393,11 @@ class DefaultTbContext implements TbContext { @@ -389,6 +393,11 @@ class DefaultTbContext implements TbContext {
389 } 393 }
390 394
391 @Override 395 @Override
  396 + public RuleEngineDeviceProfileCache getDeviceProfileCache() {
  397 + return mainCtx.getDeviceProfileCache();
  398 + }
  399 +
  400 + @Override
392 public EventLoopGroup getSharedEventLoop() { 401 public EventLoopGroup getSharedEventLoop() {
393 return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup(); 402 return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();
394 } 403 }
@@ -422,6 +431,30 @@ class DefaultTbContext implements TbContext { @@ -422,6 +431,30 @@ class DefaultTbContext implements TbContext {
422 return mainCtx.getRedisTemplate(); 431 return mainCtx.getRedisTemplate();
423 } 432 }
424 433
  434 + @Override
  435 + public PageData<RuleNodeState> findRuleNodeStates(PageLink pageLink) {
  436 + if (log.isDebugEnabled()) {
  437 + log.debug("[{}][{}] Fetch Rule Node States.", getTenantId(), getSelfId());
  438 + }
  439 + return mainCtx.getRuleNodeStateService().findByRuleNodeId(getTenantId(), getSelfId(), pageLink);
  440 + }
  441 +
  442 + @Override
  443 + public RuleNodeState findRuleNodeStateForEntity(EntityId entityId) {
  444 + if (log.isDebugEnabled()) {
  445 + log.debug("[{}][{}][{}] Fetch Rule Node State for entity.", getTenantId(), getSelfId(), entityId);
  446 + }
  447 + return mainCtx.getRuleNodeStateService().findByRuleNodeIdAndEntityId(getTenantId(), getSelfId(), entityId);
  448 + }
  449 +
  450 + @Override
  451 + public RuleNodeState saveRuleNodeState(RuleNodeState state) {
  452 + if (log.isDebugEnabled()) {
  453 + log.debug("[{}][{}][{}] Persist Rule Node State for entity: {}", getTenantId(), getSelfId(), state.getEntityId(), state.getStateData());
  454 + }
  455 + state.setRuleNodeId(getSelfId());
  456 + return mainCtx.getRuleNodeStateService().save(getTenantId(), state);
  457 + }
425 458
426 private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) { 459 private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) {
427 TbMsgMetaData metaData = new TbMsgMetaData(); 460 TbMsgMetaData metaData = new TbMsgMetaData();
@@ -103,7 +103,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod @@ -103,7 +103,7 @@ public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNod
103 } 103 }
104 104
105 void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) throws Exception { 105 void onRuleChainToRuleNodeMsg(RuleChainToRuleNodeMsg msg) throws Exception {
106 - msg.getMsg().getCallback().visit(info); 106 + msg.getMsg().getCallback().onProcessingStart(info);
107 checkActive(msg.getMsg()); 107 checkActive(msg.getMsg());
108 if (ruleNode.isDebugMode()) { 108 if (ruleNode.isDebugMode()) {
109 systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType()); 109 systemContext.persistDebugInput(tenantId, entityId, msg.getMsg(), msg.getFromRelationType());
@@ -31,6 +31,7 @@ import org.thingsboard.server.actors.service.ContextBasedCreator; @@ -31,6 +31,7 @@ import org.thingsboard.server.actors.service.ContextBasedCreator;
31 import org.thingsboard.server.actors.service.DefaultActorService; 31 import org.thingsboard.server.actors.service.DefaultActorService;
32 import org.thingsboard.server.common.data.EntityType; 32 import org.thingsboard.server.common.data.EntityType;
33 import org.thingsboard.server.common.data.Tenant; 33 import org.thingsboard.server.common.data.Tenant;
  34 +import org.thingsboard.server.common.data.TenantProfile;
34 import org.thingsboard.server.common.data.id.DeviceId; 35 import org.thingsboard.server.common.data.id.DeviceId;
35 import org.thingsboard.server.common.data.id.EntityId; 36 import org.thingsboard.server.common.data.id.EntityId;
36 import org.thingsboard.server.common.data.id.RuleChainId; 37 import org.thingsboard.server.common.data.id.RuleChainId;
@@ -75,12 +76,16 @@ public class TenantActor extends RuleChainManagerActor { @@ -75,12 +76,16 @@ public class TenantActor extends RuleChainManagerActor {
75 // This Service may be started for specific tenant only. 76 // This Service may be started for specific tenant only.
76 Optional<TenantId> isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant(); 77 Optional<TenantId> isolatedTenantId = systemContext.getServiceInfoProvider().getIsolatedTenant();
77 78
  79 + // TODO: Tenant Profile from cache
  80 +
  81 + TenantProfile tenantProfile = systemContext.getTenantProfileService().findTenantProfileById(tenantId, tenant.getTenantProfileId());
  82 +
78 isRuleEngineForCurrentTenant = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE); 83 isRuleEngineForCurrentTenant = systemContext.getServiceInfoProvider().isService(ServiceType.TB_RULE_ENGINE);
79 isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE); 84 isCore = systemContext.getServiceInfoProvider().isService(ServiceType.TB_CORE);
80 85
81 if (isRuleEngineForCurrentTenant) { 86 if (isRuleEngineForCurrentTenant) {
82 try { 87 try {
83 - if (isolatedTenantId.map(id -> id.equals(tenantId)).orElseGet(() -> !tenant.isIsolatedTbRuleEngine())) { 88 + if (isolatedTenantId.map(id -> id.equals(tenantId)).orElseGet(() -> !tenantProfile.isIsolatedTbRuleEngine())) {
84 log.info("[{}] Going to init rule chains", tenantId); 89 log.info("[{}] Going to init rule chains", tenantId);
85 initRuleChains(); 90 initRuleChains();
86 } else { 91 } else {
@@ -90,7 +90,7 @@ public class AlarmController extends BaseController { @@ -90,7 +90,7 @@ public class AlarmController extends BaseController {
90 checkEntity(alarm.getId(), alarm, Resource.ALARM); 90 checkEntity(alarm.getId(), alarm, Resource.ALARM);
91 91
92 Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm)); 92 Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm));
93 - logEntityAction(savedAlarm.getId(), savedAlarm, 93 + logEntityAction(savedAlarm.getOriginator(), savedAlarm,
94 getCurrentUser().getCustomerId(), 94 getCurrentUser().getCustomerId(),
95 alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); 95 alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
96 return savedAlarm; 96 return savedAlarm;
@@ -126,7 +126,7 @@ public class AlarmController extends BaseController { @@ -126,7 +126,7 @@ public class AlarmController extends BaseController {
126 long ackTs = System.currentTimeMillis(); 126 long ackTs = System.currentTimeMillis();
127 alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, ackTs).get(); 127 alarmService.ackAlarm(getCurrentUser().getTenantId(), alarmId, ackTs).get();
128 alarm.setAckTs(ackTs); 128 alarm.setAckTs(ackTs);
129 - logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null); 129 + logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null);
130 } catch (Exception e) { 130 } catch (Exception e) {
131 throw handleException(e); 131 throw handleException(e);
132 } 132 }
@@ -143,7 +143,7 @@ public class AlarmController extends BaseController { @@ -143,7 +143,7 @@ public class AlarmController extends BaseController {
143 long clearTs = System.currentTimeMillis(); 143 long clearTs = System.currentTimeMillis();
144 alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, clearTs).get(); 144 alarmService.clearAlarm(getCurrentUser().getTenantId(), alarmId, null, clearTs).get();
145 alarm.setClearTs(clearTs); 145 alarm.setClearTs(clearTs);
146 - logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null); 146 + logEntityAction(alarm.getOriginator(), alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null);
147 } catch (Exception e) { 147 } catch (Exception e) {
148 throw handleException(e); 148 throw handleException(e);
149 } 149 }
@@ -36,6 +36,22 @@ import org.thingsboard.server.common.data.audit.ActionType; @@ -36,6 +36,22 @@ import org.thingsboard.server.common.data.audit.ActionType;
36 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 36 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
37 import org.thingsboard.server.common.data.exception.ThingsboardException; 37 import org.thingsboard.server.common.data.exception.ThingsboardException;
38 import org.thingsboard.server.common.data.id.*; 38 import org.thingsboard.server.common.data.id.*;
  39 +import org.thingsboard.server.common.data.id.AlarmId;
  40 +import org.thingsboard.server.common.data.id.AssetId;
  41 +import org.thingsboard.server.common.data.id.CustomerId;
  42 +import org.thingsboard.server.common.data.id.DashboardId;
  43 +import org.thingsboard.server.common.data.id.DeviceId;
  44 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  45 +import org.thingsboard.server.common.data.id.EntityId;
  46 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  47 +import org.thingsboard.server.common.data.id.EntityViewId;
  48 +import org.thingsboard.server.common.data.id.RuleChainId;
  49 +import org.thingsboard.server.common.data.id.RuleNodeId;
  50 +import org.thingsboard.server.common.data.id.TenantId;
  51 +import org.thingsboard.server.common.data.id.TenantProfileId;
  52 +import org.thingsboard.server.common.data.id.UserId;
  53 +import org.thingsboard.server.common.data.id.WidgetTypeId;
  54 +import org.thingsboard.server.common.data.id.WidgetsBundleId;
39 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 55 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
40 import org.thingsboard.server.common.data.kv.DataType; 56 import org.thingsboard.server.common.data.kv.DataType;
41 import org.thingsboard.server.common.data.page.PageLink; 57 import org.thingsboard.server.common.data.page.PageLink;
@@ -57,6 +73,7 @@ import org.thingsboard.server.dao.customer.CustomerService; @@ -57,6 +73,7 @@ import org.thingsboard.server.dao.customer.CustomerService;
57 import org.thingsboard.server.dao.dashboard.DashboardService; 73 import org.thingsboard.server.dao.dashboard.DashboardService;
58 import org.thingsboard.server.dao.device.ClaimDevicesService; 74 import org.thingsboard.server.dao.device.ClaimDevicesService;
59 import org.thingsboard.server.dao.device.DeviceCredentialsService; 75 import org.thingsboard.server.dao.device.DeviceCredentialsService;
  76 +import org.thingsboard.server.dao.device.DeviceProfileService;
60 import org.thingsboard.server.dao.device.DeviceService; 77 import org.thingsboard.server.dao.device.DeviceService;
61 import org.thingsboard.server.dao.entityview.EntityViewService; 78 import org.thingsboard.server.dao.entityview.EntityViewService;
62 import org.thingsboard.server.dao.exception.DataValidationException; 79 import org.thingsboard.server.dao.exception.DataValidationException;
@@ -66,6 +83,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; @@ -66,6 +83,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
66 import org.thingsboard.server.dao.oauth2.OAuth2Service; 83 import org.thingsboard.server.dao.oauth2.OAuth2Service;
67 import org.thingsboard.server.dao.relation.RelationService; 84 import org.thingsboard.server.dao.relation.RelationService;
68 import org.thingsboard.server.dao.rule.RuleChainService; 85 import org.thingsboard.server.dao.rule.RuleChainService;
  86 +import org.thingsboard.server.dao.tenant.TenantProfileService;
69 import org.thingsboard.server.dao.tenant.TenantService; 87 import org.thingsboard.server.dao.tenant.TenantService;
70 import org.thingsboard.server.dao.user.UserService; 88 import org.thingsboard.server.dao.user.UserService;
71 import org.thingsboard.server.dao.widget.WidgetTypeService; 89 import org.thingsboard.server.dao.widget.WidgetTypeService;
@@ -75,6 +93,7 @@ import org.thingsboard.server.queue.discovery.PartitionService; @@ -75,6 +93,7 @@ import org.thingsboard.server.queue.discovery.PartitionService;
75 import org.thingsboard.server.queue.provider.TbQueueProducerProvider; 93 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
76 import org.thingsboard.server.queue.util.TbCoreComponent; 94 import org.thingsboard.server.queue.util.TbCoreComponent;
77 import org.thingsboard.server.service.component.ComponentDiscoveryService; 95 import org.thingsboard.server.service.component.ComponentDiscoveryService;
  96 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
78 import org.thingsboard.server.service.queue.TbClusterService; 97 import org.thingsboard.server.service.queue.TbClusterService;
79 import org.thingsboard.server.service.security.model.SecurityUser; 98 import org.thingsboard.server.service.security.model.SecurityUser;
80 import org.thingsboard.server.service.security.permission.AccessControlService; 99 import org.thingsboard.server.service.security.permission.AccessControlService;
@@ -112,6 +131,9 @@ public abstract class BaseController { @@ -112,6 +131,9 @@ public abstract class BaseController {
112 protected TenantService tenantService; 131 protected TenantService tenantService;
113 132
114 @Autowired 133 @Autowired
  134 + protected TenantProfileService tenantProfileService;
  135 +
  136 + @Autowired
115 protected CustomerService customerService; 137 protected CustomerService customerService;
116 138
117 @Autowired 139 @Autowired
@@ -121,6 +143,9 @@ public abstract class BaseController { @@ -121,6 +143,9 @@ public abstract class BaseController {
121 protected DeviceService deviceService; 143 protected DeviceService deviceService;
122 144
123 @Autowired 145 @Autowired
  146 + protected DeviceProfileService deviceProfileService;
  147 +
  148 + @Autowired
124 protected AssetService assetService; 149 protected AssetService assetService;
125 150
126 @Autowired 151 @Autowired
@@ -180,6 +205,9 @@ public abstract class BaseController { @@ -180,6 +205,9 @@ public abstract class BaseController {
180 @Autowired 205 @Autowired
181 protected TbQueueProducerProvider producerProvider; 206 protected TbQueueProducerProvider producerProvider;
182 207
  208 + @Autowired
  209 + protected TbDeviceProfileCache deviceProfileCache;
  210 +
183 @Value("${server.log_controller_error_stack_trace}") 211 @Value("${server.log_controller_error_stack_trace}")
184 @Getter 212 @Getter
185 private boolean logControllerErrorStackTrace; 213 private boolean logControllerErrorStackTrace;
@@ -295,6 +323,30 @@ public abstract class BaseController { @@ -295,6 +323,30 @@ public abstract class BaseController {
295 } 323 }
296 } 324 }
297 325
  326 + TenantInfo checkTenantInfoId(TenantId tenantId, Operation operation) throws ThingsboardException {
  327 + try {
  328 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  329 + TenantInfo tenant = tenantService.findTenantInfoById(tenantId);
  330 + checkNotNull(tenant);
  331 + accessControlService.checkPermission(getCurrentUser(), Resource.TENANT, operation, tenantId, tenant);
  332 + return tenant;
  333 + } catch (Exception e) {
  334 + throw handleException(e, false);
  335 + }
  336 + }
  337 +
  338 + TenantProfile checkTenantProfileId(TenantProfileId tenantProfileId, Operation operation) throws ThingsboardException {
  339 + try {
  340 + validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId);
  341 + TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(getTenantId(), tenantProfileId);
  342 + checkNotNull(tenantProfile);
  343 + accessControlService.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, operation);
  344 + return tenantProfile;
  345 + } catch (Exception e) {
  346 + throw handleException(e, false);
  347 + }
  348 + }
  349 +
298 protected TenantId getTenantId() throws ThingsboardException { 350 protected TenantId getTenantId() throws ThingsboardException {
299 return getCurrentUser().getTenantId(); 351 return getCurrentUser().getTenantId();
300 } 352 }
@@ -343,12 +395,18 @@ public abstract class BaseController { @@ -343,12 +395,18 @@ public abstract class BaseController {
343 case DEVICE: 395 case DEVICE:
344 checkDeviceId(new DeviceId(entityId.getId()), operation); 396 checkDeviceId(new DeviceId(entityId.getId()), operation);
345 return; 397 return;
  398 + case DEVICE_PROFILE:
  399 + checkDeviceProfileId(new DeviceProfileId(entityId.getId()), operation);
  400 + return;
346 case CUSTOMER: 401 case CUSTOMER:
347 checkCustomerId(new CustomerId(entityId.getId()), operation); 402 checkCustomerId(new CustomerId(entityId.getId()), operation);
348 return; 403 return;
349 case TENANT: 404 case TENANT:
350 checkTenantId(new TenantId(entityId.getId()), operation); 405 checkTenantId(new TenantId(entityId.getId()), operation);
351 return; 406 return;
  407 + case TENANT_PROFILE:
  408 + checkTenantProfileId(new TenantProfileId(entityId.getId()), operation);
  409 + return;
352 case RULE_CHAIN: 410 case RULE_CHAIN:
353 checkRuleChain(new RuleChainId(entityId.getId()), operation); 411 checkRuleChain(new RuleChainId(entityId.getId()), operation);
354 return; 412 return;
@@ -408,6 +466,18 @@ public abstract class BaseController { @@ -408,6 +466,18 @@ public abstract class BaseController {
408 } 466 }
409 } 467 }
410 468
  469 + DeviceProfile checkDeviceProfileId(DeviceProfileId deviceProfileId, Operation operation) throws ThingsboardException {
  470 + try {
  471 + validateId(deviceProfileId, "Incorrect deviceProfileId " + deviceProfileId);
  472 + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(getCurrentUser().getTenantId(), deviceProfileId);
  473 + checkNotNull(deviceProfile);
  474 + accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE_PROFILE, operation, deviceProfileId, deviceProfile);
  475 + return deviceProfile;
  476 + } catch (Exception e) {
  477 + throw handleException(e, false);
  478 + }
  479 + }
  480 +
411 protected EntityView checkEntityViewId(EntityViewId entityViewId, Operation operation) throws ThingsboardException { 481 protected EntityView checkEntityViewId(EntityViewId entityViewId, Operation operation) throws ThingsboardException {
412 try { 482 try {
413 validateId(entityViewId, "Incorrect entityViewId " + entityViewId); 483 validateId(entityViewId, "Incorrect entityViewId " + entityViewId);
@@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
47 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.CustomerId; 48 import org.thingsboard.server.common.data.id.CustomerId;
49 import org.thingsboard.server.common.data.id.DeviceId; 49 import org.thingsboard.server.common.data.id.DeviceId;
  50 +import org.thingsboard.server.common.data.id.DeviceProfileId;
50 import org.thingsboard.server.common.data.id.TenantId; 51 import org.thingsboard.server.common.data.id.TenantId;
51 import org.thingsboard.server.common.data.page.PageData; 52 import org.thingsboard.server.common.data.page.PageData;
52 import org.thingsboard.server.common.data.page.PageLink; 53 import org.thingsboard.server.common.data.page.PageLink;
@@ -315,6 +316,7 @@ public class DeviceController extends BaseController { @@ -315,6 +316,7 @@ public class DeviceController extends BaseController {
315 @RequestParam int pageSize, 316 @RequestParam int pageSize,
316 @RequestParam int page, 317 @RequestParam int page,
317 @RequestParam(required = false) String type, 318 @RequestParam(required = false) String type,
  319 + @RequestParam(required = false) String deviceProfileId,
318 @RequestParam(required = false) String textSearch, 320 @RequestParam(required = false) String textSearch,
319 @RequestParam(required = false) String sortProperty, 321 @RequestParam(required = false) String sortProperty,
320 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 322 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
@@ -323,6 +325,9 @@ public class DeviceController extends BaseController { @@ -323,6 +325,9 @@ public class DeviceController extends BaseController {
323 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 325 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
324 if (type != null && type.trim().length() > 0) { 326 if (type != null && type.trim().length() > 0) {
325 return checkNotNull(deviceService.findDeviceInfosByTenantIdAndType(tenantId, type, pageLink)); 327 return checkNotNull(deviceService.findDeviceInfosByTenantIdAndType(tenantId, type, pageLink));
  328 + } else if (deviceProfileId != null && deviceProfileId.length() > 0) {
  329 + DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId));
  330 + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndDeviceProfileId(tenantId, profileId, pageLink));
326 } else { 331 } else {
327 return checkNotNull(deviceService.findDeviceInfosByTenantId(tenantId, pageLink)); 332 return checkNotNull(deviceService.findDeviceInfosByTenantId(tenantId, pageLink));
328 } 333 }
@@ -379,6 +384,7 @@ public class DeviceController extends BaseController { @@ -379,6 +384,7 @@ public class DeviceController extends BaseController {
379 @RequestParam int pageSize, 384 @RequestParam int pageSize,
380 @RequestParam int page, 385 @RequestParam int page,
381 @RequestParam(required = false) String type, 386 @RequestParam(required = false) String type,
  387 + @RequestParam(required = false) String deviceProfileId,
382 @RequestParam(required = false) String textSearch, 388 @RequestParam(required = false) String textSearch,
383 @RequestParam(required = false) String sortProperty, 389 @RequestParam(required = false) String sortProperty,
384 @RequestParam(required = false) String sortOrder) throws ThingsboardException { 390 @RequestParam(required = false) String sortOrder) throws ThingsboardException {
@@ -390,6 +396,9 @@ public class DeviceController extends BaseController { @@ -390,6 +396,9 @@ public class DeviceController extends BaseController {
390 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); 396 PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
391 if (type != null && type.trim().length() > 0) { 397 if (type != null && type.trim().length() > 0) {
392 return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); 398 return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
  399 + } else if (deviceProfileId != null && deviceProfileId.length() > 0) {
  400 + DeviceProfileId profileId = new DeviceProfileId(toUUID(deviceProfileId));
  401 + return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndDeviceProfileId(tenantId, customerId, profileId, pageLink));
393 } else { 402 } else {
394 return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink)); 403 return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink));
395 } 404 }
  1 +/**
  2 + * Copyright © 2016-2020 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.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.DeviceProfile;
  30 +import org.thingsboard.server.common.data.DeviceProfileInfo;
  31 +import org.thingsboard.server.common.data.EntityType;
  32 +import org.thingsboard.server.common.data.audit.ActionType;
  33 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  34 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  35 +import org.thingsboard.server.common.data.page.PageData;
  36 +import org.thingsboard.server.common.data.page.PageLink;
  37 +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  38 +import org.thingsboard.server.queue.util.TbCoreComponent;
  39 +import org.thingsboard.server.service.security.permission.Operation;
  40 +import org.thingsboard.server.service.security.permission.Resource;
  41 +
  42 +@RestController
  43 +@TbCoreComponent
  44 +@RequestMapping("/api")
  45 +@Slf4j
  46 +public class DeviceProfileController extends BaseController {
  47 +
  48 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  49 + @RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.GET)
  50 + @ResponseBody
  51 + public DeviceProfile getDeviceProfileById(@PathVariable("deviceProfileId") String strDeviceProfileId) throws ThingsboardException {
  52 + checkParameter("deviceProfileId", strDeviceProfileId);
  53 + try {
  54 + DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
  55 + return checkDeviceProfileId(deviceProfileId, Operation.READ);
  56 + } catch (Exception e) {
  57 + throw handleException(e);
  58 + }
  59 + }
  60 +
  61 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  62 + @RequestMapping(value = "/deviceProfileInfo/{deviceProfileId}", method = RequestMethod.GET)
  63 + @ResponseBody
  64 + public DeviceProfileInfo getDeviceProfileInfoById(@PathVariable("deviceProfileId") String strDeviceProfileId) throws ThingsboardException {
  65 + checkParameter("deviceProfileId", strDeviceProfileId);
  66 + try {
  67 + DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
  68 + return checkNotNull(deviceProfileService.findDeviceProfileInfoById(getTenantId(), deviceProfileId));
  69 + } catch (Exception e) {
  70 + throw handleException(e);
  71 + }
  72 + }
  73 +
  74 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  75 + @RequestMapping(value = "/deviceProfileInfo/default", method = RequestMethod.GET)
  76 + @ResponseBody
  77 + public DeviceProfileInfo getDefaultDeviceProfileInfo() throws ThingsboardException {
  78 + try {
  79 + return checkNotNull(deviceProfileService.findDefaultDeviceProfileInfo(getTenantId()));
  80 + } catch (Exception e) {
  81 + throw handleException(e);
  82 + }
  83 + }
  84 +
  85 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  86 + @RequestMapping(value = "/deviceProfile", method = RequestMethod.POST)
  87 + @ResponseBody
  88 + public DeviceProfile saveDeviceProfile(@RequestBody DeviceProfile deviceProfile) throws ThingsboardException {
  89 + try {
  90 + boolean created = deviceProfile.getId() == null;
  91 + deviceProfile.setTenantId(getTenantId());
  92 +
  93 + checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
  94 +
  95 + DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
  96 +
  97 + deviceProfileCache.put(savedDeviceProfile);
  98 + tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);
  99 + tbClusterService.onEntityStateChange(deviceProfile.getTenantId(), savedDeviceProfile.getId(),
  100 + created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
  101 +
  102 + logEntityAction(savedDeviceProfile.getId(), savedDeviceProfile,
  103 + null,
  104 + savedDeviceProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
  105 +
  106 + return savedDeviceProfile;
  107 + } catch (Exception e) {
  108 + logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile,
  109 + null, deviceProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
  110 + throw handleException(e);
  111 + }
  112 + }
  113 +
  114 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  115 + @RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.DELETE)
  116 + @ResponseStatus(value = HttpStatus.OK)
  117 + public void deleteDeviceProfile(@PathVariable("deviceProfileId") String strDeviceProfileId) throws ThingsboardException {
  118 + checkParameter("deviceProfileId", strDeviceProfileId);
  119 + try {
  120 + DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
  121 + DeviceProfile deviceProfile = checkDeviceProfileId(deviceProfileId, Operation.DELETE);
  122 + deviceProfileService.deleteDeviceProfile(getTenantId(), deviceProfileId);
  123 + deviceProfileCache.evict(deviceProfileId);
  124 +
  125 + tbClusterService.onDeviceProfileDelete(deviceProfile, null);
  126 + tbClusterService.onEntityStateChange(deviceProfile.getTenantId(), deviceProfile.getId(), ComponentLifecycleEvent.DELETED);
  127 +
  128 + logEntityAction(deviceProfileId, deviceProfile,
  129 + null,
  130 + ActionType.DELETED, null, strDeviceProfileId);
  131 +
  132 + } catch (Exception e) {
  133 + logEntityAction(emptyId(EntityType.DEVICE_PROFILE),
  134 + null,
  135 + null,
  136 + ActionType.DELETED, e, strDeviceProfileId);
  137 + throw handleException(e);
  138 + }
  139 + }
  140 +
  141 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  142 + @RequestMapping(value = "/deviceProfile/{deviceProfileId}/default", method = RequestMethod.POST)
  143 + @ResponseBody
  144 + public DeviceProfile setDefaultDeviceProfile(@PathVariable("deviceProfileId") String strDeviceProfileId) throws ThingsboardException {
  145 + checkParameter("deviceProfileId", strDeviceProfileId);
  146 + try {
  147 + DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
  148 + DeviceProfile deviceProfile = checkDeviceProfileId(deviceProfileId, Operation.WRITE);
  149 + DeviceProfile previousDefaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(getTenantId());
  150 + if (deviceProfileService.setDefaultDeviceProfile(getTenantId(), deviceProfileId)) {
  151 + if (previousDefaultDeviceProfile != null) {
  152 + previousDefaultDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), previousDefaultDeviceProfile.getId());
  153 +
  154 + logEntityAction(previousDefaultDeviceProfile.getId(), previousDefaultDeviceProfile,
  155 + null, ActionType.UPDATED, null);
  156 + }
  157 + deviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfileId);
  158 +
  159 + logEntityAction(deviceProfile.getId(), deviceProfile,
  160 + null, ActionType.UPDATED, null);
  161 + }
  162 + return deviceProfile;
  163 + } catch (Exception e) {
  164 + logEntityAction(emptyId(EntityType.DEVICE_PROFILE),
  165 + null,
  166 + null,
  167 + ActionType.UPDATED, e, strDeviceProfileId);
  168 + throw handleException(e);
  169 + }
  170 + }
  171 +
  172 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  173 + @RequestMapping(value = "/deviceProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET)
  174 + @ResponseBody
  175 + public PageData<DeviceProfile> getDeviceProfiles(@RequestParam int pageSize,
  176 + @RequestParam int page,
  177 + @RequestParam(required = false) String textSearch,
  178 + @RequestParam(required = false) String sortProperty,
  179 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  180 + try {
  181 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  182 + return checkNotNull(deviceProfileService.findDeviceProfiles(getTenantId(), pageLink));
  183 + } catch (Exception e) {
  184 + throw handleException(e);
  185 + }
  186 + }
  187 +
  188 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  189 + @RequestMapping(value = "/deviceProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
  190 + @ResponseBody
  191 + public PageData<DeviceProfileInfo> getDeviceProfileInfos(@RequestParam int pageSize,
  192 + @RequestParam int page,
  193 + @RequestParam(required = false) String textSearch,
  194 + @RequestParam(required = false) String sortProperty,
  195 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  196 + try {
  197 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  198 + return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink));
  199 + } catch (Exception e) {
  200 + throw handleException(e);
  201 + }
  202 + }
  203 +}
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.beans.factory.annotation.Value; 24 import org.springframework.beans.factory.annotation.Value;
25 import org.springframework.http.HttpStatus; 25 import org.springframework.http.HttpStatus;
26 import org.springframework.security.access.prepost.PreAuthorize; 26 import org.springframework.security.access.prepost.PreAuthorize;
  27 +import org.springframework.util.CollectionUtils;
27 import org.springframework.util.StringUtils; 28 import org.springframework.util.StringUtils;
28 import org.springframework.web.bind.annotation.PathVariable; 29 import org.springframework.web.bind.annotation.PathVariable;
29 import org.springframework.web.bind.annotation.RequestBody; 30 import org.springframework.web.bind.annotation.RequestBody;
@@ -47,7 +48,10 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -47,7 +48,10 @@ import org.thingsboard.server.common.data.id.TenantId;
47 import org.thingsboard.server.common.data.page.PageData; 48 import org.thingsboard.server.common.data.page.PageData;
48 import org.thingsboard.server.common.data.page.PageLink; 49 import org.thingsboard.server.common.data.page.PageLink;
49 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 50 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
  51 +import org.thingsboard.server.common.data.rule.DefaultRuleChainCreateRequest;
50 import org.thingsboard.server.common.data.rule.RuleChain; 52 import org.thingsboard.server.common.data.rule.RuleChain;
  53 +import org.thingsboard.server.common.data.rule.RuleChainData;
  54 +import org.thingsboard.server.common.data.rule.RuleChainImportResult;
51 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 55 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
52 import org.thingsboard.server.common.data.rule.RuleNode; 56 import org.thingsboard.server.common.data.rule.RuleNode;
53 import org.thingsboard.server.common.msg.TbMsg; 57 import org.thingsboard.server.common.msg.TbMsg;
@@ -55,6 +59,7 @@ import org.thingsboard.server.common.msg.TbMsgDataType; @@ -55,6 +59,7 @@ import org.thingsboard.server.common.msg.TbMsgDataType;
55 import org.thingsboard.server.common.msg.TbMsgMetaData; 59 import org.thingsboard.server.common.msg.TbMsgMetaData;
56 import org.thingsboard.server.dao.event.EventService; 60 import org.thingsboard.server.dao.event.EventService;
57 import org.thingsboard.server.queue.util.TbCoreComponent; 61 import org.thingsboard.server.queue.util.TbCoreComponent;
  62 +import org.thingsboard.server.service.install.InstallScripts;
58 import org.thingsboard.server.service.script.JsInvokeService; 63 import org.thingsboard.server.service.script.JsInvokeService;
59 import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; 64 import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
60 import org.thingsboard.server.service.security.permission.Operation; 65 import org.thingsboard.server.service.security.permission.Operation;
@@ -78,6 +83,9 @@ public class RuleChainController extends BaseController { @@ -78,6 +83,9 @@ public class RuleChainController extends BaseController {
78 private static final ObjectMapper objectMapper = new ObjectMapper(); 83 private static final ObjectMapper objectMapper = new ObjectMapper();
79 84
80 @Autowired 85 @Autowired
  86 + private InstallScripts installScripts;
  87 +
  88 + @Autowired
81 private EventService eventService; 89 private EventService eventService;
82 90
83 @Autowired 91 @Autowired
@@ -147,6 +155,27 @@ public class RuleChainController extends BaseController { @@ -147,6 +155,27 @@ public class RuleChainController extends BaseController {
147 } 155 }
148 156
149 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") 157 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
  158 + @RequestMapping(value = "/ruleChain/device/default", method = RequestMethod.POST)
  159 + @ResponseBody
  160 + public RuleChain saveRuleChain(@RequestBody DefaultRuleChainCreateRequest request) throws ThingsboardException {
  161 + try {
  162 + checkNotNull(request);
  163 + checkNotNull(request.getName());
  164 +
  165 + RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName());
  166 +
  167 + logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null);
  168 +
  169 + return savedRuleChain;
  170 + } catch (Exception e) {
  171 + RuleChain ruleChain = new RuleChain();
  172 + ruleChain.setName(request.getName());
  173 + logEntityAction(emptyId(EntityType.RULE_CHAIN), ruleChain, null, ActionType.ADDED, e);
  174 + throw handleException(e);
  175 + }
  176 + }
  177 +
  178 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
150 @RequestMapping(value = "/ruleChain/{ruleChainId}/root", method = RequestMethod.POST) 179 @RequestMapping(value = "/ruleChain/{ruleChainId}/root", method = RequestMethod.POST)
151 @ResponseBody 180 @ResponseBody
152 public RuleChain setRootRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException { 181 public RuleChain setRootRuleChain(@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
@@ -360,6 +389,36 @@ public class RuleChainController extends BaseController { @@ -360,6 +389,36 @@ public class RuleChainController extends BaseController {
360 } 389 }
361 } 390 }
362 391
  392 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  393 + @RequestMapping(value = "/ruleChains/export", params = {"limit"}, method = RequestMethod.GET)
  394 + @ResponseBody
  395 + public RuleChainData exportRuleChains(@RequestParam("limit") int limit) throws ThingsboardException {
  396 + try {
  397 + TenantId tenantId = getCurrentUser().getTenantId();
  398 + PageLink pageLink = new PageLink(limit);
  399 + return checkNotNull(ruleChainService.exportTenantRuleChains(tenantId, pageLink));
  400 + } catch (Exception e) {
  401 + throw handleException(e);
  402 + }
  403 + }
  404 +
  405 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  406 + @RequestMapping(value = "/ruleChains/import", method = RequestMethod.POST)
  407 + @ResponseBody
  408 + public void importRuleChains(@RequestBody RuleChainData ruleChainData, @RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException {
  409 + try {
  410 + TenantId tenantId = getCurrentUser().getTenantId();
  411 + List<RuleChainImportResult> importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite);
  412 + if (!CollectionUtils.isEmpty(importResults)) {
  413 + for (RuleChainImportResult importResult : importResults) {
  414 + tbClusterService.onEntityStateChange(importResult.getTenantId(), importResult.getRuleChainId(), importResult.getLifecycleEvent());
  415 + }
  416 + }
  417 + } catch (Exception e) {
  418 + throw handleException(e);
  419 + }
  420 + }
  421 +
363 private String msgToOutput(TbMsg msg) throws Exception { 422 private String msgToOutput(TbMsg msg) throws Exception {
364 ObjectNode msgData = objectMapper.createObjectNode(); 423 ObjectNode msgData = objectMapper.createObjectNode();
365 if (!StringUtils.isEmpty(msg.getData())) { 424 if (!StringUtils.isEmpty(msg.getData())) {
@@ -197,19 +197,21 @@ public class TelemetryController extends BaseController { @@ -197,19 +197,21 @@ public class TelemetryController extends BaseController {
197 @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"}) 197 @RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"})
198 @ResponseBody 198 @ResponseBody
199 public DeferredResult<ResponseEntity> getTimeseries( 199 public DeferredResult<ResponseEntity> getTimeseries(
200 - @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, 200 + @PathVariable("entityType") String entityType,
  201 + @PathVariable("entityId") String entityIdStr,
201 @RequestParam(name = "keys") String keys, 202 @RequestParam(name = "keys") String keys,
202 @RequestParam(name = "startTs") Long startTs, 203 @RequestParam(name = "startTs") Long startTs,
203 @RequestParam(name = "endTs") Long endTs, 204 @RequestParam(name = "endTs") Long endTs,
204 @RequestParam(name = "interval", defaultValue = "0") Long interval, 205 @RequestParam(name = "interval", defaultValue = "0") Long interval,
205 @RequestParam(name = "limit", defaultValue = "100") Integer limit, 206 @RequestParam(name = "limit", defaultValue = "100") Integer limit,
206 @RequestParam(name = "agg", defaultValue = "NONE") String aggStr, 207 @RequestParam(name = "agg", defaultValue = "NONE") String aggStr,
  208 + @RequestParam(name= "orderBy", defaultValue = "DESC") String orderBy,
207 @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { 209 @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
208 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, 210 return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
209 (result, tenantId, entityId) -> { 211 (result, tenantId, entityId) -> {
210 // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted 212 // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted
211 Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); 213 Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr);
212 - List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg)) 214 + List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy))
213 .collect(Collectors.toList()); 215 .collect(Collectors.toList());
214 216
215 Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); 217 Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor());
@@ -28,6 +28,7 @@ import org.springframework.web.bind.annotation.ResponseBody; @@ -28,6 +28,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
28 import org.springframework.web.bind.annotation.ResponseStatus; 28 import org.springframework.web.bind.annotation.ResponseStatus;
29 import org.springframework.web.bind.annotation.RestController; 29 import org.springframework.web.bind.annotation.RestController;
30 import org.thingsboard.server.common.data.Tenant; 30 import org.thingsboard.server.common.data.Tenant;
  31 +import org.thingsboard.server.common.data.TenantInfo;
31 import org.thingsboard.server.common.data.exception.ThingsboardException; 32 import org.thingsboard.server.common.data.exception.ThingsboardException;
32 import org.thingsboard.server.common.data.id.TenantId; 33 import org.thingsboard.server.common.data.id.TenantId;
33 import org.thingsboard.server.common.data.page.PageData; 34 import org.thingsboard.server.common.data.page.PageData;
@@ -58,8 +59,20 @@ public class TenantController extends BaseController { @@ -58,8 +59,20 @@ public class TenantController extends BaseController {
58 checkParameter("tenantId", strTenantId); 59 checkParameter("tenantId", strTenantId);
59 try { 60 try {
60 TenantId tenantId = new TenantId(toUUID(strTenantId)); 61 TenantId tenantId = new TenantId(toUUID(strTenantId));
61 - checkTenantId(tenantId, Operation.READ);  
62 - return checkNotNull(tenantService.findTenantById(tenantId)); 62 + return checkTenantId(tenantId, Operation.READ);
  63 + } catch (Exception e) {
  64 + throw handleException(e);
  65 + }
  66 + }
  67 +
  68 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
  69 + @RequestMapping(value = "/tenant/info/{tenantId}", method = RequestMethod.GET)
  70 + @ResponseBody
  71 + public TenantInfo getTenantInfoById(@PathVariable("tenantId") String strTenantId) throws ThingsboardException {
  72 + checkParameter("tenantId", strTenantId);
  73 + try {
  74 + TenantId tenantId = new TenantId(toUUID(strTenantId));
  75 + return checkTenantInfoId(tenantId, Operation.READ);
63 } catch (Exception e) { 76 } catch (Exception e) {
64 throw handleException(e); 77 throw handleException(e);
65 } 78 }
@@ -115,4 +128,20 @@ public class TenantController extends BaseController { @@ -115,4 +128,20 @@ public class TenantController extends BaseController {
115 } 128 }
116 } 129 }
117 130
  131 + @PreAuthorize("hasAuthority('SYS_ADMIN')")
  132 + @RequestMapping(value = "/tenantInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
  133 + @ResponseBody
  134 + public PageData<TenantInfo> getTenantInfos(@RequestParam int pageSize,
  135 + @RequestParam int page,
  136 + @RequestParam(required = false) String textSearch,
  137 + @RequestParam(required = false) String sortProperty,
  138 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  139 + try {
  140 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  141 + return checkNotNull(tenantService.findTenantInfos(pageLink));
  142 + } catch (Exception e) {
  143 + throw handleException(e);
  144 + }
  145 + }
  146 +
118 } 147 }
  1 +/**
  2 + * Copyright © 2016-2020 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.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.EntityInfo;
  30 +import org.thingsboard.server.common.data.TenantProfile;
  31 +import org.thingsboard.server.common.data.exception.ThingsboardException;
  32 +import org.thingsboard.server.common.data.id.TenantProfileId;
  33 +import org.thingsboard.server.common.data.page.PageData;
  34 +import org.thingsboard.server.common.data.page.PageLink;
  35 +import org.thingsboard.server.queue.util.TbCoreComponent;
  36 +import org.thingsboard.server.service.security.permission.Operation;
  37 +import org.thingsboard.server.service.security.permission.Resource;
  38 +
  39 +@RestController
  40 +@TbCoreComponent
  41 +@RequestMapping("/api")
  42 +@Slf4j
  43 +public class TenantProfileController extends BaseController {
  44 +
  45 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
  46 + @RequestMapping(value = "/tenantProfile/{tenantProfileId}", method = RequestMethod.GET)
  47 + @ResponseBody
  48 + public TenantProfile getTenantProfileById(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException {
  49 + checkParameter("tenantProfileId", strTenantProfileId);
  50 + try {
  51 + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId));
  52 + return checkTenantProfileId(tenantProfileId, Operation.READ);
  53 + } catch (Exception e) {
  54 + throw handleException(e);
  55 + }
  56 + }
  57 +
  58 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
  59 + @RequestMapping(value = "/tenantProfileInfo/{tenantProfileId}", method = RequestMethod.GET)
  60 + @ResponseBody
  61 + public EntityInfo getTenantProfileInfoById(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException {
  62 + checkParameter("tenantProfileId", strTenantProfileId);
  63 + try {
  64 + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId));
  65 + return checkNotNull(tenantProfileService.findTenantProfileInfoById(getTenantId(), tenantProfileId));
  66 + } catch (Exception e) {
  67 + throw handleException(e);
  68 + }
  69 + }
  70 +
  71 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
  72 + @RequestMapping(value = "/tenantProfileInfo/default", method = RequestMethod.GET)
  73 + @ResponseBody
  74 + public EntityInfo getDefaultTenantProfileInfo() throws ThingsboardException {
  75 + try {
  76 + return checkNotNull(tenantProfileService.findDefaultTenantProfileInfo(getTenantId()));
  77 + } catch (Exception e) {
  78 + throw handleException(e);
  79 + }
  80 + }
  81 +
  82 + @PreAuthorize("hasAuthority('SYS_ADMIN')")
  83 + @RequestMapping(value = "/tenantProfile", method = RequestMethod.POST)
  84 + @ResponseBody
  85 + public TenantProfile saveTenantProfile(@RequestBody TenantProfile tenantProfile) throws ThingsboardException {
  86 + try {
  87 + boolean newTenantProfile = tenantProfile.getId() == null;
  88 + if (newTenantProfile) {
  89 + accessControlService
  90 + .checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, Operation.CREATE);
  91 + } else {
  92 + checkEntityId(tenantProfile.getId(), Operation.WRITE);
  93 + }
  94 +
  95 + tenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(getTenantId(), tenantProfile));
  96 + return tenantProfile;
  97 + } catch (Exception e) {
  98 + throw handleException(e);
  99 + }
  100 + }
  101 +
  102 + @PreAuthorize("hasAuthority('SYS_ADMIN')")
  103 + @RequestMapping(value = "/tenantProfile/{tenantProfileId}", method = RequestMethod.DELETE)
  104 + @ResponseStatus(value = HttpStatus.OK)
  105 + public void deleteTenantProfile(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException {
  106 + checkParameter("tenantProfileId", strTenantProfileId);
  107 + try {
  108 + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId));
  109 + checkTenantProfileId(tenantProfileId, Operation.DELETE);
  110 + tenantProfileService.deleteTenantProfile(getTenantId(), tenantProfileId);
  111 + } catch (Exception e) {
  112 + throw handleException(e);
  113 + }
  114 + }
  115 +
  116 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
  117 + @RequestMapping(value = "/tenantProfile/{tenantProfileId}/default", method = RequestMethod.POST)
  118 + @ResponseBody
  119 + public TenantProfile setDefaultTenantProfile(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException {
  120 + checkParameter("tenantProfileId", strTenantProfileId);
  121 + try {
  122 + TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId));
  123 + TenantProfile tenantProfile = checkTenantProfileId(tenantProfileId, Operation.WRITE);
  124 + tenantProfileService.setDefaultTenantProfile(getTenantId(), tenantProfileId);
  125 + return tenantProfile;
  126 + } catch (Exception e) {
  127 + throw handleException(e);
  128 + }
  129 + }
  130 +
  131 + @PreAuthorize("hasAuthority('SYS_ADMIN')")
  132 + @RequestMapping(value = "/tenantProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET)
  133 + @ResponseBody
  134 + public PageData<TenantProfile> getTenantProfiles(@RequestParam int pageSize,
  135 + @RequestParam int page,
  136 + @RequestParam(required = false) String textSearch,
  137 + @RequestParam(required = false) String sortProperty,
  138 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  139 + try {
  140 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  141 + return checkNotNull(tenantProfileService.findTenantProfiles(getTenantId(), pageLink));
  142 + } catch (Exception e) {
  143 + throw handleException(e);
  144 + }
  145 + }
  146 +
  147 + @PreAuthorize("hasAuthority('SYS_ADMIN')")
  148 + @RequestMapping(value = "/tenantProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
  149 + @ResponseBody
  150 + public PageData<EntityInfo> getTenantProfileInfos(@RequestParam int pageSize,
  151 + @RequestParam int page,
  152 + @RequestParam(required = false) String textSearch,
  153 + @RequestParam(required = false) String sortProperty,
  154 + @RequestParam(required = false) String sortOrder) throws ThingsboardException {
  155 + try {
  156 + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
  157 + return checkNotNull(tenantProfileService.findTenantProfileInfos(getTenantId(), pageLink));
  158 + } catch (Exception e) {
  159 + throw handleException(e);
  160 + }
  161 + }
  162 +}
@@ -175,6 +175,14 @@ public class ThingsboardInstallService { @@ -175,6 +175,14 @@ public class ThingsboardInstallService {
175 case "3.1.0": 175 case "3.1.0":
176 log.info("Upgrading ThingsBoard from version 3.1.0 to 3.1.1 ..."); 176 log.info("Upgrading ThingsBoard from version 3.1.0 to 3.1.1 ...");
177 databaseEntitiesUpgradeService.upgradeDatabase("3.1.0"); 177 databaseEntitiesUpgradeService.upgradeDatabase("3.1.0");
  178 + case "3.1.1":
  179 + log.info("Upgrading ThingsBoard from version 3.1.1 to 3.1.2 ...");
  180 + if (databaseTsUpgradeService != null) {
  181 + databaseTsUpgradeService.upgradeDatabase("3.1.1");
  182 + }
  183 + case "3.1.2":
  184 + log.info("Upgrading ThingsBoard from version 3.1.2 to 3.2.0 ...");
  185 + databaseEntitiesUpgradeService.upgradeDatabase("3.1.2");
178 log.info("Updating system data..."); 186 log.info("Updating system data...");
179 systemDataLoaderService.updateSystemWidgets(); 187 systemDataLoaderService.updateSystemWidgets();
180 case "3.1.1": 188 case "3.1.1":
@@ -211,6 +219,7 @@ public class ThingsboardInstallService { @@ -211,6 +219,7 @@ public class ThingsboardInstallService {
211 componentDiscoveryService.discoverComponents(); 219 componentDiscoveryService.discoverComponents();
212 220
213 systemDataLoaderService.createSysAdmin(); 221 systemDataLoaderService.createSysAdmin();
  222 + systemDataLoaderService.createDefaultTenantProfiles();
214 systemDataLoaderService.createAdminSettings(); 223 systemDataLoaderService.createAdminSettings();
215 systemDataLoaderService.loadSystemWidgets(); 224 systemDataLoaderService.loadSystemWidgets();
216 systemDataLoaderService.createOAuth2Templates(); 225 systemDataLoaderService.createOAuth2Templates();
@@ -49,6 +49,7 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase @@ -49,6 +49,7 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase
49 log.info("Schema updated."); 49 log.info("Schema updated.");
50 break; 50 break;
51 case "2.5.0": 51 case "2.5.0":
  52 + case "3.1.1":
52 break; 53 break;
53 default: 54 default:
54 throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); 55 throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion);
@@ -27,11 +27,15 @@ import org.thingsboard.server.common.data.AdminSettings; @@ -27,11 +27,15 @@ import org.thingsboard.server.common.data.AdminSettings;
27 import org.thingsboard.server.common.data.Customer; 27 import org.thingsboard.server.common.data.Customer;
28 import org.thingsboard.server.common.data.DataConstants; 28 import org.thingsboard.server.common.data.DataConstants;
29 import org.thingsboard.server.common.data.Device; 29 import org.thingsboard.server.common.data.Device;
  30 +import org.thingsboard.server.common.data.DeviceProfile;
30 import org.thingsboard.server.common.data.Tenant; 31 import org.thingsboard.server.common.data.Tenant;
  32 +import org.thingsboard.server.common.data.TenantProfile;
  33 +import org.thingsboard.server.common.data.TenantProfileData;
31 import org.thingsboard.server.common.data.User; 34 import org.thingsboard.server.common.data.User;
32 import org.thingsboard.server.common.data.asset.Asset; 35 import org.thingsboard.server.common.data.asset.Asset;
33 import org.thingsboard.server.common.data.id.CustomerId; 36 import org.thingsboard.server.common.data.id.CustomerId;
34 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.DeviceProfileId;
35 import org.thingsboard.server.common.data.id.TenantId; 39 import org.thingsboard.server.common.data.id.TenantId;
36 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; 40 import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
37 import org.thingsboard.server.common.data.kv.BooleanDataEntry; 41 import org.thingsboard.server.common.data.kv.BooleanDataEntry;
@@ -46,9 +50,12 @@ import org.thingsboard.server.dao.asset.AssetService; @@ -46,9 +50,12 @@ import org.thingsboard.server.dao.asset.AssetService;
46 import org.thingsboard.server.dao.attributes.AttributesService; 50 import org.thingsboard.server.dao.attributes.AttributesService;
47 import org.thingsboard.server.dao.customer.CustomerService; 51 import org.thingsboard.server.dao.customer.CustomerService;
48 import org.thingsboard.server.dao.device.DeviceCredentialsService; 52 import org.thingsboard.server.dao.device.DeviceCredentialsService;
  53 +import org.thingsboard.server.dao.device.DeviceProfileService;
49 import org.thingsboard.server.dao.device.DeviceService; 54 import org.thingsboard.server.dao.device.DeviceService;
  55 +import org.thingsboard.server.dao.exception.DataValidationException;
50 import org.thingsboard.server.dao.relation.RelationService; 56 import org.thingsboard.server.dao.relation.RelationService;
51 import org.thingsboard.server.dao.settings.AdminSettingsService; 57 import org.thingsboard.server.dao.settings.AdminSettingsService;
  58 +import org.thingsboard.server.dao.tenant.TenantProfileService;
52 import org.thingsboard.server.dao.tenant.TenantService; 59 import org.thingsboard.server.dao.tenant.TenantService;
53 import org.thingsboard.server.dao.user.UserService; 60 import org.thingsboard.server.dao.user.UserService;
54 import org.thingsboard.server.dao.widget.WidgetsBundleService; 61 import org.thingsboard.server.dao.widget.WidgetsBundleService;
@@ -83,6 +90,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -83,6 +90,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
83 private TenantService tenantService; 90 private TenantService tenantService;
84 91
85 @Autowired 92 @Autowired
  93 + private TenantProfileService tenantProfileService;
  94 +
  95 + @Autowired
86 private CustomerService customerService; 96 private CustomerService customerService;
87 97
88 @Autowired 98 @Autowired
@@ -95,6 +105,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -95,6 +105,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
95 private DeviceService deviceService; 105 private DeviceService deviceService;
96 106
97 @Autowired 107 @Autowired
  108 + private DeviceProfileService deviceProfileService;
  109 +
  110 + @Autowired
98 private AttributesService attributesService; 111 private AttributesService attributesService;
99 112
100 @Autowired 113 @Autowired
@@ -111,6 +124,50 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -111,6 +124,50 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
111 } 124 }
112 125
113 @Override 126 @Override
  127 + public void createDefaultTenantProfiles() throws Exception {
  128 + tenantProfileService.findOrCreateDefaultTenantProfile(TenantId.SYS_TENANT_ID);
  129 +
  130 + TenantProfile isolatedTbCoreProfile = new TenantProfile();
  131 + isolatedTbCoreProfile.setDefault(false);
  132 + isolatedTbCoreProfile.setName("Isolated TB Core");
  133 + isolatedTbCoreProfile.setProfileData(new TenantProfileData());
  134 + isolatedTbCoreProfile.setDescription("Isolated TB Core tenant profile");
  135 + isolatedTbCoreProfile.setIsolatedTbCore(true);
  136 + isolatedTbCoreProfile.setIsolatedTbRuleEngine(false);
  137 + try {
  138 + tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreProfile);
  139 + } catch (DataValidationException e) {
  140 + log.warn(e.getMessage());
  141 + }
  142 +
  143 + TenantProfile isolatedTbRuleEngineProfile = new TenantProfile();
  144 + isolatedTbRuleEngineProfile.setDefault(false);
  145 + isolatedTbRuleEngineProfile.setName("Isolated TB Rule Engine");
  146 + isolatedTbRuleEngineProfile.setProfileData(new TenantProfileData());
  147 + isolatedTbRuleEngineProfile.setDescription("Isolated TB Rule Engine tenant profile");
  148 + isolatedTbRuleEngineProfile.setIsolatedTbCore(false);
  149 + isolatedTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
  150 + try {
  151 + tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbRuleEngineProfile);
  152 + } catch (DataValidationException e) {
  153 + log.warn(e.getMessage());
  154 + }
  155 +
  156 + TenantProfile isolatedTbCoreAndTbRuleEngineProfile = new TenantProfile();
  157 + isolatedTbCoreAndTbRuleEngineProfile.setDefault(false);
  158 + isolatedTbCoreAndTbRuleEngineProfile.setName("Isolated TB Core and TB Rule Engine");
  159 + isolatedTbCoreAndTbRuleEngineProfile.setProfileData(new TenantProfileData());
  160 + isolatedTbCoreAndTbRuleEngineProfile.setDescription("Isolated TB Core and TB Rule Engine tenant profile");
  161 + isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbCore(true);
  162 + isolatedTbCoreAndTbRuleEngineProfile.setIsolatedTbRuleEngine(true);
  163 + try {
  164 + tenantProfileService.saveTenantProfile(TenantId.SYS_TENANT_ID, isolatedTbCoreAndTbRuleEngineProfile);
  165 + } catch (DataValidationException e) {
  166 + log.warn(e.getMessage());
  167 + }
  168 + }
  169 +
  170 + @Override
114 public void createAdminSettings() throws Exception { 171 public void createAdminSettings() throws Exception {
115 AdminSettings generalSettings = new AdminSettings(); 172 AdminSettings generalSettings = new AdminSettings();
116 generalSettings.setKey("general"); 173 generalSettings.setKey("general");
@@ -167,16 +224,18 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -167,16 +224,18 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
167 createUser(Authority.CUSTOMER_USER, demoTenant.getId(), customerB.getId(), "customerB@thingsboard.org", CUSTOMER_CRED); 224 createUser(Authority.CUSTOMER_USER, demoTenant.getId(), customerB.getId(), "customerB@thingsboard.org", CUSTOMER_CRED);
168 createUser(Authority.CUSTOMER_USER, demoTenant.getId(), customerC.getId(), "customerC@thingsboard.org", CUSTOMER_CRED); 225 createUser(Authority.CUSTOMER_USER, demoTenant.getId(), customerC.getId(), "customerC@thingsboard.org", CUSTOMER_CRED);
169 226
170 - createDevice(demoTenant.getId(), customerA.getId(), DEFAULT_DEVICE_TYPE, "Test Device A1", "A1_TEST_TOKEN", null);  
171 - createDevice(demoTenant.getId(), customerA.getId(), DEFAULT_DEVICE_TYPE, "Test Device A2", "A2_TEST_TOKEN", null);  
172 - createDevice(demoTenant.getId(), customerA.getId(), DEFAULT_DEVICE_TYPE, "Test Device A3", "A3_TEST_TOKEN", null);  
173 - createDevice(demoTenant.getId(), customerB.getId(), DEFAULT_DEVICE_TYPE, "Test Device B1", "B1_TEST_TOKEN", null);  
174 - createDevice(demoTenant.getId(), customerC.getId(), DEFAULT_DEVICE_TYPE, "Test Device C1", "C1_TEST_TOKEN", null); 227 + DeviceProfile defaultDeviceProfile = this.deviceProfileService.findOrCreateDeviceProfile(demoTenant.getId(), DEFAULT_DEVICE_TYPE);
  228 +
  229 + createDevice(demoTenant.getId(), customerA.getId(), defaultDeviceProfile.getId(), "Test Device A1", "A1_TEST_TOKEN", null);
  230 + createDevice(demoTenant.getId(), customerA.getId(), defaultDeviceProfile.getId(), "Test Device A2", "A2_TEST_TOKEN", null);
  231 + createDevice(demoTenant.getId(), customerA.getId(), defaultDeviceProfile.getId(), "Test Device A3", "A3_TEST_TOKEN", null);
  232 + createDevice(demoTenant.getId(), customerB.getId(), defaultDeviceProfile.getId(), "Test Device B1", "B1_TEST_TOKEN", null);
  233 + createDevice(demoTenant.getId(), customerC.getId(), defaultDeviceProfile.getId(), "Test Device C1", "C1_TEST_TOKEN", null);
175 234
176 - createDevice(demoTenant.getId(), null, DEFAULT_DEVICE_TYPE, "DHT11 Demo Device", "DHT11_DEMO_TOKEN", "Demo device that is used in sample " + 235 + createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "DHT11 Demo Device", "DHT11_DEMO_TOKEN", "Demo device that is used in sample " +
177 "applications that upload data from DHT11 temperature and humidity sensor"); 236 "applications that upload data from DHT11 temperature and humidity sensor");
178 237
179 - createDevice(demoTenant.getId(), null, DEFAULT_DEVICE_TYPE, "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " + 238 + createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " +
180 "Raspberry Pi GPIO control sample application"); 239 "Raspberry Pi GPIO control sample application");
181 240
182 Asset thermostatAlarms = new Asset(); 241 Asset thermostatAlarms = new Asset();
@@ -185,8 +244,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -185,8 +244,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
185 thermostatAlarms.setType("AlarmPropagationAsset"); 244 thermostatAlarms.setType("AlarmPropagationAsset");
186 thermostatAlarms = assetService.saveAsset(thermostatAlarms); 245 thermostatAlarms = assetService.saveAsset(thermostatAlarms);
187 246
188 - DeviceId t1Id = createDevice(demoTenant.getId(), null, "thermostat", "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();  
189 - DeviceId t2Id = createDevice(demoTenant.getId(), null, "thermostat", "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); 247 + DeviceProfile thermostatDeviceProfile = this.deviceProfileService.findOrCreateDeviceProfile(demoTenant.getId(), "thermostat");
  248 +
  249 + DeviceId t1Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
  250 + DeviceId t2Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId();
190 251
191 relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t1Id, "ToAlarmPropagationAsset")); 252 relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t1Id, "ToAlarmPropagationAsset"));
192 relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t2Id, "ToAlarmPropagationAsset")); 253 relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t2Id, "ToAlarmPropagationAsset"));
@@ -262,14 +323,14 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { @@ -262,14 +323,14 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
262 323
263 private Device createDevice(TenantId tenantId, 324 private Device createDevice(TenantId tenantId,
264 CustomerId customerId, 325 CustomerId customerId,
265 - String type, 326 + DeviceProfileId deviceProfileId,
266 String name, 327 String name,
267 String accessToken, 328 String accessToken,
268 String description) { 329 String description) {
269 Device device = new Device(); 330 Device device = new Device();
270 device.setTenantId(tenantId); 331 device.setTenantId(tenantId);
271 device.setCustomerId(customerId); 332 device.setCustomerId(customerId);
272 - device.setType(type); 333 + device.setDeviceProfileId(deviceProfileId);
273 device.setName(name); 334 device.setName(name);
274 if (description != null) { 335 if (description != null) {
275 ObjectNode additionalInfo = objectMapper.createObjectNode(); 336 ObjectNode additionalInfo = objectMapper.createObjectNode();
@@ -59,6 +59,7 @@ public class InstallScripts { @@ -59,6 +59,7 @@ public class InstallScripts {
59 public static final String JSON_DIR = "json"; 59 public static final String JSON_DIR = "json";
60 public static final String SYSTEM_DIR = "system"; 60 public static final String SYSTEM_DIR = "system";
61 public static final String TENANT_DIR = "tenant"; 61 public static final String TENANT_DIR = "tenant";
  62 + public static final String DEVICE_PROFILE_DIR = "device_profile";
62 public static final String DEMO_DIR = "demo"; 63 public static final String DEMO_DIR = "demo";
63 public static final String RULE_CHAINS_DIR = "rule_chains"; 64 public static final String RULE_CHAINS_DIR = "rule_chains";
64 public static final String WIDGET_BUNDLES_DIR = "widget_bundles"; 65 public static final String WIDGET_BUNDLES_DIR = "widget_bundles";
@@ -89,6 +90,10 @@ public class InstallScripts { @@ -89,6 +90,10 @@ public class InstallScripts {
89 return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); 90 return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR);
90 } 91 }
91 92
  93 + public Path getDeviceProfileDefaultRuleChainTemplateFilePath() {
  94 + return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, DEVICE_PROFILE_DIR, "rule_chain_template.json");
  95 + }
  96 +
92 public String getDataDir() { 97 public String getDataDir() {
93 if (!StringUtils.isEmpty(dataDir)) { 98 if (!StringUtils.isEmpty(dataDir)) {
94 if (!Paths.get(this.dataDir).toFile().isDirectory()) { 99 if (!Paths.get(this.dataDir).toFile().isDirectory()) {
@@ -116,15 +121,7 @@ public class InstallScripts { @@ -116,15 +121,7 @@ public class InstallScripts {
116 dirStream.forEach( 121 dirStream.forEach(
117 path -> { 122 path -> {
118 try { 123 try {
119 - JsonNode ruleChainJson = objectMapper.readTree(path.toFile());  
120 - RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class);  
121 - RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class);  
122 -  
123 - ruleChain.setTenantId(tenantId);  
124 - ruleChain = ruleChainService.saveRuleChain(ruleChain);  
125 -  
126 - ruleChainMetaData.setRuleChainId(ruleChain.getId());  
127 - ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData); 124 + createRuleChainFromFile(tenantId, path, null);
128 } catch (Exception e) { 125 } catch (Exception e) {
129 log.error("Unable to load rule chain from json: [{}]", path.toString()); 126 log.error("Unable to load rule chain from json: [{}]", path.toString());
130 throw new RuntimeException("Unable to load rule chain from json", e); 127 throw new RuntimeException("Unable to load rule chain from json", e);
@@ -134,6 +131,28 @@ public class InstallScripts { @@ -134,6 +131,28 @@ public class InstallScripts {
134 } 131 }
135 } 132 }
136 133
  134 + public RuleChain createDefaultRuleChain(TenantId tenantId, String ruleChainName) throws IOException {
  135 + return createRuleChainFromFile(tenantId, getDeviceProfileDefaultRuleChainTemplateFilePath(), ruleChainName);
  136 + }
  137 +
  138 + public RuleChain createRuleChainFromFile(TenantId tenantId, Path templateFilePath, String newRuleChainName) throws IOException {
  139 + JsonNode ruleChainJson = objectMapper.readTree(templateFilePath.toFile());
  140 + RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class);
  141 + RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class);
  142 +
  143 + ruleChain.setTenantId(tenantId);
  144 + if (!StringUtils.isEmpty(newRuleChainName)) {
  145 + ruleChain.setName(newRuleChainName);
  146 + }
  147 + ruleChain = ruleChainService.saveRuleChain(ruleChain);
  148 +
  149 + ruleChainMetaData.setRuleChainId(ruleChain.getId());
  150 + ruleChainService.saveRuleChainMetaData(new TenantId(EntityId.NULL_UUID), ruleChainMetaData);
  151 +
  152 + return ruleChain;
  153 + }
  154 +
  155 +
137 public void loadSystemWidgets() throws Exception { 156 public void loadSystemWidgets() throws Exception {
138 Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); 157 Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR);
139 try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { 158 try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) {
@@ -195,6 +195,14 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe @@ -195,6 +195,14 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
195 executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001"); 195 executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001");
196 } 196 }
197 break; 197 break;
  198 + case "3.1.1":
  199 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  200 + log.info("Load TTL functions ...");
  201 + loadSql(conn, LOAD_TTL_FUNCTIONS_SQL);
  202 + log.info("Load Drop Partitions functions ...");
  203 + loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL);
  204 + }
  205 + break;
198 default: 206 default:
199 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); 207 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
200 } 208 }
@@ -239,4 +247,4 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe @@ -239,4 +247,4 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe
239 log.info("Failed to load PostgreSQL upgrade functions due to: {}", e.getMessage()); 247 log.info("Failed to load PostgreSQL upgrade functions due to: {}", e.getMessage());
240 } 248 }
241 } 249 }
242 -}  
  250 +}
@@ -20,7 +20,14 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -20,7 +20,14 @@ import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.beans.factory.annotation.Value; 20 import org.springframework.beans.factory.annotation.Value;
21 import org.springframework.context.annotation.Profile; 21 import org.springframework.context.annotation.Profile;
22 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
  23 +import org.thingsboard.server.common.data.EntitySubtype;
  24 +import org.thingsboard.server.common.data.Tenant;
  25 +import org.thingsboard.server.common.data.page.PageData;
  26 +import org.thingsboard.server.common.data.page.PageLink;
23 import org.thingsboard.server.dao.dashboard.DashboardService; 27 import org.thingsboard.server.dao.dashboard.DashboardService;
  28 +import org.thingsboard.server.dao.device.DeviceProfileService;
  29 +import org.thingsboard.server.dao.device.DeviceService;
  30 +import org.thingsboard.server.dao.tenant.TenantService;
24 import org.thingsboard.server.service.install.sql.SqlDbHelper; 31 import org.thingsboard.server.service.install.sql.SqlDbHelper;
25 32
26 import java.nio.charset.Charset; 33 import java.nio.charset.Charset;
@@ -34,6 +41,7 @@ import java.sql.SQLException; @@ -34,6 +41,7 @@ import java.sql.SQLException;
34 import java.sql.SQLSyntaxErrorException; 41 import java.sql.SQLSyntaxErrorException;
35 import java.sql.SQLWarning; 42 import java.sql.SQLWarning;
36 import java.sql.Statement; 43 import java.sql.Statement;
  44 +import java.util.List;
37 45
38 import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO; 46 import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO;
39 import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS; 47 import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS;
@@ -76,6 +84,19 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService @@ -76,6 +84,19 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
76 @Autowired 84 @Autowired
77 private InstallScripts installScripts; 85 private InstallScripts installScripts;
78 86
  87 + @Autowired
  88 + private SystemDataLoaderService systemDataLoaderService;
  89 +
  90 + @Autowired
  91 + private TenantService tenantService;
  92 +
  93 + @Autowired
  94 + private DeviceService deviceService;
  95 +
  96 + @Autowired
  97 + private DeviceProfileService deviceProfileService;
  98 +
  99 +
79 @Override 100 @Override
80 public void upgradeDatabase(String fromVersion) throws Exception { 101 public void upgradeDatabase(String fromVersion) throws Exception {
81 switch (fromVersion) { 102 switch (fromVersion) {
@@ -303,11 +324,72 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService @@ -303,11 +324,72 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
303 log.info("Schema updated."); 324 log.info("Schema updated.");
304 } 325 }
305 break; 326 break;
306 - case "3.1.1": 327 + case "3.1.2":
307 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { 328 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
308 log.info("Updating schema ..."); 329 log.info("Updating schema ...");
309 - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update.sql");  
310 - loadSql(schemaUpdateFile, conn); 330 + if (isOldSchema(conn, 3001000)) {
  331 +
  332 + try {
  333 + conn.createStatement().execute("ALTER TABLE device ADD COLUMN device_profile_id uuid, ADD COLUMN device_data jsonb");
  334 + } catch (Exception e) {
  335 + }
  336 +
  337 + try {
  338 + conn.createStatement().execute("ALTER TABLE tenant ADD COLUMN tenant_profile_id uuid");
  339 + } catch (Exception e) {
  340 + }
  341 +
  342 + try {
  343 + conn.createStatement().execute("CREATE TABLE IF NOT EXISTS rule_node_state (" +
  344 + " id uuid NOT NULL CONSTRAINT rule_node_state_pkey PRIMARY KEY," +
  345 + " created_time bigint NOT NULL," +
  346 + " rule_node_id uuid NOT NULL," +
  347 + " entity_type varchar(32) NOT NULL," +
  348 + " entity_id uuid NOT NULL," +
  349 + " state_data varchar(16384) NOT NULL," +
  350 + " CONSTRAINT rule_node_state_unq_key UNIQUE (rule_node_id, entity_id)," +
  351 + " CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE)");
  352 + } catch (Exception e) {
  353 + }
  354 +
  355 + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.2", "schema_update_before.sql");
  356 + loadSql(schemaUpdateFile, conn);
  357 +
  358 + log.info("Creating default tenant profiles...");
  359 + systemDataLoaderService.createDefaultTenantProfiles();
  360 +
  361 + log.info("Updating tenant profiles...");
  362 + conn.createStatement().execute("call update_tenant_profiles()");
  363 +
  364 + log.info("Creating default device profiles...");
  365 + PageLink pageLink = new PageLink(100);
  366 + PageData<Tenant> pageData;
  367 + do {
  368 + pageData = tenantService.findTenants(pageLink);
  369 + for (Tenant tenant : pageData.getData()) {
  370 + List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenant.getId()).get();
  371 + try {
  372 + deviceProfileService.createDefaultDeviceProfile(tenant.getId());
  373 + } catch (Exception e) {
  374 + }
  375 + for (EntitySubtype deviceType : deviceTypes) {
  376 + try {
  377 + deviceProfileService.findOrCreateDeviceProfile(tenant.getId(), deviceType.getType());
  378 + } catch (Exception e) {
  379 + }
  380 + }
  381 + }
  382 + pageLink = pageLink.nextPageLink();
  383 + } while (pageData.hasNext());
  384 +
  385 + log.info("Updating device profiles...");
  386 + conn.createStatement().execute("call update_device_profiles()");
  387 +
  388 + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.2", "schema_update_after.sql");
  389 + loadSql(schemaUpdateFile, conn);
  390 +
  391 + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3002000;");
  392 + }
311 log.info("Schema updated."); 393 log.info("Schema updated.");
312 } catch (Exception e) { 394 } catch (Exception e) {
313 log.error("Failed updating schema!!!", e); 395 log.error("Failed updating schema!!!", e);
@@ -19,6 +19,8 @@ public interface SystemDataLoaderService { @@ -19,6 +19,8 @@ public interface SystemDataLoaderService {
19 19
20 void createSysAdmin() throws Exception; 20 void createSysAdmin() throws Exception;
21 21
  22 + void createDefaultTenantProfiles() throws Exception;
  23 +
22 void createAdminSettings() throws Exception; 24 void createAdminSettings() throws Exception;
23 25
24 void createOAuth2Templates() throws Exception; 26 void createOAuth2Templates() throws Exception;
@@ -177,6 +177,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr @@ -177,6 +177,8 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
177 executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001"); 177 executeQuery(conn, "UPDATE tb_schema_settings SET schema_version = 2005001");
178 } 178 }
179 break; 179 break;
  180 + case "3.1.1":
  181 + break;
180 default: 182 default:
181 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); 183 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
182 } 184 }
@@ -207,4 +209,4 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr @@ -207,4 +209,4 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr
207 log.info("Failed to load PostgreSQL upgrade functions due to: {}", e.getMessage()); 209 log.info("Failed to load PostgreSQL upgrade functions due to: {}", e.getMessage());
208 } 210 }
209 } 211 }
210 -}  
  212 +}
  1 +/**
  2 + * Copyright © 2016-2020 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.profile;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.stereotype.Service;
  20 +import org.thingsboard.server.common.data.Device;
  21 +import org.thingsboard.server.common.data.DeviceProfile;
  22 +import org.thingsboard.server.common.data.id.DeviceId;
  23 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.dao.device.DeviceProfileService;
  26 +import org.thingsboard.server.dao.device.DeviceService;
  27 +
  28 +import java.util.concurrent.ConcurrentHashMap;
  29 +import java.util.concurrent.ConcurrentMap;
  30 +import java.util.concurrent.locks.Lock;
  31 +import java.util.concurrent.locks.ReentrantLock;
  32 +
  33 +@Service
  34 +@Slf4j
  35 +public class DefaultTbDeviceProfileCache implements TbDeviceProfileCache {
  36 +
  37 + private final Lock deviceProfileFetchLock = new ReentrantLock();
  38 + private final DeviceProfileService deviceProfileService;
  39 + private final DeviceService deviceService;
  40 +
  41 + private final ConcurrentMap<DeviceProfileId, DeviceProfile> deviceProfilesMap = new ConcurrentHashMap<>();
  42 + private final ConcurrentMap<DeviceId, DeviceProfileId> devicesMap = new ConcurrentHashMap<>();
  43 +
  44 + public DefaultTbDeviceProfileCache(DeviceProfileService deviceProfileService, DeviceService deviceService) {
  45 + this.deviceProfileService = deviceProfileService;
  46 + this.deviceService = deviceService;
  47 + }
  48 +
  49 + @Override
  50 + public DeviceProfile get(TenantId tenantId, DeviceProfileId deviceProfileId) {
  51 + DeviceProfile profile = deviceProfilesMap.get(deviceProfileId);
  52 + if (profile == null) {
  53 + deviceProfileFetchLock.lock();
  54 + profile = deviceProfilesMap.get(deviceProfileId);
  55 + if (profile == null) {
  56 + try {
  57 + profile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
  58 + if (profile != null) {
  59 + deviceProfilesMap.put(deviceProfileId, profile);
  60 + }
  61 + } finally {
  62 + deviceProfileFetchLock.unlock();
  63 + }
  64 + }
  65 + }
  66 + return profile;
  67 + }
  68 +
  69 + @Override
  70 + public DeviceProfile get(TenantId tenantId, DeviceId deviceId) {
  71 + DeviceProfileId profileId = devicesMap.get(deviceId);
  72 + if (profileId == null) {
  73 + Device device = deviceService.findDeviceById(tenantId, deviceId);
  74 + if (device != null) {
  75 + profileId = device.getDeviceProfileId();
  76 + devicesMap.put(deviceId, profileId);
  77 + }
  78 + }
  79 + return get(tenantId, profileId);
  80 + }
  81 +
  82 + @Override
  83 + public void put(DeviceProfile profile) {
  84 + if (profile.getId() != null) {
  85 + deviceProfilesMap.put(profile.getId(), profile);
  86 + }
  87 + }
  88 +
  89 + @Override
  90 + public void evict(DeviceProfileId profileId) {
  91 + deviceProfilesMap.remove(profileId);
  92 + }
  93 +
  94 + @Override
  95 + public void evict(DeviceId deviceId) {
  96 + devicesMap.remove(deviceId);
  97 + }
  98 +
  99 +}
  1 +/**
  2 + * Copyright © 2016-2020 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.profile;
  17 +
  18 +import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache;
  19 +import org.thingsboard.server.common.data.DeviceProfile;
  20 +import org.thingsboard.server.common.data.id.DeviceId;
  21 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  22 +
  23 +public interface TbDeviceProfileCache extends RuleEngineDeviceProfileCache {
  24 +
  25 + void put(DeviceProfile profile);
  26 +
  27 + void evict(DeviceProfileId id);
  28 +
  29 + void evict(DeviceId id);
  30 +
  31 +}
@@ -21,14 +21,20 @@ import org.springframework.beans.factory.annotation.Value; @@ -21,14 +21,20 @@ import org.springframework.beans.factory.annotation.Value;
21 import org.springframework.scheduling.annotation.Scheduled; 21 import org.springframework.scheduling.annotation.Scheduled;
22 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
23 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; 23 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
  24 +import org.thingsboard.server.common.data.DeviceProfile;
24 import org.thingsboard.server.common.data.EntityType; 25 import org.thingsboard.server.common.data.EntityType;
  26 +import org.thingsboard.server.common.data.id.DeviceId;
  27 +import org.thingsboard.server.common.data.id.DeviceProfileId;
25 import org.thingsboard.server.common.data.id.EntityId; 28 import org.thingsboard.server.common.data.id.EntityId;
  29 +import org.thingsboard.server.common.data.id.RuleChainId;
26 import org.thingsboard.server.common.data.id.TenantId; 30 import org.thingsboard.server.common.data.id.TenantId;
27 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 31 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
28 import org.thingsboard.server.common.msg.TbMsg; 32 import org.thingsboard.server.common.msg.TbMsg;
29 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 33 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
30 import org.thingsboard.server.common.msg.queue.ServiceType; 34 import org.thingsboard.server.common.msg.queue.ServiceType;
31 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; 35 import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
  36 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  37 +import org.thingsboard.server.gen.transport.TransportProtos;
32 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto; 38 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
33 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; 39 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg;
34 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; 40 import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg;
@@ -40,7 +46,7 @@ import org.thingsboard.server.queue.TbQueueProducer; @@ -40,7 +46,7 @@ import org.thingsboard.server.queue.TbQueueProducer;
40 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 46 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
41 import org.thingsboard.server.queue.discovery.PartitionService; 47 import org.thingsboard.server.queue.discovery.PartitionService;
42 import org.thingsboard.server.queue.provider.TbQueueProducerProvider; 48 import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
43 -import org.thingsboard.server.service.encoding.DataDecodingEncodingService; 49 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
44 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 50 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
45 51
46 import java.util.HashSet; 52 import java.util.HashSet;
@@ -64,11 +70,13 @@ public class DefaultTbClusterService implements TbClusterService { @@ -64,11 +70,13 @@ public class DefaultTbClusterService implements TbClusterService {
64 private final TbQueueProducerProvider producerProvider; 70 private final TbQueueProducerProvider producerProvider;
65 private final PartitionService partitionService; 71 private final PartitionService partitionService;
66 private final DataDecodingEncodingService encodingService; 72 private final DataDecodingEncodingService encodingService;
  73 + private final TbDeviceProfileCache deviceProfileCache;
67 74
68 - public DefaultTbClusterService(TbQueueProducerProvider producerProvider, PartitionService partitionService, DataDecodingEncodingService encodingService) { 75 + public DefaultTbClusterService(TbQueueProducerProvider producerProvider, PartitionService partitionService, DataDecodingEncodingService encodingService, TbDeviceProfileCache deviceProfileCache) {
69 this.producerProvider = producerProvider; 76 this.producerProvider = producerProvider;
70 this.partitionService = partitionService; 77 this.partitionService = partitionService;
71 this.encodingService = encodingService; 78 this.encodingService = encodingService;
  79 + this.deviceProfileCache = deviceProfileCache;
72 } 80 }
73 81
74 @Override 82 @Override
@@ -124,6 +132,12 @@ public class DefaultTbClusterService implements TbClusterService { @@ -124,6 +132,12 @@ public class DefaultTbClusterService implements TbClusterService {
124 log.warn("[{}][{}] Received invalid message: {}", tenantId, entityId, tbMsg); 132 log.warn("[{}][{}] Received invalid message: {}", tenantId, entityId, tbMsg);
125 return; 133 return;
126 } 134 }
  135 + } else {
  136 + if (entityId.getEntityType().equals(EntityType.DEVICE)) {
  137 + tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceId(entityId.getId())));
  138 + } else if (entityId.getEntityType().equals(EntityType.DEVICE_PROFILE)) {
  139 + tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId())));
  140 + }
127 } 141 }
128 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, entityId); 142 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, entityId);
129 log.trace("PUSHING msg: {} to:{}", tbMsg, tpi); 143 log.trace("PUSHING msg: {} to:{}", tbMsg, tpi);
@@ -135,6 +149,16 @@ public class DefaultTbClusterService implements TbClusterService { @@ -135,6 +149,16 @@ public class DefaultTbClusterService implements TbClusterService {
135 toRuleEngineMsgs.incrementAndGet(); 149 toRuleEngineMsgs.incrementAndGet();
136 } 150 }
137 151
  152 + private TbMsg transformMsg(TbMsg tbMsg, DeviceProfile deviceProfile) {
  153 + if (deviceProfile != null) {
  154 + RuleChainId targetRuleChainId = deviceProfile.getDefaultRuleChainId();
  155 + if (targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId())) {
  156 + tbMsg = TbMsg.transformMsg(tbMsg, targetRuleChainId);
  157 + }
  158 + }
  159 + return tbMsg;
  160 + }
  161 +
138 @Override 162 @Override
139 public void pushNotificationToRuleEngine(String serviceId, FromDeviceRpcResponse response, TbQueueCallback callback) { 163 public void pushNotificationToRuleEngine(String serviceId, FromDeviceRpcResponse response, TbQueueCallback callback) {
140 TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId); 164 TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_RULE_ENGINE, serviceId);
@@ -163,6 +187,36 @@ public class DefaultTbClusterService implements TbClusterService { @@ -163,6 +187,36 @@ public class DefaultTbClusterService implements TbClusterService {
163 broadcast(new ComponentLifecycleMsg(tenantId, entityId, state)); 187 broadcast(new ComponentLifecycleMsg(tenantId, entityId, state));
164 } 188 }
165 189
  190 + @Override
  191 + public void onDeviceProfileChange(DeviceProfile deviceProfile, TbQueueCallback callback) {
  192 + log.trace("[{}][{}] Processing device profile [{}] change event", deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile.getName());
  193 + TransportProtos.DeviceProfileUpdateMsg profileUpdateMsg = TransportProtos.DeviceProfileUpdateMsg.newBuilder()
  194 + .setData(ByteString.copyFrom(encodingService.encode(deviceProfile))).build();
  195 + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setDeviceProfileUpdateMsg(profileUpdateMsg).build();
  196 + broadcast(transportMsg);
  197 + }
  198 +
  199 + @Override
  200 + public void onDeviceProfileDelete(DeviceProfile deviceProfile, TbQueueCallback callback) {
  201 + log.trace("[{}][{}] Processing device profile [{}] delete event", deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile.getName());
  202 + TransportProtos.DeviceProfileDeleteMsg profileDeleteMsg = TransportProtos.DeviceProfileDeleteMsg.newBuilder()
  203 + .setProfileIdMSB(deviceProfile.getId().getId().getMostSignificantBits())
  204 + .setProfileIdLSB(deviceProfile.getId().getId().getLeastSignificantBits())
  205 + .build();
  206 + ToTransportMsg transportMsg = ToTransportMsg.newBuilder().setDeviceProfileDeleteMsg(profileDeleteMsg).build();
  207 + broadcast(transportMsg);
  208 + }
  209 +
  210 + private void broadcast(ToTransportMsg transportMsg) {
  211 + TbQueueProducer<TbProtoQueueMsg<ToTransportMsg>> toTransportNfProducer = producerProvider.getTransportNotificationsMsgProducer();
  212 + Set<String> tbTransportServices = partitionService.getAllServiceIds(ServiceType.TB_TRANSPORT);
  213 + for (String transportServiceId : tbTransportServices) {
  214 + TopicPartitionInfo tpi = partitionService.getNotificationsTopic(ServiceType.TB_TRANSPORT, transportServiceId);
  215 + toTransportNfProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), transportMsg), null);
  216 + toTransportNfs.incrementAndGet();
  217 + }
  218 + }
  219 +
166 private void broadcast(ComponentLifecycleMsg msg) { 220 private void broadcast(ComponentLifecycleMsg msg) {
167 byte[] msgBytes = encodingService.encode(msg); 221 byte[] msgBytes = encodingService.encode(msg);
168 TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer(); 222 TbQueueProducer<TbProtoQueueMsg<ToRuleEngineNotificationMsg>> toRuleEngineProducer = producerProvider.getRuleEngineNotificationsMsgProducer();
@@ -21,10 +21,13 @@ import org.springframework.scheduling.annotation.Scheduled; @@ -21,10 +21,13 @@ import org.springframework.scheduling.annotation.Scheduled;
21 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
22 import org.thingsboard.rule.engine.api.RpcError; 22 import org.thingsboard.rule.engine.api.RpcError;
23 import org.thingsboard.server.actors.ActorSystemContext; 23 import org.thingsboard.server.actors.ActorSystemContext;
  24 +import org.thingsboard.server.common.data.EntityType;
24 import org.thingsboard.server.common.data.alarm.Alarm; 25 import org.thingsboard.server.common.data.alarm.Alarm;
  26 +import org.thingsboard.server.common.data.id.DeviceProfileId;
25 import org.thingsboard.server.common.data.id.TenantId; 27 import org.thingsboard.server.common.data.id.TenantId;
26 import org.thingsboard.server.common.msg.MsgType; 28 import org.thingsboard.server.common.msg.MsgType;
27 import org.thingsboard.server.common.msg.TbActorMsg; 29 import org.thingsboard.server.common.msg.TbActorMsg;
  30 +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
28 import org.thingsboard.server.common.msg.queue.ServiceType; 31 import org.thingsboard.server.common.msg.queue.ServiceType;
29 import org.thingsboard.server.common.msg.queue.TbCallback; 32 import org.thingsboard.server.common.msg.queue.TbCallback;
30 import org.thingsboard.server.dao.util.mapping.JacksonUtil; 33 import org.thingsboard.server.dao.util.mapping.JacksonUtil;
@@ -47,7 +50,8 @@ import org.thingsboard.server.queue.discovery.PartitionChangeEvent; @@ -47,7 +50,8 @@ import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
47 import org.thingsboard.server.queue.provider.TbCoreQueueFactory; 50 import org.thingsboard.server.queue.provider.TbCoreQueueFactory;
48 import org.thingsboard.server.common.stats.StatsFactory; 51 import org.thingsboard.server.common.stats.StatsFactory;
49 import org.thingsboard.server.queue.util.TbCoreComponent; 52 import org.thingsboard.server.queue.util.TbCoreComponent;
50 -import org.thingsboard.server.service.encoding.DataDecodingEncodingService; 53 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  54 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
51 import org.thingsboard.server.service.queue.processing.AbstractConsumerService; 55 import org.thingsboard.server.service.queue.processing.AbstractConsumerService;
52 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 56 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
53 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; 57 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
@@ -92,8 +96,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -92,8 +96,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
92 public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory, ActorSystemContext actorContext, 96 public DefaultTbCoreConsumerService(TbCoreQueueFactory tbCoreQueueFactory, ActorSystemContext actorContext,
93 DeviceStateService stateService, TbLocalSubscriptionService localSubscriptionService, 97 DeviceStateService stateService, TbLocalSubscriptionService localSubscriptionService,
94 SubscriptionManagerService subscriptionManagerService, DataDecodingEncodingService encodingService, 98 SubscriptionManagerService subscriptionManagerService, DataDecodingEncodingService encodingService,
95 - TbCoreDeviceRpcService tbCoreDeviceRpcService, StatsFactory statsFactory) {  
96 - super(actorContext, encodingService, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer()); 99 + TbCoreDeviceRpcService tbCoreDeviceRpcService, StatsFactory statsFactory, TbDeviceProfileCache deviceProfileCache) {
  100 + super(actorContext, encodingService, deviceProfileCache, tbCoreQueueFactory.createToCoreNotificationsMsgConsumer());
97 this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer(); 101 this.mainConsumer = tbCoreQueueFactory.createToCoreMsgConsumer();
98 this.stateService = stateService; 102 this.stateService = stateService;
99 this.localSubscriptionService = localSubscriptionService; 103 this.localSubscriptionService = localSubscriptionService;
@@ -211,11 +215,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore @@ -211,11 +215,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore
211 log.trace("[{}] Forwarding message to RPC service {}", id, toCoreNotification.getFromDeviceRpcResponse()); 215 log.trace("[{}] Forwarding message to RPC service {}", id, toCoreNotification.getFromDeviceRpcResponse());
212 forwardToCoreRpcService(toCoreNotification.getFromDeviceRpcResponse(), callback); 216 forwardToCoreRpcService(toCoreNotification.getFromDeviceRpcResponse(), callback);
213 } else if (toCoreNotification.getComponentLifecycleMsg() != null && !toCoreNotification.getComponentLifecycleMsg().isEmpty()) { 217 } else if (toCoreNotification.getComponentLifecycleMsg() != null && !toCoreNotification.getComponentLifecycleMsg().isEmpty()) {
214 - Optional<TbActorMsg> actorMsg = encodingService.decode(toCoreNotification.getComponentLifecycleMsg().toByteArray());  
215 - if (actorMsg.isPresent()) {  
216 - log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get());  
217 - actorContext.tellWithHighPriority(actorMsg.get());  
218 - } 218 + handleComponentLifecycleMsg(id, toCoreNotification.getComponentLifecycleMsg());
219 callback.onSuccess(); 219 callback.onSuccess();
220 } 220 }
221 if (statsEnabled) { 221 if (statsEnabled) {
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.service.queue; 16 package org.thingsboard.server.service.queue;
17 17
  18 +import com.google.protobuf.ByteString;
18 import com.google.protobuf.ProtocolStringList; 19 import com.google.protobuf.ProtocolStringList;
19 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
20 import org.springframework.beans.factory.annotation.Value; 21 import org.springframework.beans.factory.annotation.Value;
@@ -37,7 +38,8 @@ import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory; @@ -37,7 +38,8 @@ import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory;
37 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; 38 import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings;
38 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration; 39 import org.thingsboard.server.queue.settings.TbRuleEngineQueueConfiguration;
39 import org.thingsboard.server.queue.util.TbRuleEngineComponent; 40 import org.thingsboard.server.queue.util.TbRuleEngineComponent;
40 -import org.thingsboard.server.service.encoding.DataDecodingEncodingService; 41 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  42 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
41 import org.thingsboard.server.service.queue.processing.*; 43 import org.thingsboard.server.service.queue.processing.*;
42 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; 44 import org.thingsboard.server.service.rpc.FromDeviceRpcResponse;
43 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; 45 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
@@ -80,8 +82,8 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -80,8 +82,8 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
80 TbRuleEngineQueueFactory tbRuleEngineQueueFactory, RuleEngineStatisticsService statisticsService, 82 TbRuleEngineQueueFactory tbRuleEngineQueueFactory, RuleEngineStatisticsService statisticsService,
81 ActorSystemContext actorContext, DataDecodingEncodingService encodingService, 83 ActorSystemContext actorContext, DataDecodingEncodingService encodingService,
82 TbRuleEngineDeviceRpcService tbDeviceRpcService, 84 TbRuleEngineDeviceRpcService tbDeviceRpcService,
83 - StatsFactory statsFactory) {  
84 - super(actorContext, encodingService, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer()); 85 + StatsFactory statsFactory, TbDeviceProfileCache deviceProfileCache) {
  86 + super(actorContext, encodingService, deviceProfileCache, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer());
85 this.statisticsService = statisticsService; 87 this.statisticsService = statisticsService;
86 this.ruleEngineSettings = ruleEngineSettings; 88 this.ruleEngineSettings = ruleEngineSettings;
87 this.tbRuleEngineQueueFactory = tbRuleEngineQueueFactory; 89 this.tbRuleEngineQueueFactory = tbRuleEngineQueueFactory;
@@ -144,7 +146,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -144,7 +146,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
144 submitStrategy.init(msgs); 146 submitStrategy.init(msgs);
145 147
146 while (!stopped) { 148 while (!stopped) {
147 - TbMsgPackProcessingContext ctx = new TbMsgPackProcessingContext(submitStrategy); 149 + TbMsgPackProcessingContext ctx = new TbMsgPackProcessingContext(configuration.getName(), submitStrategy);
148 submitStrategy.submitAttempt((id, msg) -> submitExecutor.submit(() -> { 150 submitStrategy.submitAttempt((id, msg) -> submitExecutor.submit(() -> {
149 log.trace("[{}] Creating callback for message: {}", id, msg.getValue()); 151 log.trace("[{}] Creating callback for message: {}", id, msg.getValue());
150 ToRuleEngineMsg toRuleEngineMsg = msg.getValue(); 152 ToRuleEngineMsg toRuleEngineMsg = msg.getValue();
@@ -175,6 +177,8 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -175,6 +177,8 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
175 if (!ctx.getFailedMap().isEmpty()) { 177 if (!ctx.getFailedMap().isEmpty()) {
176 printFirstOrAll(configuration, ctx, ctx.getFailedMap(), "Failed"); 178 printFirstOrAll(configuration, ctx, ctx.getFailedMap(), "Failed");
177 } 179 }
  180 + ctx.printProfilerStats();
  181 +
178 TbRuleEngineProcessingDecision decision = ackStrategy.analyze(result); 182 TbRuleEngineProcessingDecision decision = ackStrategy.analyze(result);
179 if (statsEnabled) { 183 if (statsEnabled) {
180 stats.log(result, decision.isCommit()); 184 stats.log(result, decision.isCommit());
@@ -237,11 +241,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< @@ -237,11 +241,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
237 protected void handleNotification(UUID id, TbProtoQueueMsg<ToRuleEngineNotificationMsg> msg, TbCallback callback) throws Exception { 241 protected void handleNotification(UUID id, TbProtoQueueMsg<ToRuleEngineNotificationMsg> msg, TbCallback callback) throws Exception {
238 ToRuleEngineNotificationMsg nfMsg = msg.getValue(); 242 ToRuleEngineNotificationMsg nfMsg = msg.getValue();
239 if (nfMsg.getComponentLifecycleMsg() != null && !nfMsg.getComponentLifecycleMsg().isEmpty()) { 243 if (nfMsg.getComponentLifecycleMsg() != null && !nfMsg.getComponentLifecycleMsg().isEmpty()) {
240 - Optional<TbActorMsg> actorMsg = encodingService.decode(nfMsg.getComponentLifecycleMsg().toByteArray());  
241 - if (actorMsg.isPresent()) {  
242 - log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get());  
243 - actorContext.tellWithHighPriority(actorMsg.get());  
244 - } 244 + handleComponentLifecycleMsg(id, nfMsg.getComponentLifecycleMsg());
245 callback.onSuccess(); 245 callback.onSuccess();
246 } else if (nfMsg.hasFromDeviceRpcResponse()) { 246 } else if (nfMsg.hasFromDeviceRpcResponse()) {
247 TransportProtos.FromDeviceRPCResponseProto proto = nfMsg.getFromDeviceRpcResponse(); 247 TransportProtos.FromDeviceRPCResponseProto proto = nfMsg.getFromDeviceRpcResponse();
@@ -19,7 +19,9 @@ import lombok.extern.slf4j.Slf4j; @@ -19,7 +19,9 @@ import lombok.extern.slf4j.Slf4j;
19 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; 19 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
20 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
21 import org.thingsboard.server.common.data.Tenant; 21 import org.thingsboard.server.common.data.Tenant;
  22 +import org.thingsboard.server.common.data.TenantProfile;
22 import org.thingsboard.server.common.data.id.TenantId; 23 import org.thingsboard.server.common.data.id.TenantId;
  24 +import org.thingsboard.server.dao.tenant.TenantProfileService;
23 import org.thingsboard.server.dao.tenant.TenantService; 25 import org.thingsboard.server.dao.tenant.TenantService;
24 import org.thingsboard.server.queue.discovery.TenantRoutingInfo; 26 import org.thingsboard.server.queue.discovery.TenantRoutingInfo;
25 import org.thingsboard.server.queue.discovery.TenantRoutingInfoService; 27 import org.thingsboard.server.queue.discovery.TenantRoutingInfoService;
@@ -31,15 +33,20 @@ public class DefaultTenantRoutingInfoService implements TenantRoutingInfoService @@ -31,15 +33,20 @@ public class DefaultTenantRoutingInfoService implements TenantRoutingInfoService
31 33
32 private final TenantService tenantService; 34 private final TenantService tenantService;
33 35
34 - public DefaultTenantRoutingInfoService(TenantService tenantService) { 36 + private final TenantProfileService tenantProfileService;
  37 +
  38 + public DefaultTenantRoutingInfoService(TenantService tenantService, TenantProfileService tenantProfileService) {
35 this.tenantService = tenantService; 39 this.tenantService = tenantService;
  40 + this.tenantProfileService = tenantProfileService;
36 } 41 }
37 42
38 @Override 43 @Override
39 public TenantRoutingInfo getRoutingInfo(TenantId tenantId) { 44 public TenantRoutingInfo getRoutingInfo(TenantId tenantId) {
40 Tenant tenant = tenantService.findTenantById(tenantId); 45 Tenant tenant = tenantService.findTenantById(tenantId);
41 if (tenant != null) { 46 if (tenant != null) {
42 - return new TenantRoutingInfo(tenantId, tenant.isIsolatedTbCore(), tenant.isIsolatedTbRuleEngine()); 47 + // TODO: Tenant Profile from cache
  48 + TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(tenantId, tenant.getTenantProfileId());
  49 + return new TenantRoutingInfo(tenantId, tenantProfile.isIsolatedTbCore(), tenantProfile.isIsolatedTbRuleEngine());
43 } else { 50 } else {
44 throw new RuntimeException("Tenant not found!"); 51 throw new RuntimeException("Tenant not found!");
45 } 52 }
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 package org.thingsboard.server.service.queue; 16 package org.thingsboard.server.service.queue;
17 17
18 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; 18 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
  19 +import org.thingsboard.server.common.data.DeviceProfile;
  20 +import org.thingsboard.server.common.data.id.DeviceProfileId;
19 import org.thingsboard.server.common.data.id.EntityId; 21 import org.thingsboard.server.common.data.id.EntityId;
20 import org.thingsboard.server.common.data.id.TenantId; 22 import org.thingsboard.server.common.data.id.TenantId;
21 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; 23 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
@@ -49,4 +51,7 @@ public interface TbClusterService { @@ -49,4 +51,7 @@ public interface TbClusterService {
49 51
50 void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state); 52 void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state);
51 53
  54 + void onDeviceProfileChange(DeviceProfile deviceProfile, TbQueueCallback callback);
  55 +
  56 + void onDeviceProfileDelete(DeviceProfile deviceProfileId, TbQueueCallback callback);
52 } 57 }
@@ -67,8 +67,14 @@ public class TbMsgPackCallback implements TbMsgCallback { @@ -67,8 +67,14 @@ public class TbMsgPackCallback implements TbMsgCallback {
67 } 67 }
68 68
69 @Override 69 @Override
70 - public void visit(RuleNodeInfo ruleNodeInfo) {  
71 - log.trace("[{}] ON PROCESS: {}", id, ruleNodeInfo);  
72 - ctx.visit(id, ruleNodeInfo); 70 + public void onProcessingStart(RuleNodeInfo ruleNodeInfo) {
  71 + log.trace("[{}] ON PROCESSING START: {}", id, ruleNodeInfo);
  72 + ctx.onProcessingStart(id, ruleNodeInfo);
  73 + }
  74 +
  75 + @Override
  76 + public void onProcessingEnd(RuleNodeId ruleNodeId) {
  77 + log.trace("[{}] ON PROCESSING END: {}", id, ruleNodeId);
  78 + ctx.onProcessingEnd(id, ruleNodeId);
73 } 79 }
74 } 80 }
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.service.queue; 16 package org.thingsboard.server.service.queue;
17 17
18 import lombok.Getter; 18 import lombok.Getter;
  19 +import lombok.extern.slf4j.Slf4j;
19 import org.thingsboard.server.common.data.id.RuleNodeId; 20 import org.thingsboard.server.common.data.id.RuleNodeId;
20 import org.thingsboard.server.common.data.id.TenantId; 21 import org.thingsboard.server.common.data.id.TenantId;
21 import org.thingsboard.server.common.msg.queue.RuleEngineException; 22 import org.thingsboard.server.common.msg.queue.RuleEngineException;
@@ -24,6 +25,8 @@ import org.thingsboard.server.gen.transport.TransportProtos; @@ -24,6 +25,8 @@ import org.thingsboard.server.gen.transport.TransportProtos;
24 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 25 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
25 import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy; 26 import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategy;
26 27
  28 +import java.util.Comparator;
  29 +import java.util.Map;
27 import java.util.UUID; 30 import java.util.UUID;
28 import java.util.concurrent.ConcurrentHashMap; 31 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ConcurrentMap; 32 import java.util.concurrent.ConcurrentMap;
@@ -31,9 +34,13 @@ import java.util.concurrent.CountDownLatch; @@ -31,9 +34,13 @@ import java.util.concurrent.CountDownLatch;
31 import java.util.concurrent.TimeUnit; 34 import java.util.concurrent.TimeUnit;
32 import java.util.concurrent.atomic.AtomicInteger; 35 import java.util.concurrent.atomic.AtomicInteger;
33 36
  37 +@Slf4j
34 public class TbMsgPackProcessingContext { 38 public class TbMsgPackProcessingContext {
35 39
  40 + private final String queueName;
36 private final TbRuleEngineSubmitStrategy submitStrategy; 41 private final TbRuleEngineSubmitStrategy submitStrategy;
  42 + @Getter
  43 + private final boolean profilerEnabled;
37 private final AtomicInteger pendingCount; 44 private final AtomicInteger pendingCount;
38 private final CountDownLatch processingTimeoutLatch = new CountDownLatch(1); 45 private final CountDownLatch processingTimeoutLatch = new CountDownLatch(1);
39 @Getter 46 @Getter
@@ -47,14 +54,20 @@ public class TbMsgPackProcessingContext { @@ -47,14 +54,20 @@ public class TbMsgPackProcessingContext {
47 54
48 private final ConcurrentMap<UUID, RuleNodeInfo> lastRuleNodeMap = new ConcurrentHashMap<>(); 55 private final ConcurrentMap<UUID, RuleNodeInfo> lastRuleNodeMap = new ConcurrentHashMap<>();
49 56
50 - public TbMsgPackProcessingContext(TbRuleEngineSubmitStrategy submitStrategy) { 57 + public TbMsgPackProcessingContext(String queueName, TbRuleEngineSubmitStrategy submitStrategy) {
  58 + this.queueName = queueName;
51 this.submitStrategy = submitStrategy; 59 this.submitStrategy = submitStrategy;
  60 + this.profilerEnabled = log.isDebugEnabled();
52 this.pendingMap = submitStrategy.getPendingMap(); 61 this.pendingMap = submitStrategy.getPendingMap();
53 this.pendingCount = new AtomicInteger(pendingMap.size()); 62 this.pendingCount = new AtomicInteger(pendingMap.size());
54 } 63 }
55 64
56 public boolean await(long packProcessingTimeout, TimeUnit milliseconds) throws InterruptedException { 65 public boolean await(long packProcessingTimeout, TimeUnit milliseconds) throws InterruptedException {
57 - return processingTimeoutLatch.await(packProcessingTimeout, milliseconds); 66 + boolean success = processingTimeoutLatch.await(packProcessingTimeout, milliseconds);
  67 + if (!success && profilerEnabled) {
  68 + msgProfilerMap.values().forEach(this::onTimeout);
  69 + }
  70 + return success;
58 } 71 }
59 72
60 public void onSuccess(UUID id) { 73 public void onSuccess(UUID id) {
@@ -85,12 +98,53 @@ public class TbMsgPackProcessingContext { @@ -85,12 +98,53 @@ public class TbMsgPackProcessingContext {
85 } 98 }
86 } 99 }
87 100
88 - public void visit(UUID id, RuleNodeInfo ruleNodeInfo) { 101 + private final ConcurrentHashMap<UUID, TbMsgProfilerInfo> msgProfilerMap = new ConcurrentHashMap<>();
  102 + private final ConcurrentHashMap<UUID, TbRuleNodeProfilerInfo> ruleNodeProfilerMap = new ConcurrentHashMap<>();
  103 +
  104 + public void onProcessingStart(UUID id, RuleNodeInfo ruleNodeInfo) {
89 lastRuleNodeMap.put(id, ruleNodeInfo); 105 lastRuleNodeMap.put(id, ruleNodeInfo);
  106 + if (profilerEnabled) {
  107 + msgProfilerMap.computeIfAbsent(id, TbMsgProfilerInfo::new).onStart(ruleNodeInfo.getRuleNodeId());
  108 + ruleNodeProfilerMap.putIfAbsent(ruleNodeInfo.getRuleNodeId().getId(), new TbRuleNodeProfilerInfo(ruleNodeInfo));
  109 + }
  110 + }
  111 +
  112 + public void onProcessingEnd(UUID id, RuleNodeId ruleNodeId) {
  113 + if (profilerEnabled) {
  114 + long processingTime = msgProfilerMap.computeIfAbsent(id, TbMsgProfilerInfo::new).onEnd(ruleNodeId);
  115 + if (processingTime > 0) {
  116 + ruleNodeProfilerMap.computeIfAbsent(ruleNodeId.getId(), TbRuleNodeProfilerInfo::new).record(processingTime);
  117 + }
  118 + }
  119 + }
  120 +
  121 + public void onTimeout(TbMsgProfilerInfo profilerInfo) {
  122 + Map.Entry<UUID, Long> ruleNodeInfo = profilerInfo.onTimeout();
  123 + if (ruleNodeInfo != null) {
  124 + ruleNodeProfilerMap.computeIfAbsent(ruleNodeInfo.getKey(), TbRuleNodeProfilerInfo::new).record(ruleNodeInfo.getValue());
  125 + }
90 } 126 }
91 127
92 public RuleNodeInfo getLastVisitedRuleNode(UUID id) { 128 public RuleNodeInfo getLastVisitedRuleNode(UUID id) {
93 return lastRuleNodeMap.get(id); 129 return lastRuleNodeMap.get(id);
94 } 130 }
95 131
  132 + public void printProfilerStats() {
  133 + if (profilerEnabled) {
  134 + log.debug("Top Rule Nodes by max execution time:");
  135 + ruleNodeProfilerMap.values().stream()
  136 + .sorted(Comparator.comparingLong(TbRuleNodeProfilerInfo::getMaxExecutionTime).reversed()).limit(5)
  137 + .forEach(info -> log.debug("[{}][{}] max execution time: {}. {}", queueName, info.getRuleNodeId(), info.getMaxExecutionTime(), info.getLabel()));
  138 +
  139 + log.info("Top Rule Nodes by avg execution time:");
  140 + ruleNodeProfilerMap.values().stream()
  141 + .sorted(Comparator.comparingDouble(TbRuleNodeProfilerInfo::getAvgExecutionTime).reversed()).limit(5)
  142 + .forEach(info -> log.info("[{}][{}] avg execution time: {}. {}", queueName, info.getRuleNodeId(), info.getAvgExecutionTime(), info.getLabel()));
  143 +
  144 + log.info("Top Rule Nodes by execution count:");
  145 + ruleNodeProfilerMap.values().stream()
  146 + .sorted(Comparator.comparingInt(TbRuleNodeProfilerInfo::getExecutionCount).reversed()).limit(5)
  147 + .forEach(info -> log.info("[{}][{}] execution count: {}. {}", queueName, info.getRuleNodeId(), info.getExecutionCount(), info.getLabel()));
  148 + }
  149 + }
96 } 150 }
  1 +/**
  2 + * Copyright © 2016-2020 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.queue;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.server.common.data.id.RuleNodeId;
  20 +import org.thingsboard.server.common.msg.queue.RuleNodeInfo;
  21 +
  22 +import java.util.AbstractMap;
  23 +import java.util.Map;
  24 +import java.util.UUID;
  25 +import java.util.concurrent.atomic.AtomicLong;
  26 +import java.util.concurrent.locks.Lock;
  27 +import java.util.concurrent.locks.ReentrantLock;
  28 +
  29 +@Slf4j
  30 +public class TbMsgProfilerInfo {
  31 + private final UUID msgId;
  32 + private AtomicLong totalProcessingTime = new AtomicLong();
  33 + private Lock stateLock = new ReentrantLock();
  34 + private RuleNodeId currentRuleNodeId;
  35 + private long stateChangeTime;
  36 +
  37 + public TbMsgProfilerInfo(UUID msgId) {
  38 + this.msgId = msgId;
  39 + }
  40 +
  41 + public void onStart(RuleNodeId ruleNodeId) {
  42 + long currentTime = System.currentTimeMillis();
  43 + stateLock.lock();
  44 + try {
  45 + currentRuleNodeId = ruleNodeId;
  46 + stateChangeTime = currentTime;
  47 + } finally {
  48 + stateLock.unlock();
  49 + }
  50 + }
  51 +
  52 + public long onEnd(RuleNodeId ruleNodeId) {
  53 + long currentTime = System.currentTimeMillis();
  54 + stateLock.lock();
  55 + try {
  56 + if (ruleNodeId.equals(currentRuleNodeId)) {
  57 + long processingTime = currentTime - stateChangeTime;
  58 + stateChangeTime = currentTime;
  59 + totalProcessingTime.addAndGet(processingTime);
  60 + currentRuleNodeId = null;
  61 + return processingTime;
  62 + } else {
  63 + log.trace("[{}] Invalid sequence of rule node processing detected. Expected [{}] but was [{}]", msgId, currentRuleNodeId, ruleNodeId);
  64 + return 0;
  65 + }
  66 + } finally {
  67 + stateLock.unlock();
  68 + }
  69 + }
  70 +
  71 + public Map.Entry<UUID, Long> onTimeout() {
  72 + long currentTime = System.currentTimeMillis();
  73 + stateLock.lock();
  74 + try {
  75 + if (currentRuleNodeId != null && stateChangeTime > 0) {
  76 + long timeoutTime = currentTime - stateChangeTime;
  77 + totalProcessingTime.addAndGet(timeoutTime);
  78 + return new AbstractMap.SimpleEntry<>(currentRuleNodeId.getId(), timeoutTime);
  79 + }
  80 + } finally {
  81 + stateLock.unlock();
  82 + }
  83 + return null;
  84 + }
  85 +}
  1 +/**
  2 + * Copyright © 2016-2020 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.queue;
  17 +
  18 +import lombok.Getter;
  19 +import org.thingsboard.server.common.msg.queue.RuleNodeInfo;
  20 +
  21 +import java.util.UUID;
  22 +import java.util.concurrent.atomic.AtomicInteger;
  23 +import java.util.concurrent.atomic.AtomicLong;
  24 +
  25 +public class TbRuleNodeProfilerInfo {
  26 + @Getter
  27 + private final UUID ruleNodeId;
  28 + @Getter
  29 + private final String label;
  30 + private AtomicInteger executionCount = new AtomicInteger(0);
  31 + private AtomicLong executionTime = new AtomicLong(0);
  32 + private AtomicLong maxExecutionTime = new AtomicLong(0);
  33 +
  34 + public TbRuleNodeProfilerInfo(RuleNodeInfo ruleNodeInfo) {
  35 + this.ruleNodeId = ruleNodeInfo.getRuleNodeId().getId();
  36 + this.label = ruleNodeInfo.toString();
  37 + }
  38 +
  39 + public TbRuleNodeProfilerInfo(UUID ruleNodeId) {
  40 + this.ruleNodeId = ruleNodeId;
  41 + this.label = "";
  42 + }
  43 +
  44 + public void record(long processingTime) {
  45 + executionCount.incrementAndGet();
  46 + executionTime.addAndGet(processingTime);
  47 + while (true) {
  48 + long value = maxExecutionTime.get();
  49 + if (value >= processingTime) {
  50 + break;
  51 + }
  52 + if (maxExecutionTime.compareAndSet(value, processingTime)) {
  53 + break;
  54 + }
  55 + }
  56 + }
  57 +
  58 + int getExecutionCount() {
  59 + return executionCount.get();
  60 + }
  61 +
  62 + long getMaxExecutionTime() {
  63 + return maxExecutionTime.get();
  64 + }
  65 +
  66 + double getAvgExecutionTime() {
  67 + double executionCnt = (double) executionCount.get();
  68 + if (executionCnt > 0) {
  69 + return executionTime.get() / executionCnt;
  70 + } else {
  71 + return 0.0;
  72 + }
  73 + }
  74 +
  75 +}
@@ -15,23 +15,31 @@ @@ -15,23 +15,31 @@
15 */ 15 */
16 package org.thingsboard.server.service.queue.processing; 16 package org.thingsboard.server.service.queue.processing;
17 17
  18 +import com.google.protobuf.ByteString;
18 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.boot.context.event.ApplicationReadyEvent; 20 import org.springframework.boot.context.event.ApplicationReadyEvent;
20 import org.springframework.context.ApplicationListener; 21 import org.springframework.context.ApplicationListener;
21 import org.springframework.context.event.EventListener; 22 import org.springframework.context.event.EventListener;
22 import org.thingsboard.common.util.ThingsBoardThreadFactory; 23 import org.thingsboard.common.util.ThingsBoardThreadFactory;
23 import org.thingsboard.server.actors.ActorSystemContext; 24 import org.thingsboard.server.actors.ActorSystemContext;
  25 +import org.thingsboard.server.common.data.EntityType;
  26 +import org.thingsboard.server.common.data.id.DeviceId;
  27 +import org.thingsboard.server.common.data.id.DeviceProfileId;
  28 +import org.thingsboard.server.common.msg.TbActorMsg;
  29 +import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
24 import org.thingsboard.server.common.msg.queue.ServiceType; 30 import org.thingsboard.server.common.msg.queue.ServiceType;
25 import org.thingsboard.server.common.msg.queue.TbCallback; 31 import org.thingsboard.server.common.msg.queue.TbCallback;
26 import org.thingsboard.server.queue.TbQueueConsumer; 32 import org.thingsboard.server.queue.TbQueueConsumer;
27 import org.thingsboard.server.queue.common.TbProtoQueueMsg; 33 import org.thingsboard.server.queue.common.TbProtoQueueMsg;
28 import org.thingsboard.server.queue.discovery.PartitionChangeEvent; 34 import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
29 -import org.thingsboard.server.service.encoding.DataDecodingEncodingService; 35 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
  36 +import org.thingsboard.server.service.profile.TbDeviceProfileCache;
30 import org.thingsboard.server.service.queue.TbPackCallback; 37 import org.thingsboard.server.service.queue.TbPackCallback;
31 import org.thingsboard.server.service.queue.TbPackProcessingContext; 38 import org.thingsboard.server.service.queue.TbPackProcessingContext;
32 39
33 import javax.annotation.PreDestroy; 40 import javax.annotation.PreDestroy;
34 import java.util.List; 41 import java.util.List;
  42 +import java.util.Optional;
35 import java.util.UUID; 43 import java.util.UUID;
36 import java.util.concurrent.ConcurrentHashMap; 44 import java.util.concurrent.ConcurrentHashMap;
37 import java.util.concurrent.ConcurrentMap; 45 import java.util.concurrent.ConcurrentMap;
@@ -51,12 +59,15 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene @@ -51,12 +59,15 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
51 59
52 protected final ActorSystemContext actorContext; 60 protected final ActorSystemContext actorContext;
53 protected final DataDecodingEncodingService encodingService; 61 protected final DataDecodingEncodingService encodingService;
  62 + protected final TbDeviceProfileCache deviceProfileCache;
54 63
55 protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer; 64 protected final TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer;
56 65
57 - public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) { 66 + public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService,
  67 + TbDeviceProfileCache deviceProfileCache, TbQueueConsumer<TbProtoQueueMsg<N>> nfConsumer) {
58 this.actorContext = actorContext; 68 this.actorContext = actorContext;
59 this.encodingService = encodingService; 69 this.encodingService = encodingService;
  70 + this.deviceProfileCache = deviceProfileCache;
60 this.nfConsumer = nfConsumer; 71 this.nfConsumer = nfConsumer;
61 } 72 }
62 73
@@ -126,18 +137,32 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene @@ -126,18 +137,32 @@ public abstract class AbstractConsumerService<N extends com.google.protobuf.Gene
126 }); 137 });
127 } 138 }
128 139
  140 + protected void handleComponentLifecycleMsg(UUID id, ByteString nfMsg) {
  141 + Optional<TbActorMsg> actorMsgOpt = encodingService.decode(nfMsg.toByteArray());
  142 + if (actorMsgOpt.isPresent()) {
  143 + TbActorMsg actorMsg = actorMsgOpt.get();
  144 + if (actorMsg instanceof ComponentLifecycleMsg) {
  145 + ComponentLifecycleMsg componentLifecycleMsg = (ComponentLifecycleMsg) actorMsg;
  146 + if (EntityType.DEVICE_PROFILE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
  147 + deviceProfileCache.evict(new DeviceProfileId(componentLifecycleMsg.getEntityId().getId()));
  148 + } else if (EntityType.DEVICE.equals(componentLifecycleMsg.getEntityId().getEntityType())) {
  149 + deviceProfileCache.evict(new DeviceId(componentLifecycleMsg.getEntityId().getId()));
  150 + }
  151 + }
  152 + log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg);
  153 + actorContext.tellWithHighPriority(actorMsg);
  154 + }
  155 + }
  156 +
129 protected abstract void handleNotification(UUID id, TbProtoQueueMsg<N> msg, TbCallback callback) throws Exception; 157 protected abstract void handleNotification(UUID id, TbProtoQueueMsg<N> msg, TbCallback callback) throws Exception;
130 158
131 @PreDestroy 159 @PreDestroy
132 public void destroy() { 160 public void destroy() {
133 stopped = true; 161 stopped = true;
134 -  
135 stopMainConsumers(); 162 stopMainConsumers();
136 -  
137 if (nfConsumer != null) { 163 if (nfConsumer != null) {
138 nfConsumer.unsubscribe(); 164 nfConsumer.unsubscribe();
139 } 165 }
140 -  
141 if (consumersExecutor != null) { 166 if (consumersExecutor != null) {
142 consumersExecutor.shutdownNow(); 167 consumersExecutor.shutdownNow();
143 } 168 }
@@ -68,18 +68,20 @@ public class BatchTbRuleEngineSubmitStrategy extends AbstractTbRuleEngineSubmitS @@ -68,18 +68,20 @@ public class BatchTbRuleEngineSubmitStrategy extends AbstractTbRuleEngineSubmitS
68 int listSize = orderedMsgList.size(); 68 int listSize = orderedMsgList.size();
69 int startIdx = Math.min(packIdx.get() * batchSize, listSize); 69 int startIdx = Math.min(packIdx.get() * batchSize, listSize);
70 int endIdx = Math.min(startIdx + batchSize, listSize); 70 int endIdx = Math.min(startIdx + batchSize, listSize);
  71 + Map<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> tmpPack;
71 synchronized (pendingPack) { 72 synchronized (pendingPack) {
72 pendingPack.clear(); 73 pendingPack.clear();
73 for (int i = startIdx; i < endIdx; i++) { 74 for (int i = startIdx; i < endIdx; i++) {
74 IdMsgPair pair = orderedMsgList.get(i); 75 IdMsgPair pair = orderedMsgList.get(i);
75 pendingPack.put(pair.uuid, pair.msg); 76 pendingPack.put(pair.uuid, pair.msg);
76 } 77 }
  78 + tmpPack = new LinkedHashMap<>(pendingPack);
77 } 79 }
78 int submitSize = pendingPack.size(); 80 int submitSize = pendingPack.size();
79 if (log.isDebugEnabled() && submitSize > 0) { 81 if (log.isDebugEnabled() && submitSize > 0) {
80 log.debug("[{}] submitting [{}] messages to rule engine", queueName, submitSize); 82 log.debug("[{}] submitting [{}] messages to rule engine", queueName, submitSize);
81 } 83 }
82 - pendingPack.forEach(msgConsumer); 84 + tmpPack.forEach(msgConsumer);
83 } 85 }
84 86
85 } 87 }
@@ -56,7 +56,9 @@ public class TbRuleEngineProcessingStrategyFactory { @@ -56,7 +56,9 @@ public class TbRuleEngineProcessingStrategyFactory {
56 private final boolean retryTimeout; 56 private final boolean retryTimeout;
57 private final int maxRetries; 57 private final int maxRetries;
58 private final double maxAllowedFailurePercentage; 58 private final double maxAllowedFailurePercentage;
59 - private final long pauseBetweenRetries; 59 + private final long maxPauseBetweenRetries;
  60 +
  61 + private long pauseBetweenRetries;
60 62
61 private int initialTotalCount; 63 private int initialTotalCount;
62 private int retryCount; 64 private int retryCount;
@@ -69,6 +71,7 @@ public class TbRuleEngineProcessingStrategyFactory { @@ -69,6 +71,7 @@ public class TbRuleEngineProcessingStrategyFactory {
69 this.maxRetries = configuration.getRetries(); 71 this.maxRetries = configuration.getRetries();
70 this.maxAllowedFailurePercentage = configuration.getFailurePercentage(); 72 this.maxAllowedFailurePercentage = configuration.getFailurePercentage();
71 this.pauseBetweenRetries = configuration.getPauseBetweenRetries(); 73 this.pauseBetweenRetries = configuration.getPauseBetweenRetries();
  74 + this.maxPauseBetweenRetries = configuration.getMaxPauseBetweenRetries();
72 } 75 }
73 76
74 @Override 77 @Override
@@ -108,6 +111,9 @@ public class TbRuleEngineProcessingStrategyFactory { @@ -108,6 +111,9 @@ public class TbRuleEngineProcessingStrategyFactory {
108 } catch (InterruptedException e) { 111 } catch (InterruptedException e) {
109 throw new RuntimeException(e); 112 throw new RuntimeException(e);
110 } 113 }
  114 + if (maxPauseBetweenRetries > pauseBetweenRetries) {
  115 + pauseBetweenRetries = Math.min(maxPauseBetweenRetries, pauseBetweenRetries * 2);
  116 + }
111 } 117 }
112 return new TbRuleEngineProcessingDecision(false, toReprocess); 118 return new TbRuleEngineProcessingDecision(false, toReprocess);
113 } 119 }
@@ -27,6 +27,7 @@ import org.springframework.web.context.request.async.DeferredResult; @@ -27,6 +27,7 @@ import org.springframework.web.context.request.async.DeferredResult;
27 import org.thingsboard.common.util.ThingsBoardThreadFactory; 27 import org.thingsboard.common.util.ThingsBoardThreadFactory;
28 import org.thingsboard.server.common.data.Customer; 28 import org.thingsboard.server.common.data.Customer;
29 import org.thingsboard.server.common.data.Device; 29 import org.thingsboard.server.common.data.Device;
  30 +import org.thingsboard.server.common.data.DeviceProfile;
30 import org.thingsboard.server.common.data.EntityView; 31 import org.thingsboard.server.common.data.EntityView;
31 import org.thingsboard.server.common.data.Tenant; 32 import org.thingsboard.server.common.data.Tenant;
32 import org.thingsboard.server.common.data.User; 33 import org.thingsboard.server.common.data.User;
@@ -35,6 +36,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -35,6 +36,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
35 import org.thingsboard.server.common.data.id.AssetId; 36 import org.thingsboard.server.common.data.id.AssetId;
36 import org.thingsboard.server.common.data.id.CustomerId; 37 import org.thingsboard.server.common.data.id.CustomerId;
37 import org.thingsboard.server.common.data.id.DeviceId; 38 import org.thingsboard.server.common.data.id.DeviceId;
  39 +import org.thingsboard.server.common.data.id.DeviceProfileId;
38 import org.thingsboard.server.common.data.id.EntityId; 40 import org.thingsboard.server.common.data.id.EntityId;
39 import org.thingsboard.server.common.data.id.EntityIdFactory; 41 import org.thingsboard.server.common.data.id.EntityIdFactory;
40 import org.thingsboard.server.common.data.id.EntityViewId; 42 import org.thingsboard.server.common.data.id.EntityViewId;
@@ -48,6 +50,7 @@ import org.thingsboard.server.controller.HttpValidationCallback; @@ -48,6 +50,7 @@ import org.thingsboard.server.controller.HttpValidationCallback;
48 import org.thingsboard.server.dao.alarm.AlarmService; 50 import org.thingsboard.server.dao.alarm.AlarmService;
49 import org.thingsboard.server.dao.asset.AssetService; 51 import org.thingsboard.server.dao.asset.AssetService;
50 import org.thingsboard.server.dao.customer.CustomerService; 52 import org.thingsboard.server.dao.customer.CustomerService;
  53 +import org.thingsboard.server.dao.device.DeviceProfileService;
51 import org.thingsboard.server.dao.device.DeviceService; 54 import org.thingsboard.server.dao.device.DeviceService;
52 import org.thingsboard.server.dao.entityview.EntityViewService; 55 import org.thingsboard.server.dao.entityview.EntityViewService;
53 import org.thingsboard.server.dao.rule.RuleChainService; 56 import org.thingsboard.server.dao.rule.RuleChainService;
@@ -72,6 +75,7 @@ import java.util.function.BiConsumer; @@ -72,6 +75,7 @@ import java.util.function.BiConsumer;
72 @Component 75 @Component
73 public class AccessValidator { 76 public class AccessValidator {
74 77
  78 + public static final String ONLY_SYSTEM_ADMINISTRATOR_IS_ALLOWED_TO_PERFORM_THIS_OPERATION = "Only system administrator is allowed to perform this operation!";
75 public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!"; 79 public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!";
76 public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; 80 public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!";
77 public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!"; 81 public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!";
@@ -90,6 +94,9 @@ public class AccessValidator { @@ -90,6 +94,9 @@ public class AccessValidator {
90 protected DeviceService deviceService; 94 protected DeviceService deviceService;
91 95
92 @Autowired 96 @Autowired
  97 + protected DeviceProfileService deviceProfileService;
  98 +
  99 + @Autowired
93 protected AssetService assetService; 100 protected AssetService assetService;
94 101
95 @Autowired 102 @Autowired
@@ -162,6 +169,9 @@ public class AccessValidator { @@ -162,6 +169,9 @@ public class AccessValidator {
162 case DEVICE: 169 case DEVICE:
163 validateDevice(currentUser, operation, entityId, callback); 170 validateDevice(currentUser, operation, entityId, callback);
164 return; 171 return;
  172 + case DEVICE_PROFILE:
  173 + validateDeviceProfile(currentUser, operation, entityId, callback);
  174 + return;
165 case ASSET: 175 case ASSET:
166 validateAsset(currentUser, operation, entityId, callback); 176 validateAsset(currentUser, operation, entityId, callback);
167 return; 177 return;
@@ -174,6 +184,9 @@ public class AccessValidator { @@ -174,6 +184,9 @@ public class AccessValidator {
174 case TENANT: 184 case TENANT:
175 validateTenant(currentUser, operation, entityId, callback); 185 validateTenant(currentUser, operation, entityId, callback);
176 return; 186 return;
  187 + case TENANT_PROFILE:
  188 + validateTenantProfile(currentUser, operation, entityId, callback);
  189 + return;
177 case USER: 190 case USER:
178 validateUser(currentUser, operation, entityId, callback); 191 validateUser(currentUser, operation, entityId, callback);
179 return; 192 return;
@@ -206,6 +219,24 @@ public class AccessValidator { @@ -206,6 +219,24 @@ public class AccessValidator {
206 } 219 }
207 } 220 }
208 221
  222 + private void validateDeviceProfile(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
  223 + if (currentUser.isSystemAdmin()) {
  224 + callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
  225 + } else {
  226 + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(currentUser.getTenantId(), new DeviceProfileId(entityId.getId()));
  227 + if (deviceProfile == null) {
  228 + callback.onSuccess(ValidationResult.entityNotFound("Device profile with requested id wasn't found!"));
  229 + } else {
  230 + try {
  231 + accessControlService.checkPermission(currentUser, Resource.DEVICE_PROFILE, operation, entityId, deviceProfile);
  232 + } catch (ThingsboardException e) {
  233 + callback.onSuccess(ValidationResult.accessDenied(e.getMessage()));
  234 + }
  235 + callback.onSuccess(ValidationResult.ok(deviceProfile));
  236 + }
  237 + }
  238 + }
  239 +
209 private void validateAsset(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { 240 private void validateAsset(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
210 if (currentUser.isSystemAdmin()) { 241 if (currentUser.isSystemAdmin()) {
211 callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); 242 callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
@@ -313,6 +344,14 @@ public class AccessValidator { @@ -313,6 +344,14 @@ public class AccessValidator {
313 } 344 }
314 } 345 }
315 346
  347 + private void validateTenantProfile(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
  348 + if (currentUser.isSystemAdmin()) {
  349 + callback.onSuccess(ValidationResult.ok(null));
  350 + } else {
  351 + callback.onSuccess(ValidationResult.accessDenied(ONLY_SYSTEM_ADMINISTRATOR_IS_ALLOWED_TO_PERFORM_THIS_OPERATION));
  352 + }
  353 + }
  354 +
316 private void validateUser(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) { 355 private void validateUser(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback<ValidationResult> callback) {
317 ListenableFuture<User> userFuture = userService.findUserByIdAsync(currentUser.getTenantId(), new UserId(entityId.getId())); 356 ListenableFuture<User> userFuture = userService.findUserByIdAsync(currentUser.getTenantId(), new UserId(entityId.getId()));
318 Futures.addCallback(userFuture, getCallback(callback, user -> { 357 Futures.addCallback(userFuture, getCallback(callback, user -> {
@@ -34,7 +34,9 @@ public enum Resource { @@ -34,7 +34,9 @@ public enum Resource {
34 WIDGET_TYPE(EntityType.WIDGET_TYPE), 34 WIDGET_TYPE(EntityType.WIDGET_TYPE),
35 OAUTH2_CONFIGURATION_INFO(EntityType.OAUTH2_CLIENT_REGISTRATION_INFO), 35 OAUTH2_CONFIGURATION_INFO(EntityType.OAUTH2_CLIENT_REGISTRATION_INFO),
36 OAUTH2_CONFIGURATION_TEMPLATE(EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE), 36 OAUTH2_CONFIGURATION_TEMPLATE(EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE),
37 - ; 37 + WIDGET_TYPE(EntityType.WIDGET_TYPE),
  38 + TENANT_PROFILE(EntityType.TENANT_PROFILE),
  39 + DEVICE_PROFILE(EntityType.DEVICE_PROFILE);
38 40
39 private final EntityType entityType; 41 private final EntityType entityType;
40 42
@@ -37,6 +37,7 @@ public class SysAdminPermissions extends AbstractPermissions { @@ -37,6 +37,7 @@ public class SysAdminPermissions extends AbstractPermissions {
37 put(Resource.WIDGET_TYPE, systemEntityPermissionChecker); 37 put(Resource.WIDGET_TYPE, systemEntityPermissionChecker);
38 put(Resource.OAUTH2_CONFIGURATION_INFO, PermissionChecker.allowAllPermissionChecker); 38 put(Resource.OAUTH2_CONFIGURATION_INFO, PermissionChecker.allowAllPermissionChecker);
39 put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker); 39 put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker);
  40 + put(Resource.TENANT_PROFILE, PermissionChecker.allowAllPermissionChecker);
40 } 41 }
41 42
42 private static final PermissionChecker systemEntityPermissionChecker = new PermissionChecker() { 43 private static final PermissionChecker systemEntityPermissionChecker = new PermissionChecker() {
@@ -39,6 +39,7 @@ public class TenantAdminPermissions extends AbstractPermissions { @@ -39,6 +39,7 @@ public class TenantAdminPermissions extends AbstractPermissions {
39 put(Resource.USER, userPermissionChecker); 39 put(Resource.USER, userPermissionChecker);
40 put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); 40 put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker);
41 put(Resource.WIDGET_TYPE, widgetsPermissionChecker); 41 put(Resource.WIDGET_TYPE, widgetsPermissionChecker);
  42 + put(Resource.DEVICE_PROFILE, tenantEntityPermissionChecker);
42 } 43 }
43 44
44 public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { 45 public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() {
@@ -226,6 +226,11 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer @@ -226,6 +226,11 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer
226 226
227 @Override 227 @Override
228 public void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, TbCallback callback) { 228 public void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, TbCallback callback) {
  229 + onAttributesUpdate(tenantId, entityId, scope, attributes, true, callback);
  230 + }
  231 +
  232 + @Override
  233 + public void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, TbCallback callback) {
229 onLocalTelemetrySubUpdate(entityId, 234 onLocalTelemetrySubUpdate(entityId,
230 s -> { 235 s -> {
231 if (TbSubscriptionType.ATTRIBUTES.equals(s.getType())) { 236 if (TbSubscriptionType.ATTRIBUTES.equals(s.getType())) {
@@ -254,7 +259,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer @@ -254,7 +259,7 @@ public class DefaultSubscriptionManagerService implements SubscriptionManagerSer
254 deviceStateService.onDeviceInactivityTimeoutUpdate(new DeviceId(entityId.getId()), attribute.getLongValue().orElse(0L)); 259 deviceStateService.onDeviceInactivityTimeoutUpdate(new DeviceId(entityId.getId()), attribute.getLongValue().orElse(0L));
255 } 260 }
256 } 261 }
257 - } else if (TbAttributeSubscriptionScope.SHARED_SCOPE.name().equalsIgnoreCase(scope)) { 262 + } else if (TbAttributeSubscriptionScope.SHARED_SCOPE.name().equalsIgnoreCase(scope) && notifyDevice) {
258 clusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onUpdate(tenantId, 263 clusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onUpdate(tenantId,
259 new DeviceId(entityId.getId()), DataConstants.SHARED_SCOPE, new ArrayList<>(attributes)) 264 new DeviceId(entityId.getId()), DataConstants.SHARED_SCOPE, new ArrayList<>(attributes))
260 , null); 265 , null);
@@ -17,13 +17,12 @@ package org.thingsboard.server.service.subscription; @@ -17,13 +17,12 @@ package org.thingsboard.server.service.subscription;
17 17
18 import org.springframework.context.ApplicationListener; 18 import org.springframework.context.ApplicationListener;
19 import org.thingsboard.server.common.data.alarm.Alarm; 19 import org.thingsboard.server.common.data.alarm.Alarm;
20 -import org.thingsboard.server.common.data.id.AlarmId;  
21 import org.thingsboard.server.common.data.id.EntityId; 20 import org.thingsboard.server.common.data.id.EntityId;
22 import org.thingsboard.server.common.data.id.TenantId; 21 import org.thingsboard.server.common.data.id.TenantId;
23 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 22 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
24 import org.thingsboard.server.common.data.kv.TsKvEntry; 23 import org.thingsboard.server.common.data.kv.TsKvEntry;
25 -import org.thingsboard.server.queue.discovery.PartitionChangeEvent;  
26 import org.thingsboard.server.common.msg.queue.TbCallback; 24 import org.thingsboard.server.common.msg.queue.TbCallback;
  25 +import org.thingsboard.server.queue.discovery.PartitionChangeEvent;
27 26
28 import java.util.List; 27 import java.util.List;
29 28
@@ -37,9 +36,13 @@ public interface SubscriptionManagerService extends ApplicationListener<Partitio @@ -37,9 +36,13 @@ public interface SubscriptionManagerService extends ApplicationListener<Partitio
37 36
38 void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, TbCallback callback); 37 void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, TbCallback callback);
39 38
  39 + void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, TbCallback callback);
  40 +
40 void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List<String> keys, TbCallback empty); 41 void onAttributesDelete(TenantId tenantId, EntityId entityId, String scope, List<String> keys, TbCallback empty);
41 42
42 void onAlarmUpdate(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback); 43 void onAlarmUpdate(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback);
43 44
44 void onAlarmDeleted(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback); 45 void onAlarmDeleted(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback);
  46 +
  47 +
45 } 48 }
@@ -171,9 +171,14 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer @@ -171,9 +171,14 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
171 171
172 @Override 172 @Override
173 public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback) { 173 public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback) {
  174 + saveAndNotify(tenantId, entityId, scope, attributes, true, callback);
  175 + }
  176 +
  177 + @Override
  178 + public void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, FutureCallback<Void> callback) {
174 ListenableFuture<List<Void>> saveFuture = attrService.save(tenantId, entityId, scope, attributes); 179 ListenableFuture<List<Void>> saveFuture = attrService.save(tenantId, entityId, scope, attributes);
175 addMainCallback(saveFuture, callback); 180 addMainCallback(saveFuture, callback);
176 - addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope, attributes)); 181 + addWsCallback(saveFuture, success -> onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice));
177 } 182 }
178 183
179 @Override 184 @Override
@@ -236,11 +241,11 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer @@ -236,11 +241,11 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
236 , System.currentTimeMillis())), callback); 241 , System.currentTimeMillis())), callback);
237 } 242 }
238 243
239 - private void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes) { 244 + private void onAttributesUpdate(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice) {
240 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); 245 TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId);
241 if (currentPartitions.contains(tpi)) { 246 if (currentPartitions.contains(tpi)) {
242 if (subscriptionManagerService.isPresent()) { 247 if (subscriptionManagerService.isPresent()) {
243 - subscriptionManagerService.get().onAttributesUpdate(tenantId, entityId, scope, attributes, TbCallback.EMPTY); 248 + subscriptionManagerService.get().onAttributesUpdate(tenantId, entityId, scope, attributes, notifyDevice, TbCallback.EMPTY);
244 } else { 249 } else {
245 log.warn("Possible misconfiguration because subscriptionManagerService is null!"); 250 log.warn("Possible misconfiguration because subscriptionManagerService is null!");
246 } 251 }
@@ -21,26 +21,36 @@ import com.fasterxml.jackson.databind.node.ObjectNode; @@ -21,26 +21,36 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
21 import com.google.common.util.concurrent.Futures; 21 import com.google.common.util.concurrent.Futures;
22 import com.google.common.util.concurrent.ListenableFuture; 22 import com.google.common.util.concurrent.ListenableFuture;
23 import com.google.common.util.concurrent.MoreExecutors; 23 import com.google.common.util.concurrent.MoreExecutors;
  24 +import com.google.protobuf.ByteString;
24 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
25 -import org.springframework.beans.factory.annotation.Autowired; 26 +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
26 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
27 import org.springframework.util.StringUtils; 28 import org.springframework.util.StringUtils;
28 import org.thingsboard.server.common.data.DataConstants; 29 import org.thingsboard.server.common.data.DataConstants;
29 import org.thingsboard.server.common.data.Device; 30 import org.thingsboard.server.common.data.Device;
30 -import org.thingsboard.server.common.data.Tenant; 31 +import org.thingsboard.server.common.data.DeviceProfile;
  32 +import org.thingsboard.server.common.data.TenantProfile;
  33 +import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
31 import org.thingsboard.server.common.data.id.CustomerId; 34 import org.thingsboard.server.common.data.id.CustomerId;
32 import org.thingsboard.server.common.data.id.DeviceId; 35 import org.thingsboard.server.common.data.id.DeviceId;
  36 +import org.thingsboard.server.common.data.id.DeviceProfileId;
33 import org.thingsboard.server.common.data.id.TenantId; 37 import org.thingsboard.server.common.data.id.TenantId;
34 import org.thingsboard.server.common.data.relation.EntityRelation; 38 import org.thingsboard.server.common.data.relation.EntityRelation;
35 import org.thingsboard.server.common.data.security.DeviceCredentials; 39 import org.thingsboard.server.common.data.security.DeviceCredentials;
36 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 40 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
  41 +import org.thingsboard.server.common.msg.EncryptionUtil;
37 import org.thingsboard.server.common.msg.TbMsg; 42 import org.thingsboard.server.common.msg.TbMsg;
38 import org.thingsboard.server.common.msg.TbMsgDataType; 43 import org.thingsboard.server.common.msg.TbMsgDataType;
39 import org.thingsboard.server.common.msg.TbMsgMetaData; 44 import org.thingsboard.server.common.msg.TbMsgMetaData;
  45 +import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
40 import org.thingsboard.server.dao.device.DeviceCredentialsService; 46 import org.thingsboard.server.dao.device.DeviceCredentialsService;
  47 +import org.thingsboard.server.dao.device.DeviceProfileService;
41 import org.thingsboard.server.dao.device.DeviceService; 48 import org.thingsboard.server.dao.device.DeviceService;
42 import org.thingsboard.server.dao.relation.RelationService; 49 import org.thingsboard.server.dao.relation.RelationService;
  50 +import org.thingsboard.server.dao.tenant.TenantProfileService;
43 import org.thingsboard.server.dao.tenant.TenantService; 51 import org.thingsboard.server.dao.tenant.TenantService;
  52 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
  53 +import org.thingsboard.server.gen.transport.TransportProtos;
44 import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; 54 import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto;
45 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; 55 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg;
46 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; 56 import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg;
@@ -74,44 +84,64 @@ public class DefaultTransportApiService implements TransportApiService { @@ -74,44 +84,64 @@ public class DefaultTransportApiService implements TransportApiService {
74 private static final ObjectMapper mapper = new ObjectMapper(); 84 private static final ObjectMapper mapper = new ObjectMapper();
75 85
76 //TODO: Constructor dependencies; 86 //TODO: Constructor dependencies;
77 - @Autowired  
78 - private TenantService tenantService; 87 + private final DeviceProfileService deviceProfileService;
  88 + private final TenantService tenantService;
  89 + private final TenantProfileService tenantProfileService;
  90 + private final DeviceService deviceService;
  91 + private final RelationService relationService;
  92 + private final DeviceCredentialsService deviceCredentialsService;
  93 + private final DeviceStateService deviceStateService;
  94 + private final DbCallbackExecutorService dbCallbackExecutorService;
  95 + private final TbClusterService tbClusterService;
  96 + private final DataDecodingEncodingService dataDecodingEncodingService;
79 97
80 - @Autowired  
81 - private DeviceService deviceService;  
82 -  
83 - @Autowired  
84 - private RelationService relationService;  
85 -  
86 - @Autowired  
87 - private DeviceCredentialsService deviceCredentialsService;  
88 -  
89 - @Autowired  
90 - private DeviceStateService deviceStateService;  
91 -  
92 - @Autowired  
93 - private DbCallbackExecutorService dbCallbackExecutorService;  
94 -  
95 - @Autowired  
96 - protected TbClusterService tbClusterService;  
97 98
98 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); 99 private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
99 100
  101 + public DefaultTransportApiService(DeviceProfileService deviceProfileService, TenantService tenantService,
  102 + TenantProfileService tenantProfileService, DeviceService deviceService,
  103 + RelationService relationService, DeviceCredentialsService deviceCredentialsService,
  104 + DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService,
  105 + TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService) {
  106 + this.deviceProfileService = deviceProfileService;
  107 + this.tenantService = tenantService;
  108 + this.tenantProfileService = tenantProfileService;
  109 + this.deviceService = deviceService;
  110 + this.relationService = relationService;
  111 + this.deviceCredentialsService = deviceCredentialsService;
  112 + this.deviceStateService = deviceStateService;
  113 + this.dbCallbackExecutorService = dbCallbackExecutorService;
  114 + this.tbClusterService = tbClusterService;
  115 + this.dataDecodingEncodingService = dataDecodingEncodingService;
  116 + }
  117 +
100 @Override 118 @Override
101 public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) { 119 public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) {
102 TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue(); 120 TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue();
103 if (transportApiRequestMsg.hasValidateTokenRequestMsg()) { 121 if (transportApiRequestMsg.hasValidateTokenRequestMsg()) {
104 ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg(); 122 ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg();
105 - return Futures.transform(validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN), value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 123 + return Futures.transform(validateCredentials(msg.getToken(), DeviceCredentialsType.ACCESS_TOKEN),
  124 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  125 + } else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) {
  126 + TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg();
  127 + return Futures.transform(validateCredentials(msg),
  128 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
106 } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) { 129 } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) {
107 ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg(); 130 ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg();
108 - return Futures.transform(validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE), value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 131 + return Futures.transform(validateCredentials(msg.getHash(), DeviceCredentialsType.X509_CERTIFICATE),
  132 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
109 } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) { 133 } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) {
110 - return Futures.transform(handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()), value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 134 + return Futures.transform(handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()),
  135 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
111 } else if (transportApiRequestMsg.hasGetTenantRoutingInfoRequestMsg()) { 136 } else if (transportApiRequestMsg.hasGetTenantRoutingInfoRequestMsg()) {
112 - return Futures.transform(handle(transportApiRequestMsg.getGetTenantRoutingInfoRequestMsg()), value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 137 + return Futures.transform(handle(transportApiRequestMsg.getGetTenantRoutingInfoRequestMsg()),
  138 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
  139 + } else if (transportApiRequestMsg.hasGetDeviceProfileRequestMsg()) {
  140 + return Futures.transform(handle(transportApiRequestMsg.getGetDeviceProfileRequestMsg()),
  141 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
113 } 142 }
114 - return Futures.transform(getEmptyTransportApiResponseFuture(), value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); 143 + return Futures.transform(getEmptyTransportApiResponseFuture(),
  144 + value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor());
115 } 145 }
116 146
117 private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) { 147 private ListenableFuture<TransportApiResponseMsg> validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) {
@@ -124,6 +154,62 @@ public class DefaultTransportApiService implements TransportApiService { @@ -124,6 +154,62 @@ public class DefaultTransportApiService implements TransportApiService {
124 } 154 }
125 } 155 }
126 156
  157 + private ListenableFuture<TransportApiResponseMsg> validateCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg mqtt) {
  158 + DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(mqtt.getUserName());
  159 + if (credentials != null) {
  160 + if (credentials.getCredentialsType() == DeviceCredentialsType.ACCESS_TOKEN) {
  161 + return getDeviceInfo(credentials.getDeviceId(), credentials);
  162 + } else if (credentials.getCredentialsType() == DeviceCredentialsType.MQTT_BASIC) {
  163 + if (!checkMqttCredentials(mqtt, credentials)) {
  164 + credentials = null;
  165 + }
  166 + }
  167 + }
  168 + if (credentials == null) {
  169 + credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash("|", mqtt.getClientId(), mqtt.getUserName()));
  170 + if (credentials == null) {
  171 + credentials = checkMqttCredentials(mqtt, EncryptionUtil.getSha3Hash(mqtt.getClientId()));
  172 + }
  173 + }
  174 + if (credentials != null) {
  175 + return getDeviceInfo(credentials.getDeviceId(), credentials);
  176 + } else {
  177 + return getEmptyTransportApiResponseFuture();
  178 + }
  179 + }
  180 +
  181 + private DeviceCredentials checkMqttCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg clientCred, String credId) {
  182 + DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByCredentialsId(credId);
  183 + if (deviceCredentials != null && deviceCredentials.getCredentialsType() == DeviceCredentialsType.MQTT_BASIC) {
  184 + if (!checkMqttCredentials(clientCred, deviceCredentials)) {
  185 + return null;
  186 + } else {
  187 + return deviceCredentials;
  188 + }
  189 + }
  190 + return null;
  191 + }
  192 +
  193 + private boolean checkMqttCredentials(TransportProtos.ValidateBasicMqttCredRequestMsg clientCred, DeviceCredentials deviceCredentials) {
  194 + BasicMqttCredentials dbCred = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), BasicMqttCredentials.class);
  195 + if (!StringUtils.isEmpty(dbCred.getClientId()) && !dbCred.getClientId().equals(clientCred.getClientId())) {
  196 + return false;
  197 + }
  198 + if (!StringUtils.isEmpty(dbCred.getUserName()) && !dbCred.getUserName().equals(clientCred.getUserName())) {
  199 + return false;
  200 + }
  201 + if (!StringUtils.isEmpty(dbCred.getPassword())) {
  202 + if (StringUtils.isEmpty(clientCred.getPassword())) {
  203 + return false;
  204 + } else {
  205 + if (!dbCred.getPassword().equals(clientCred.getPassword())) {
  206 + return false;
  207 + }
  208 + }
  209 + }
  210 + return true;
  211 + }
  212 +
127 private ListenableFuture<TransportApiResponseMsg> handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) { 213 private ListenableFuture<TransportApiResponseMsg> handle(GetOrCreateDeviceFromGatewayRequestMsg requestMsg) {
128 DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB())); 214 DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB()));
129 ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId); 215 ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId);
@@ -139,6 +225,8 @@ public class DefaultTransportApiService implements TransportApiService { @@ -139,6 +225,8 @@ public class DefaultTransportApiService implements TransportApiService {
139 device.setName(requestMsg.getDeviceName()); 225 device.setName(requestMsg.getDeviceName());
140 device.setType(requestMsg.getDeviceType()); 226 device.setType(requestMsg.getDeviceType());
141 device.setCustomerId(gateway.getCustomerId()); 227 device.setCustomerId(gateway.getCustomerId());
  228 + DeviceProfile deviceProfile = deviceProfileService.findOrCreateDeviceProfile(gateway.getTenantId(), requestMsg.getDeviceType());
  229 + device.setDeviceProfileId(deviceProfile.getId());
142 device = deviceService.saveDevice(device); 230 device = deviceService.saveDevice(device);
143 relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created")); 231 relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, new EntityRelation(gateway.getId(), device.getId(), "Created"));
144 deviceStateService.onDeviceAdded(device); 232 deviceStateService.onDeviceAdded(device);
@@ -155,10 +243,19 @@ public class DefaultTransportApiService implements TransportApiService { @@ -155,10 +243,19 @@ public class DefaultTransportApiService implements TransportApiService {
155 TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, mapper.writeValueAsString(entityNode)); 243 TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, metaData, TbMsgDataType.JSON, mapper.writeValueAsString(entityNode));
156 tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null); 244 tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, null);
157 } 245 }
  246 + GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder()
  247 + .setDeviceInfo(getDeviceInfoProto(device));
  248 + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
  249 + if (deviceProfile != null) {
  250 + builder.setProfileBody(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile)));
  251 + } else {
  252 + log.warn("[{}] Failed to find device profile [{}] for device. ", device.getId(), device.getDeviceProfileId());
  253 + }
158 return TransportApiResponseMsg.newBuilder() 254 return TransportApiResponseMsg.newBuilder()
159 - .setGetOrCreateDeviceResponseMsg(GetOrCreateDeviceFromGatewayResponseMsg.newBuilder().setDeviceInfo(getDeviceInfoProto(device)).build()).build(); 255 + .setGetOrCreateDeviceResponseMsg(builder.build())
  256 + .build();
160 } catch (JsonProcessingException e) { 257 } catch (JsonProcessingException e) {
161 - log.warn("[{}] Failed to lookup device by gateway id and name", gatewayId, requestMsg.getDeviceName(), e); 258 + log.warn("[{}] Failed to lookup device by gateway id and name: [{}]", gatewayId, requestMsg.getDeviceName(), e);
162 throw new RuntimeException(e); 259 throw new RuntimeException(e);
163 } finally { 260 } finally {
164 deviceCreationLock.unlock(); 261 deviceCreationLock.unlock();
@@ -168,10 +265,23 @@ public class DefaultTransportApiService implements TransportApiService { @@ -168,10 +265,23 @@ public class DefaultTransportApiService implements TransportApiService {
168 265
169 private ListenableFuture<TransportApiResponseMsg> handle(GetTenantRoutingInfoRequestMsg requestMsg) { 266 private ListenableFuture<TransportApiResponseMsg> handle(GetTenantRoutingInfoRequestMsg requestMsg) {
170 TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); 267 TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()));
171 - ListenableFuture<Tenant> tenantFuture = tenantService.findTenantByIdAsync(TenantId.SYS_TENANT_ID, tenantId);  
172 - return Futures.transform(tenantFuture, tenant -> TransportApiResponseMsg.newBuilder()  
173 - .setGetTenantRoutingInfoResponseMsg(GetTenantRoutingInfoResponseMsg.newBuilder().setIsolatedTbCore(tenant.isIsolatedTbCore())  
174 - .setIsolatedTbRuleEngine(tenant.isIsolatedTbRuleEngine()).build()).build(), dbCallbackExecutorService); 268 + // TODO: Tenant Profile from cache
  269 + ListenableFuture<TenantProfile> tenantProfileFuture =
  270 + Futures.transform(tenantService.findTenantByIdAsync(TenantId.SYS_TENANT_ID, tenantId), tenant ->
  271 + tenantProfileService.findTenantProfileById(TenantId.SYS_TENANT_ID, tenant.getTenantProfileId()), dbCallbackExecutorService);
  272 + return Futures.transform(tenantProfileFuture, tenantProfile -> TransportApiResponseMsg.newBuilder()
  273 + .setGetTenantRoutingInfoResponseMsg(GetTenantRoutingInfoResponseMsg.newBuilder().setIsolatedTbCore(tenantProfile.isIsolatedTbCore())
  274 + .setIsolatedTbRuleEngine(tenantProfile.isIsolatedTbRuleEngine()).build()).build(), dbCallbackExecutorService);
  275 + }
  276 +
  277 + private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetDeviceProfileRequestMsg requestMsg) {
  278 + DeviceProfileId profileId = new DeviceProfileId(new UUID(requestMsg.getProfileIdMSB(), requestMsg.getProfileIdLSB()));
  279 + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(TenantId.SYS_TENANT_ID, profileId);
  280 + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder()
  281 + .setGetDeviceProfileResponseMsg(
  282 + TransportProtos.GetDeviceProfileResponseMsg.newBuilder()
  283 + .setData(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile)))
  284 + .build()).build());
175 } 285 }
176 286
177 private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) { 287 private ListenableFuture<TransportApiResponseMsg> getDeviceInfo(DeviceId deviceId, DeviceCredentials credentials) {
@@ -183,11 +293,17 @@ public class DefaultTransportApiService implements TransportApiService { @@ -183,11 +293,17 @@ public class DefaultTransportApiService implements TransportApiService {
183 try { 293 try {
184 ValidateDeviceCredentialsResponseMsg.Builder builder = ValidateDeviceCredentialsResponseMsg.newBuilder(); 294 ValidateDeviceCredentialsResponseMsg.Builder builder = ValidateDeviceCredentialsResponseMsg.newBuilder();
185 builder.setDeviceInfo(getDeviceInfoProto(device)); 295 builder.setDeviceInfo(getDeviceInfoProto(device));
  296 + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
  297 + if (deviceProfile != null) {
  298 + builder.setProfileBody(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile)));
  299 + } else {
  300 + log.warn("[{}] Failed to find device profile [{}] for device. ", device.getId(), device.getDeviceProfileId());
  301 + }
186 if (!StringUtils.isEmpty(credentials.getCredentialsValue())) { 302 if (!StringUtils.isEmpty(credentials.getCredentialsValue())) {
187 builder.setCredentialsBody(credentials.getCredentialsValue()); 303 builder.setCredentialsBody(credentials.getCredentialsValue());
188 } 304 }
189 return TransportApiResponseMsg.newBuilder() 305 return TransportApiResponseMsg.newBuilder()
190 - .setValidateTokenResponseMsg(builder.build()).build(); 306 + .setValidateCredResponseMsg(builder.build()).build();
191 } catch (JsonProcessingException e) { 307 } catch (JsonProcessingException e) {
192 log.warn("[{}] Failed to lookup device by id", deviceId, e); 308 log.warn("[{}] Failed to lookup device by id", deviceId, e);
193 return getEmptyTransportApiResponse(); 309 return getEmptyTransportApiResponse();
@@ -203,6 +319,8 @@ public class DefaultTransportApiService implements TransportApiService { @@ -203,6 +319,8 @@ public class DefaultTransportApiService implements TransportApiService {
203 .setDeviceIdLSB(device.getId().getId().getLeastSignificantBits()) 319 .setDeviceIdLSB(device.getId().getId().getLeastSignificantBits())
204 .setDeviceName(device.getName()) 320 .setDeviceName(device.getName())
205 .setDeviceType(device.getType()) 321 .setDeviceType(device.getType())
  322 + .setDeviceProfileIdMSB(device.getDeviceProfileId().getId().getMostSignificantBits())
  323 + .setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits())
206 .setAdditionalInfo(mapper.writeValueAsString(device.getAdditionalInfo())) 324 .setAdditionalInfo(mapper.writeValueAsString(device.getAdditionalInfo()))
207 .build(); 325 .build();
208 } 326 }
@@ -213,6 +331,6 @@ public class DefaultTransportApiService implements TransportApiService { @@ -213,6 +331,6 @@ public class DefaultTransportApiService implements TransportApiService {
213 331
214 private TransportApiResponseMsg getEmptyTransportApiResponse() { 332 private TransportApiResponseMsg getEmptyTransportApiResponse() {
215 return TransportApiResponseMsg.newBuilder() 333 return TransportApiResponseMsg.newBuilder()
216 - .setValidateTokenResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build(); 334 + .setValidateCredResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build();
217 } 335 }
218 } 336 }
@@ -38,19 +38,14 @@ public abstract class AbstractCleanUpService { @@ -38,19 +38,14 @@ public abstract class AbstractCleanUpService {
38 @Value("${spring.datasource.password}") 38 @Value("${spring.datasource.password}")
39 protected String dbPassword; 39 protected String dbPassword;
40 40
41 - protected long executeQuery(Connection conn, String query) {  
42 - long removed = 0L;  
43 - try {  
44 - Statement statement = conn.createStatement();  
45 - ResultSet resultSet = statement.executeQuery(query);  
46 - getWarnings(statement); 41 + protected long executeQuery(Connection conn, String query) throws SQLException {
  42 + try (Statement statement = conn.createStatement(); ResultSet resultSet = statement.executeQuery(query)) {
  43 + if (log.isDebugEnabled()) {
  44 + getWarnings(statement);
  45 + }
47 resultSet.next(); 46 resultSet.next();
48 - removed = resultSet.getLong(1);  
49 - log.debug("Successfully executed query: {}", query);  
50 - } catch (SQLException e) {  
51 - log.debug("Failed to execute query: {} due to: {}", query, e.getMessage()); 47 + return resultSet.getLong(1);
52 } 48 }
53 - return removed;  
54 } 49 }
55 50
56 protected void getWarnings(Statement statement) throws SQLException { 51 protected void getWarnings(Statement statement) throws SQLException {
@@ -65,6 +60,6 @@ public abstract class AbstractCleanUpService { @@ -65,6 +60,6 @@ public abstract class AbstractCleanUpService {
65 } 60 }
66 } 61 }
67 62
68 - protected abstract void doCleanUp(Connection connection); 63 + protected abstract void doCleanUp(Connection connection) throws SQLException;
69 64
70 } 65 }
@@ -52,7 +52,7 @@ public class EventsCleanUpService extends AbstractCleanUpService { @@ -52,7 +52,7 @@ public class EventsCleanUpService extends AbstractCleanUpService {
52 } 52 }
53 53
54 @Override 54 @Override
55 - protected void doCleanUp(Connection connection) { 55 + protected void doCleanUp(Connection connection) throws SQLException {
56 long totalEventsRemoved = executeQuery(connection, "call cleanup_events_by_ttl(" + ttl + ", " + debugTtl + ", 0);"); 56 long totalEventsRemoved = executeQuery(connection, "call cleanup_events_by_ttl(" + ttl + ", " + debugTtl + ", 0);");
57 log.info("Total events removed by TTL: [{}]", totalEventsRemoved); 57 log.info("Total events removed by TTL: [{}]", totalEventsRemoved);
58 } 58 }
@@ -23,6 +23,7 @@ import org.thingsboard.server.dao.util.PsqlDao; @@ -23,6 +23,7 @@ import org.thingsboard.server.dao.util.PsqlDao;
23 import org.thingsboard.server.dao.util.SqlTsDao; 23 import org.thingsboard.server.dao.util.SqlTsDao;
24 24
25 import java.sql.Connection; 25 import java.sql.Connection;
  26 +import java.sql.SQLException;
26 27
27 @SqlTsDao 28 @SqlTsDao
28 @PsqlDao 29 @PsqlDao
@@ -34,10 +35,10 @@ public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpServi @@ -34,10 +35,10 @@ public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpServi
34 private String partitionType; 35 private String partitionType;
35 36
36 @Override 37 @Override
37 - protected void doCleanUp(Connection connection) {  
38 - long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);");  
39 - log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved);  
40 - long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);");  
41 - log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved); 38 + protected void doCleanUp(Connection connection) throws SQLException {
  39 + long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);");
  40 + log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved);
  41 + long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);");
  42 + log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved);
42 } 43 }
43 } 44 }
@@ -21,6 +21,7 @@ import org.thingsboard.server.dao.model.ModelConstants; @@ -21,6 +21,7 @@ import org.thingsboard.server.dao.model.ModelConstants;
21 import org.thingsboard.server.dao.util.TimescaleDBTsDao; 21 import org.thingsboard.server.dao.util.TimescaleDBTsDao;
22 22
23 import java.sql.Connection; 23 import java.sql.Connection;
  24 +import java.sql.SQLException;
24 25
25 @TimescaleDBTsDao 26 @TimescaleDBTsDao
26 @Service 27 @Service
@@ -28,8 +29,8 @@ import java.sql.Connection; @@ -28,8 +29,8 @@ import java.sql.Connection;
28 public class TimescaleTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService { 29 public class TimescaleTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService {
29 30
30 @Override 31 @Override
31 - protected void doCleanUp(Connection connection) { 32 + protected void doCleanUp(Connection connection) throws SQLException {
32 long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);"); 33 long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);");
33 log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved); 34 log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved);
34 } 35 }
35 -}  
  36 +}
@@ -29,6 +29,8 @@ @@ -29,6 +29,8 @@
29 29
30 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />--> 30 <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />-->
31 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />--> 31 <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />-->
  32 +<!-- <logger name="org.thingsboard.server.queue.memory.InMemoryStorage" level="DEBUG" />-->
  33 +
32 34
33 <!-- <logger name="org.thingsboard.server.service.subscription" level="TRACE"/>--> 35 <!-- <logger name="org.thingsboard.server.service.subscription" level="TRACE"/>-->
34 <!-- <logger name="org.thingsboard.server.service.telemetry" level="TRACE"/>--> 36 <!-- <logger name="org.thingsboard.server.service.telemetry" level="TRACE"/>-->
@@ -140,8 +140,21 @@ cassandra: @@ -140,8 +140,21 @@ cassandra:
140 url: "${CASSANDRA_URL:127.0.0.1:9042}" 140 url: "${CASSANDRA_URL:127.0.0.1:9042}"
141 # Specify local datacenter name 141 # Specify local datacenter name
142 local_datacenter: "${CASSANDRA_LOCAL_DATACENTER:datacenter1}" 142 local_datacenter: "${CASSANDRA_LOCAL_DATACENTER:datacenter1}"
143 - # Enable/disable secure connection  
144 - ssl: "${CASSANDRA_USE_SSL:false}" 143 + ssl:
  144 + # Enable/disable secure connection
  145 + enabled: "${CASSANDRA_USE_SSL:false}"
  146 + # Enable/disable validation of Cassandra server hostname
  147 + # If enabled, hostname of Cassandra server must match CN of server certificate
  148 + hostname_validation: "${CASSANDRA_SSL_HOSTNAME_VALIDATION:true}"
  149 + # Set trust store for client authentication of server (optional, uses trust store from default SSLContext if not set)
  150 + trust_store: "${CASSANDRA_SSL_TRUST_STORE:}"
  151 + trust_store_password: "${CASSANDRA_SSL_TRUST_STORE_PASSWORD:}"
  152 + # Set key store for server authentication of client (optional, uses key store from default SSLContext if not set)
  153 + # A key store is only needed if the Cassandra server requires client authentication
  154 + key_store: "${CASSANDRA_SSL_KEY_STORE:}"
  155 + key_store_password: "${CASSANDRA_SSL_KEY_STORE_PASSWORD:}"
  156 + # Comma separated list of cipher suites (optional, uses Java default cipher suites if not set)
  157 + cipher_suites: "${CASSANDRA_SSL_CIPHER_SUITES:}"
145 # Enable/disable JMX 158 # Enable/disable JMX
146 jmx: "${CASSANDRA_USE_JMX:false}" 159 jmx: "${CASSANDRA_USE_JMX:false}"
147 # Enable/disable metrics collection. 160 # Enable/disable metrics collection.
@@ -217,6 +230,8 @@ sql: @@ -217,6 +230,8 @@ sql:
217 batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:100}" 230 batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:100}"
218 stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}" 231 stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}"
219 batch_threads: "${SQL_TS_LATEST_BATCH_THREADS:4}" 232 batch_threads: "${SQL_TS_LATEST_BATCH_THREADS:4}"
  233 + # Specify whether to sort entities before batch update. Should be enabled for cluster mode to avoid deadlocks
  234 + batch_sort: "${SQL_BATCH_SORT:false}"
220 # Specify whether to remove null characters from strValue of attributes and timeseries before insert 235 # Specify whether to remove null characters from strValue of attributes and timeseries before insert
221 remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}" 236 remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}"
222 # Specify whether to log database queries and their parameters generated by entity query repository 237 # Specify whether to log database queries and their parameters generated by entity query repository
@@ -318,6 +333,12 @@ caffeine: @@ -318,6 +333,12 @@ caffeine:
318 securitySettings: 333 securitySettings:
319 timeToLiveInMinutes: 1440 334 timeToLiveInMinutes: 1440
320 maxSize: 0 335 maxSize: 0
  336 + tenantProfiles:
  337 + timeToLiveInMinutes: 1440
  338 + maxSize: 0
  339 + deviceProfiles:
  340 + timeToLiveInMinutes: 1440
  341 + maxSize: 0
321 342
322 redis: 343 redis:
323 # standalone or cluster 344 # standalone or cluster
@@ -403,7 +424,7 @@ spring: @@ -403,7 +424,7 @@ spring:
403 database-platform: "${SPRING_JPA_DATABASE_PLATFORM:org.hibernate.dialect.PostgreSQLDialect}" 424 database-platform: "${SPRING_JPA_DATABASE_PLATFORM:org.hibernate.dialect.PostgreSQLDialect}"
404 datasource: 425 datasource:
405 driverClassName: "${SPRING_DRIVER_CLASS_NAME:org.postgresql.Driver}" 426 driverClassName: "${SPRING_DRIVER_CLASS_NAME:org.postgresql.Driver}"
406 - url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard}" 427 + url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard_32}"
407 username: "${SPRING_DATASOURCE_USERNAME:postgres}" 428 username: "${SPRING_DATASOURCE_USERNAME:postgres}"
408 password: "${SPRING_DATASOURCE_PASSWORD:postgres}" 429 password: "${SPRING_DATASOURCE_PASSWORD:postgres}"
409 hikari: 430 hikari:
@@ -429,6 +450,7 @@ audit-log: @@ -429,6 +450,7 @@ audit-log:
429 "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" 450 "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}"
430 "alarm": "${AUDIT_LOG_MASK_ALARM:W}" 451 "alarm": "${AUDIT_LOG_MASK_ALARM:W}"
431 "entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}" 452 "entity_view": "${AUDIT_LOG_MASK_ENTITY_VIEW:W}"
  453 + "device_profile": "${AUDIT_LOG_MASK_DEVICE_PROFILE:W}"
432 sink: 454 sink:
433 # Type of external sink. possible options: none, elasticsearch 455 # Type of external sink. possible options: none, elasticsearch
434 type: "${AUDIT_LOG_SINK_TYPE:none}" 456 type: "${AUDIT_LOG_SINK_TYPE:none}"
@@ -461,7 +483,7 @@ js: @@ -461,7 +483,7 @@ js:
461 # Specify thread pool size for JavaScript sandbox resource monitor 483 # Specify thread pool size for JavaScript sandbox resource monitor
462 monitor_thread_pool_size: "${LOCAL_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}" 484 monitor_thread_pool_size: "${LOCAL_JS_SANDBOX_MONITOR_THREAD_POOL_SIZE:4}"
463 # Maximum CPU time in milliseconds allowed for script execution 485 # Maximum CPU time in milliseconds allowed for script execution
464 - max_cpu_time: "${LOCAL_JS_SANDBOX_MAX_CPU_TIME:10000}" 486 + max_cpu_time: "${LOCAL_JS_SANDBOX_MAX_CPU_TIME:8000}"
465 # Maximum allowed JavaScript execution errors before JavaScript will be blacklisted 487 # Maximum allowed JavaScript execution errors before JavaScript will be blacklisted
466 max_errors: "${LOCAL_JS_SANDBOX_MAX_ERRORS:3}" 488 max_errors: "${LOCAL_JS_SANDBOX_MAX_ERRORS:3}"
467 # JS Eval max request timeout. 0 - no timeout 489 # JS Eval max request timeout. 0 - no timeout
@@ -554,6 +576,10 @@ swagger: @@ -554,6 +576,10 @@ swagger:
554 576
555 queue: 577 queue:
556 type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) 578 type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ)
  579 + in_memory:
  580 + stats:
  581 + # For debug lvl
  582 + print-interval-ms: "${TB_QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}"
557 kafka: 583 kafka:
558 bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" 584 bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
559 acks: "${TB_KAFKA_ACKS:all}" 585 acks: "${TB_KAFKA_ACKS:all}"
@@ -573,11 +599,11 @@ queue: @@ -573,11 +599,11 @@ queue:
573 security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" 599 security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}"
574 other: 600 other:
575 topic-properties: 601 topic-properties:
576 - rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
577 - core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
578 - transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
579 - notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000}"  
580 - js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600}" 602 + rule-engine: "${TB_QUEUE_KAFKA_RE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  603 + core: "${TB_QUEUE_KAFKA_CORE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  604 + transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  605 + notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1}"
  606 + js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100}"
581 aws_sqs: 607 aws_sqs:
582 use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}" 608 use_default_credential_provider_chain: "${TB_QUEUE_AWS_SQS_USE_DEFAULT_CREDENTIAL_PROVIDER_CHAIN:false}"
583 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}" 609 access_key_id: "${TB_QUEUE_AWS_SQS_ACCESS_KEY_ID:YOUR_KEY}"
@@ -685,6 +711,7 @@ queue: @@ -685,6 +711,7 @@ queue:
685 retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited 711 retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
686 failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; 712 failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
687 pause-between-retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRY_PAUSE:3}"# Time in seconds to wait in consumer thread before retries; 713 pause-between-retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRY_PAUSE:3}"# Time in seconds to wait in consumer thread before retries;
  714 + max-pause-between-retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_MAX_RETRY_PAUSE:3}"# Max allowed time in seconds for pause between retries.
688 - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}" 715 - name: "${TB_QUEUE_RE_HP_QUEUE_NAME:HighPriority}"
689 topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}" 716 topic: "${TB_QUEUE_RE_HP_TOPIC:tb_rule_engine.hp}"
690 poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}" 717 poll-interval: "${TB_QUEUE_RE_HP_POLL_INTERVAL_MS:25}"
@@ -700,6 +727,7 @@ queue: @@ -700,6 +727,7 @@ queue:
700 retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited 727 retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
701 failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; 728 failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
702 pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries; 729 pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
  730 + max-pause-between-retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_MAX_RETRY_PAUSE:5}"# Max allowed time in seconds for pause between retries.
703 - name: "${TB_QUEUE_RE_SQ_QUEUE_NAME:SequentialByOriginator}" 731 - name: "${TB_QUEUE_RE_SQ_QUEUE_NAME:SequentialByOriginator}"
704 topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}" 732 topic: "${TB_QUEUE_RE_SQ_TOPIC:tb_rule_engine.sq}"
705 poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}" 733 poll-interval: "${TB_QUEUE_RE_SQ_POLL_INTERVAL_MS:25}"
@@ -715,6 +743,7 @@ queue: @@ -715,6 +743,7 @@ queue:
715 retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited 743 retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
716 failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages; 744 failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
717 pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries; 745 pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRY_PAUSE:5}"# Time in seconds to wait in consumer thread before retries;
  746 + max-pause-between-retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_MAX_RETRY_PAUSE:5}"# Max allowed time in seconds for pause between retries.
718 transport: 747 transport:
719 # For high priority notifications that require minimum latency and processing time 748 # For high priority notifications that require minimum latency and processing time
720 notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}" 749 notifications_topic: "${TB_QUEUE_TRANSPORT_NOTIFICATIONS_TOPIC:tb_transport.notifications}"
@@ -739,4 +768,4 @@ management: @@ -739,4 +768,4 @@ management:
739 web: 768 web:
740 exposure: 769 exposure:
741 # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics). 770 # Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
742 - include: '${METRICS_ENDPOINTS_EXPOSE:info}'  
  771 + include: '${METRICS_ENDPOINTS_EXPOSE:info}'
@@ -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 com.datastax.oss.driver.api.core.uuid.Uuids;
18 import com.fasterxml.jackson.core.type.TypeReference; 19 import com.fasterxml.jackson.core.type.TypeReference;
19 import com.fasterxml.jackson.databind.JsonNode; 20 import com.fasterxml.jackson.databind.JsonNode;
20 import com.fasterxml.jackson.databind.ObjectMapper; 21 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -59,8 +60,16 @@ import org.springframework.util.MultiValueMap; @@ -59,8 +60,16 @@ import org.springframework.util.MultiValueMap;
59 import org.springframework.web.context.WebApplicationContext; 60 import org.springframework.web.context.WebApplicationContext;
60 import org.thingsboard.server.common.data.BaseData; 61 import org.thingsboard.server.common.data.BaseData;
61 import org.thingsboard.server.common.data.Customer; 62 import org.thingsboard.server.common.data.Customer;
  63 +import org.thingsboard.server.common.data.DeviceProfile;
  64 +import org.thingsboard.server.common.data.DeviceProfileType;
  65 +import org.thingsboard.server.common.data.DeviceTransportType;
62 import org.thingsboard.server.common.data.Tenant; 66 import org.thingsboard.server.common.data.Tenant;
63 import org.thingsboard.server.common.data.User; 67 import org.thingsboard.server.common.data.User;
  68 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
  69 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
  70 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
  71 +import org.thingsboard.server.common.data.id.HasId;
  72 +import org.thingsboard.server.common.data.id.RuleChainId;
64 import org.thingsboard.server.common.data.id.TenantId; 73 import org.thingsboard.server.common.data.id.TenantId;
65 import org.thingsboard.server.common.data.id.UUIDBased; 74 import org.thingsboard.server.common.data.id.UUIDBased;
66 import org.thingsboard.server.common.data.page.PageLink; 75 import org.thingsboard.server.common.data.page.PageLink;
@@ -217,6 +226,10 @@ public abstract class AbstractWebTest { @@ -217,6 +226,10 @@ public abstract class AbstractWebTest {
217 login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD); 226 login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD);
218 } 227 }
219 228
  229 + protected void loginUser(String userName, String password) throws Exception {
  230 + login(userName, password);
  231 + }
  232 +
220 private Tenant savedDifferentTenant; 233 private Tenant savedDifferentTenant;
221 234
222 protected void loginDifferentTenant() throws Exception { 235 protected void loginDifferentTenant() throws Exception {
@@ -242,15 +255,27 @@ public abstract class AbstractWebTest { @@ -242,15 +255,27 @@ public abstract class AbstractWebTest {
242 protected User createUserAndLogin(User user, String password) throws Exception { 255 protected User createUserAndLogin(User user, String password) throws Exception {
243 User savedUser = doPost("/api/user", user, User.class); 256 User savedUser = doPost("/api/user", user, User.class);
244 logout(); 257 logout();
  258 + JsonNode activateRequest = getActivateRequest(password);
  259 + JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class);
  260 + validateAndSetJwtToken(tokenInfo, user.getEmail());
  261 + return savedUser;
  262 + }
  263 +
  264 + protected User createUser(User user, String password) throws Exception {
  265 + User savedUser = doPost("/api/user", user, User.class);
  266 + JsonNode activateRequest = getActivateRequest(password);
  267 + ResultActions resultActions = doPost("/api/noauth/activate", activateRequest);
  268 + resultActions.andExpect(status().isOk());
  269 + return savedUser;
  270 + }
  271 +
  272 + private JsonNode getActivateRequest(String password) throws Exception {
245 doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken) 273 doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
246 .andExpect(status().isSeeOther()) 274 .andExpect(status().isSeeOther())
247 .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken)); 275 .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
248 - JsonNode activateRequest = new ObjectMapper().createObjectNode() 276 + return new ObjectMapper().createObjectNode()
249 .put("activateToken", TestMailService.currentActivateToken) 277 .put("activateToken", TestMailService.currentActivateToken)
250 .put("password", password); 278 .put("password", password);
251 - JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class);  
252 - validateAndSetJwtToken(tokenInfo, user.getEmail());  
253 - return savedUser;  
254 } 279 }
255 280
256 protected void login(String username, String password) throws Exception { 281 protected void login(String username, String password) throws Exception {
@@ -304,6 +329,23 @@ public abstract class AbstractWebTest { @@ -304,6 +329,23 @@ public abstract class AbstractWebTest {
304 } 329 }
305 } 330 }
306 331
  332 + protected DeviceProfile createDeviceProfile(String name) {
  333 + DeviceProfile deviceProfile = new DeviceProfile();
  334 + deviceProfile.setName(name);
  335 + deviceProfile.setType(DeviceProfileType.DEFAULT);
  336 + deviceProfile.setTransportType(DeviceTransportType.DEFAULT);
  337 + deviceProfile.setDescription(name + " Test");
  338 + DeviceProfileData deviceProfileData = new DeviceProfileData();
  339 + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
  340 + DefaultDeviceProfileTransportConfiguration transportConfiguration = new DefaultDeviceProfileTransportConfiguration();
  341 + deviceProfileData.setConfiguration(configuration);
  342 + deviceProfileData.setTransportConfiguration(transportConfiguration);
  343 + deviceProfile.setProfileData(deviceProfileData);
  344 + deviceProfile.setDefault(false);
  345 + deviceProfile.setDefaultRuleChainId(null);
  346 + return deviceProfile;
  347 + }
  348 +
307 protected ResultActions doGet(String urlTemplate, Object... urlVariables) throws Exception { 349 protected ResultActions doGet(String urlTemplate, Object... urlVariables) throws Exception {
308 MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables); 350 MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables);
309 setJwtToken(getRequest); 351 setJwtToken(getRequest);
@@ -416,6 +458,10 @@ public abstract class AbstractWebTest { @@ -416,6 +458,10 @@ public abstract class AbstractWebTest {
416 return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass); 458 return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass);
417 } 459 }
418 460
  461 + protected <T> T doPostClaimAsync(String urlTemplate, Object content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
  462 + return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass);
  463 + }
  464 +
419 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { 465 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
420 return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass); 466 return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass);
421 } 467 }
@@ -486,7 +532,7 @@ public abstract class AbstractWebTest { @@ -486,7 +532,7 @@ public abstract class AbstractWebTest {
486 return mapper.readerFor(type).readValue(content); 532 return mapper.readerFor(type).readValue(content);
487 } 533 }
488 534
489 - public class IdComparator<D extends BaseData<? extends UUIDBased>> implements Comparator<D> { 535 + public class IdComparator<D extends HasId> implements Comparator<D> {
490 @Override 536 @Override
491 public int compare(D o1, D o2) { 537 public int compare(D o1, D o2) {
492 return o1.getId().getId().compareTo(o2.getId().getId()); 538 return o1.getId().getId().compareTo(o2.getId().getId());
@@ -24,6 +24,7 @@ import org.junit.Before; @@ -24,6 +24,7 @@ import org.junit.Before;
24 import org.junit.Test; 24 import org.junit.Test;
25 import org.thingsboard.server.common.data.Customer; 25 import org.thingsboard.server.common.data.Customer;
26 import org.thingsboard.server.common.data.Device; 26 import org.thingsboard.server.common.data.Device;
  27 +import org.thingsboard.server.common.data.DeviceProfile;
27 import org.thingsboard.server.common.data.EntitySubtype; 28 import org.thingsboard.server.common.data.EntitySubtype;
28 import org.thingsboard.server.common.data.Tenant; 29 import org.thingsboard.server.common.data.Tenant;
29 import org.thingsboard.server.common.data.User; 30 import org.thingsboard.server.common.data.User;
@@ -185,9 +186,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -185,9 +186,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
185 public void testSaveDeviceWithEmptyType() throws Exception { 186 public void testSaveDeviceWithEmptyType() throws Exception {
186 Device device = new Device(); 187 Device device = new Device();
187 device.setName("My device"); 188 device.setName("My device");
188 - doPost("/api/device", device)  
189 - .andExpect(status().isBadRequest())  
190 - .andExpect(statusReason(containsString("Device type should be specified"))); 189 + Device savedDevice = doPost("/api/device", device, Device.class);
  190 + Assert.assertEquals("default", savedDevice.getType());
191 } 191 }
192 192
193 @Test 193 @Test
  1 +/**
  2 + * Copyright © 2016-2020 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.fasterxml.jackson.core.type.TypeReference;
  19 +import org.junit.After;
  20 +import org.junit.Assert;
  21 +import org.junit.Before;
  22 +import org.junit.Ignore;
  23 +import org.junit.Test;
  24 +import org.thingsboard.server.common.data.Device;
  25 +import org.thingsboard.server.common.data.DeviceProfile;
  26 +import org.thingsboard.server.common.data.DeviceProfileInfo;
  27 +import org.thingsboard.server.common.data.DeviceProfileType;
  28 +import org.thingsboard.server.common.data.DeviceTransportType;
  29 +import org.thingsboard.server.common.data.Tenant;
  30 +import org.thingsboard.server.common.data.User;
  31 +import org.thingsboard.server.common.data.page.PageData;
  32 +import org.thingsboard.server.common.data.page.PageLink;
  33 +import org.thingsboard.server.common.data.security.Authority;
  34 +
  35 +import java.util.ArrayList;
  36 +import java.util.Collections;
  37 +import java.util.List;
  38 +import java.util.stream.Collectors;
  39 +
  40 +import static org.hamcrest.Matchers.containsString;
  41 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  42 +
  43 +public abstract class BaseDeviceProfileControllerTest extends AbstractControllerTest {
  44 +
  45 + private IdComparator<DeviceProfile> idComparator = new IdComparator<>();
  46 + private IdComparator<DeviceProfileInfo> deviceProfileInfoIdComparator = new IdComparator<>();
  47 +
  48 + private Tenant savedTenant;
  49 + private User tenantAdmin;
  50 +
  51 + @Before
  52 + public void beforeTest() throws Exception {
  53 + loginSysAdmin();
  54 +
  55 + Tenant tenant = new Tenant();
  56 + tenant.setTitle("My tenant");
  57 + savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  58 + Assert.assertNotNull(savedTenant);
  59 +
  60 + tenantAdmin = new User();
  61 + tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
  62 + tenantAdmin.setTenantId(savedTenant.getId());
  63 + tenantAdmin.setEmail("tenant2@thingsboard.org");
  64 + tenantAdmin.setFirstName("Joe");
  65 + tenantAdmin.setLastName("Downs");
  66 +
  67 + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
  68 + }
  69 +
  70 + @After
  71 + public void afterTest() throws Exception {
  72 + loginSysAdmin();
  73 +
  74 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  75 + .andExpect(status().isOk());
  76 + }
  77 +
  78 + @Test
  79 + public void testSaveDeviceProfile() throws Exception {
  80 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  81 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  82 + Assert.assertNotNull(savedDeviceProfile);
  83 + Assert.assertNotNull(savedDeviceProfile.getId());
  84 + Assert.assertTrue(savedDeviceProfile.getCreatedTime() > 0);
  85 + Assert.assertEquals(deviceProfile.getName(), savedDeviceProfile.getName());
  86 + Assert.assertEquals(deviceProfile.getDescription(), savedDeviceProfile.getDescription());
  87 + Assert.assertEquals(deviceProfile.getProfileData(), savedDeviceProfile.getProfileData());
  88 + Assert.assertEquals(deviceProfile.isDefault(), savedDeviceProfile.isDefault());
  89 + Assert.assertEquals(deviceProfile.getDefaultRuleChainId(), savedDeviceProfile.getDefaultRuleChainId());
  90 + savedDeviceProfile.setName("New device profile");
  91 + doPost("/api/deviceProfile", savedDeviceProfile, DeviceProfile.class);
  92 + DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString(), DeviceProfile.class);
  93 + Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfile.getName());
  94 + }
  95 +
  96 + @Test
  97 + public void testFindDeviceProfileById() throws Exception {
  98 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  99 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  100 + DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString(), DeviceProfile.class);
  101 + Assert.assertNotNull(foundDeviceProfile);
  102 + Assert.assertEquals(savedDeviceProfile, foundDeviceProfile);
  103 + }
  104 +
  105 + @Test
  106 + public void testFindDeviceProfileInfoById() throws Exception {
  107 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  108 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  109 + DeviceProfileInfo foundDeviceProfileInfo = doGet("/api/deviceProfileInfo/"+savedDeviceProfile.getId().getId().toString(), DeviceProfileInfo.class);
  110 + Assert.assertNotNull(foundDeviceProfileInfo);
  111 + Assert.assertEquals(savedDeviceProfile.getId(), foundDeviceProfileInfo.getId());
  112 + Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfileInfo.getName());
  113 + Assert.assertEquals(savedDeviceProfile.getType(), foundDeviceProfileInfo.getType());
  114 + }
  115 +
  116 + @Test
  117 + public void testFindDefaultDeviceProfileInfo() throws Exception {
  118 + DeviceProfileInfo foundDefaultDeviceProfileInfo = doGet("/api/deviceProfileInfo/default", DeviceProfileInfo.class);
  119 + Assert.assertNotNull(foundDefaultDeviceProfileInfo);
  120 + Assert.assertNotNull(foundDefaultDeviceProfileInfo.getId());
  121 + Assert.assertNotNull(foundDefaultDeviceProfileInfo.getName());
  122 + Assert.assertNotNull(foundDefaultDeviceProfileInfo.getType());
  123 + Assert.assertEquals(DeviceProfileType.DEFAULT, foundDefaultDeviceProfileInfo.getType());
  124 + Assert.assertEquals("default", foundDefaultDeviceProfileInfo.getName());
  125 + }
  126 +
  127 + @Test
  128 + public void testSetDefaultDeviceProfile() throws Exception {
  129 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile 1");
  130 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  131 + DeviceProfile defaultDeviceProfile = doPost("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString()+"/default", null, DeviceProfile.class);
  132 + Assert.assertNotNull(defaultDeviceProfile);
  133 + DeviceProfileInfo foundDefaultDeviceProfile = doGet("/api/deviceProfileInfo/default", DeviceProfileInfo.class);
  134 + Assert.assertNotNull(foundDefaultDeviceProfile);
  135 + Assert.assertEquals(savedDeviceProfile.getName(), foundDefaultDeviceProfile.getName());
  136 + Assert.assertEquals(savedDeviceProfile.getId(), foundDefaultDeviceProfile.getId());
  137 + Assert.assertEquals(savedDeviceProfile.getType(), foundDefaultDeviceProfile.getType());
  138 + }
  139 +
  140 + @Test
  141 + public void testSaveDeviceProfileWithEmptyName() throws Exception {
  142 + DeviceProfile deviceProfile = new DeviceProfile();
  143 + doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest())
  144 + .andExpect(statusReason(containsString("Device profile name should be specified")));
  145 + }
  146 +
  147 + @Test
  148 + public void testSaveDeviceProfileWithSameName() throws Exception {
  149 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  150 + doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk());
  151 + DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile");
  152 + doPost("/api/deviceProfile", deviceProfile2).andExpect(status().isBadRequest())
  153 + .andExpect(statusReason(containsString("Device profile with such name already exists")));
  154 + }
  155 +
  156 + @Ignore
  157 + @Test
  158 + public void testChangeDeviceProfileTypeWithExistingDevices() throws Exception {
  159 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  160 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  161 + Device device = new Device();
  162 + device.setName("Test device");
  163 + device.setType("default");
  164 + device.setDeviceProfileId(savedDeviceProfile.getId());
  165 + doPost("/api/device", device, Device.class);
  166 + //TODO uncomment once we have other device types;
  167 + //savedDeviceProfile.setType(DeviceProfileType.LWM2M);
  168 + doPost("/api/deviceProfile", savedDeviceProfile).andExpect(status().isBadRequest())
  169 + .andExpect(statusReason(containsString("Can't change device profile type because devices referenced it")));
  170 + }
  171 +
  172 + @Test
  173 + public void testChangeDeviceProfileTransportTypeWithExistingDevices() throws Exception {
  174 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  175 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  176 + Device device = new Device();
  177 + device.setName("Test device");
  178 + device.setType("default");
  179 + device.setDeviceProfileId(savedDeviceProfile.getId());
  180 + doPost("/api/device", device, Device.class);
  181 + savedDeviceProfile.setTransportType(DeviceTransportType.MQTT);
  182 + doPost("/api/deviceProfile", savedDeviceProfile).andExpect(status().isBadRequest())
  183 + .andExpect(statusReason(containsString("Can't change device profile transport type because devices referenced it")));
  184 + }
  185 +
  186 + @Test
  187 + public void testDeleteDeviceProfileWithExistingDevice() throws Exception {
  188 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  189 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  190 +
  191 + Device device = new Device();
  192 + device.setName("Test device");
  193 + device.setType("default");
  194 + device.setDeviceProfileId(savedDeviceProfile.getId());
  195 +
  196 + Device savedDevice = doPost("/api/device", device, Device.class);
  197 +
  198 + doDelete("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString())
  199 + .andExpect(status().isBadRequest())
  200 + .andExpect(statusReason(containsString("The device profile referenced by the devices cannot be deleted")));
  201 + }
  202 +
  203 + @Test
  204 + public void testDeleteDeviceProfile() throws Exception {
  205 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
  206 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  207 +
  208 + doDelete("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString())
  209 + .andExpect(status().isOk());
  210 +
  211 + doGet("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString())
  212 + .andExpect(status().isNotFound());
  213 + }
  214 +
  215 + @Test
  216 + public void testFindDeviceProfiles() throws Exception {
  217 + List<DeviceProfile> deviceProfiles = new ArrayList<>();
  218 + PageLink pageLink = new PageLink(17);
  219 + PageData<DeviceProfile> pageData = doGetTypedWithPageLink("/api/deviceProfiles?",
  220 + new TypeReference<PageData<DeviceProfile>>(){}, pageLink);
  221 + Assert.assertFalse(pageData.hasNext());
  222 + Assert.assertEquals(1, pageData.getTotalElements());
  223 + deviceProfiles.addAll(pageData.getData());
  224 +
  225 + for (int i=0;i<28;i++) {
  226 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i);
  227 + deviceProfiles.add(doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class));
  228 + }
  229 +
  230 + List<DeviceProfile> loadedDeviceProfiles = new ArrayList<>();
  231 + pageLink = new PageLink(17);
  232 + do {
  233 + pageData = doGetTypedWithPageLink("/api/deviceProfiles?",
  234 + new TypeReference<PageData<DeviceProfile>>(){}, pageLink);
  235 + loadedDeviceProfiles.addAll(pageData.getData());
  236 + if (pageData.hasNext()) {
  237 + pageLink = pageLink.nextPageLink();
  238 + }
  239 + } while (pageData.hasNext());
  240 +
  241 + Collections.sort(deviceProfiles, idComparator);
  242 + Collections.sort(loadedDeviceProfiles, idComparator);
  243 +
  244 + Assert.assertEquals(deviceProfiles, loadedDeviceProfiles);
  245 +
  246 + for (DeviceProfile deviceProfile : loadedDeviceProfiles) {
  247 + if (!deviceProfile.isDefault()) {
  248 + doDelete("/api/deviceProfile/" + deviceProfile.getId().getId().toString())
  249 + .andExpect(status().isOk());
  250 + }
  251 + }
  252 +
  253 + pageLink = new PageLink(17);
  254 + pageData = doGetTypedWithPageLink("/api/deviceProfiles?",
  255 + new TypeReference<PageData<DeviceProfile>>(){}, pageLink);
  256 + Assert.assertFalse(pageData.hasNext());
  257 + Assert.assertEquals(1, pageData.getTotalElements());
  258 + }
  259 +
  260 + @Test
  261 + public void testFindDeviceProfileInfos() throws Exception {
  262 + List<DeviceProfile> deviceProfiles = new ArrayList<>();
  263 + PageLink pageLink = new PageLink(17);
  264 + PageData<DeviceProfile> deviceProfilePageData = doGetTypedWithPageLink("/api/deviceProfiles?",
  265 + new TypeReference<PageData<DeviceProfile>>(){}, pageLink);
  266 + Assert.assertFalse(deviceProfilePageData.hasNext());
  267 + Assert.assertEquals(1, deviceProfilePageData.getTotalElements());
  268 + deviceProfiles.addAll(deviceProfilePageData.getData());
  269 +
  270 + for (int i=0;i<28;i++) {
  271 + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i);
  272 + deviceProfiles.add(doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class));
  273 + }
  274 +
  275 + List<DeviceProfileInfo> loadedDeviceProfileInfos = new ArrayList<>();
  276 + pageLink = new PageLink(17);
  277 + PageData<DeviceProfileInfo> pageData;
  278 + do {
  279 + pageData = doGetTypedWithPageLink("/api/deviceProfileInfos?",
  280 + new TypeReference<PageData<DeviceProfileInfo>>(){}, pageLink);
  281 + loadedDeviceProfileInfos.addAll(pageData.getData());
  282 + if (pageData.hasNext()) {
  283 + pageLink = pageLink.nextPageLink();
  284 + }
  285 + } while (pageData.hasNext());
  286 +
  287 + Collections.sort(deviceProfiles, idComparator);
  288 + Collections.sort(loadedDeviceProfileInfos, deviceProfileInfoIdComparator);
  289 +
  290 + List<DeviceProfileInfo> deviceProfileInfos = deviceProfiles.stream().map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(),
  291 + deviceProfile.getName(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList());
  292 +
  293 + Assert.assertEquals(deviceProfileInfos, loadedDeviceProfileInfos);
  294 +
  295 + for (DeviceProfile deviceProfile : deviceProfiles) {
  296 + if (!deviceProfile.isDefault()) {
  297 + doDelete("/api/deviceProfile/" + deviceProfile.getId().getId().toString())
  298 + .andExpect(status().isOk());
  299 + }
  300 + }
  301 +
  302 + pageLink = new PageLink(17);
  303 + pageData = doGetTypedWithPageLink("/api/deviceProfileInfos?",
  304 + new TypeReference<PageData<DeviceProfileInfo>>(){}, pageLink);
  305 + Assert.assertFalse(pageData.hasNext());
  306 + Assert.assertEquals(1, pageData.getTotalElements());
  307 + }
  308 +
  309 +}
@@ -22,6 +22,7 @@ import org.apache.commons.lang3.RandomStringUtils; @@ -22,6 +22,7 @@ import org.apache.commons.lang3.RandomStringUtils;
22 import org.eclipse.paho.client.mqttv3.MqttAsyncClient; 22 import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
23 import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 23 import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
24 import org.eclipse.paho.client.mqttv3.MqttMessage; 24 import org.eclipse.paho.client.mqttv3.MqttMessage;
  25 +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
25 import org.junit.After; 26 import org.junit.After;
26 import org.junit.Assert; 27 import org.junit.Assert;
27 import org.junit.Before; 28 import org.junit.Before;
@@ -424,7 +425,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @@ -424,7 +425,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
424 assertNotNull(accessToken); 425 assertNotNull(accessToken);
425 426
426 String clientId = MqttAsyncClient.generateClientId(); 427 String clientId = MqttAsyncClient.generateClientId();
427 - MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId); 428 + MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence());
428 429
429 MqttConnectOptions options = new MqttConnectOptions(); 430 MqttConnectOptions options = new MqttConnectOptions();
430 options.setUserName(accessToken); 431 options.setUserName(accessToken);
@@ -466,7 +467,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @@ -466,7 +467,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
466 assertNotNull(accessToken); 467 assertNotNull(accessToken);
467 468
468 String clientId = MqttAsyncClient.generateClientId(); 469 String clientId = MqttAsyncClient.generateClientId();
469 - MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId); 470 + MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence());
470 471
471 MqttConnectOptions options = new MqttConnectOptions(); 472 MqttConnectOptions options = new MqttConnectOptions();
472 options.setUserName(accessToken); 473 options.setUserName(accessToken);
@@ -15,21 +15,21 @@ @@ -15,21 +15,21 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
18 -import static org.hamcrest.Matchers.containsString;  
19 -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  
20 -  
21 -import java.util.ArrayList;  
22 -import java.util.Collections;  
23 -import java.util.List;  
24 - 18 +import com.fasterxml.jackson.core.type.TypeReference;
25 import org.apache.commons.lang3.RandomStringUtils; 19 import org.apache.commons.lang3.RandomStringUtils;
  20 +import org.junit.Assert;
  21 +import org.junit.Test;
26 import org.thingsboard.server.common.data.Tenant; 22 import org.thingsboard.server.common.data.Tenant;
  23 +import org.thingsboard.server.common.data.TenantInfo;
27 import org.thingsboard.server.common.data.page.PageData; 24 import org.thingsboard.server.common.data.page.PageData;
28 import org.thingsboard.server.common.data.page.PageLink; 25 import org.thingsboard.server.common.data.page.PageLink;
29 -import org.junit.Assert;  
30 -import org.junit.Test;  
31 26
32 -import com.fasterxml.jackson.core.type.TypeReference; 27 +import java.util.ArrayList;
  28 +import java.util.Collections;
  29 +import java.util.List;
  30 +
  31 +import static org.hamcrest.Matchers.containsString;
  32 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
33 33
34 public abstract class BaseTenantControllerTest extends AbstractControllerTest { 34 public abstract class BaseTenantControllerTest extends AbstractControllerTest {
35 35
@@ -65,6 +65,19 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { @@ -65,6 +65,19 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
65 doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) 65 doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
66 .andExpect(status().isOk()); 66 .andExpect(status().isOk());
67 } 67 }
  68 +
  69 + @Test
  70 + public void testFindTenantInfoById() throws Exception {
  71 + loginSysAdmin();
  72 + Tenant tenant = new Tenant();
  73 + tenant.setTitle("My tenant");
  74 + Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  75 + TenantInfo foundTenant = doGet("/api/tenant/info/"+savedTenant.getId().getId().toString(), TenantInfo.class);
  76 + Assert.assertNotNull(foundTenant);
  77 + Assert.assertEquals(new TenantInfo(savedTenant, "Default"), foundTenant);
  78 + doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
  79 + .andExpect(status().isOk());
  80 + }
68 81
69 @Test 82 @Test
70 public void testSaveTenantWithEmptyTitle() throws Exception { 83 public void testSaveTenantWithEmptyTitle() throws Exception {
@@ -217,4 +230,48 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { @@ -217,4 +230,48 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
217 Assert.assertFalse(pageData.hasNext()); 230 Assert.assertFalse(pageData.hasNext());
218 Assert.assertEquals(0, pageData.getData().size()); 231 Assert.assertEquals(0, pageData.getData().size());
219 } 232 }
  233 +
  234 + @Test
  235 + public void testFindTenantInfos() throws Exception {
  236 + loginSysAdmin();
  237 + List<TenantInfo> tenants = new ArrayList<>();
  238 + PageLink pageLink = new PageLink(17);
  239 + PageData<TenantInfo> pageData = doGetTypedWithPageLink("/api/tenantInfos?", new TypeReference<PageData<TenantInfo>>(){}, pageLink);
  240 + Assert.assertFalse(pageData.hasNext());
  241 + Assert.assertEquals(1, pageData.getData().size());
  242 + tenants.addAll(pageData.getData());
  243 +
  244 + for (int i=0;i<56;i++) {
  245 + Tenant tenant = new Tenant();
  246 + tenant.setTitle("Tenant"+i);
  247 + tenants.add(new TenantInfo(doPost("/api/tenant", tenant, Tenant.class), "Default"));
  248 + }
  249 +
  250 + List<TenantInfo> loadedTenants = new ArrayList<>();
  251 + pageLink = new PageLink(17);
  252 + do {
  253 + pageData = doGetTypedWithPageLink("/api/tenantInfos?", new TypeReference<PageData<TenantInfo>>(){}, pageLink);
  254 + loadedTenants.addAll(pageData.getData());
  255 + if (pageData.hasNext()) {
  256 + pageLink = pageLink.nextPageLink();
  257 + }
  258 + } while (pageData.hasNext());
  259 +
  260 + Collections.sort(tenants, idComparator);
  261 + Collections.sort(loadedTenants, idComparator);
  262 +
  263 + Assert.assertEquals(tenants, loadedTenants);
  264 +
  265 + for (TenantInfo tenant : loadedTenants) {
  266 + if (!tenant.getTitle().equals(TEST_TENANT_NAME)) {
  267 + doDelete("/api/tenant/"+tenant.getId().getId().toString())
  268 + .andExpect(status().isOk());
  269 + }
  270 + }
  271 +
  272 + pageLink = new PageLink(17);
  273 + pageData = doGetTypedWithPageLink("/api/tenantInfos?", new TypeReference<PageData<TenantInfo>>(){}, pageLink);
  274 + Assert.assertFalse(pageData.hasNext());
  275 + Assert.assertEquals(1, pageData.getData().size());
  276 + }
220 } 277 }
  1 +/**
  2 + * Copyright © 2016-2020 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.fasterxml.jackson.core.type.TypeReference;
  19 +import org.junit.After;
  20 +import org.junit.Assert;
  21 +import org.junit.Test;
  22 +import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.thingsboard.server.common.data.EntityInfo;
  24 +import org.thingsboard.server.common.data.Tenant;
  25 +import org.thingsboard.server.common.data.TenantProfile;
  26 +import org.thingsboard.server.common.data.TenantProfileData;
  27 +import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.common.data.page.PageData;
  29 +import org.thingsboard.server.common.data.page.PageLink;
  30 +import org.thingsboard.server.dao.tenant.TenantProfileService;
  31 +
  32 +import java.util.ArrayList;
  33 +import java.util.Collections;
  34 +import java.util.List;
  35 +import java.util.stream.Collectors;
  36 +
  37 +import static org.hamcrest.Matchers.containsString;
  38 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  39 +
  40 +public abstract class BaseTenantProfileControllerTest extends AbstractControllerTest {
  41 +
  42 + private IdComparator<TenantProfile> idComparator = new IdComparator<>();
  43 + private IdComparator<EntityInfo> tenantProfileInfoIdComparator = new IdComparator<>();
  44 +
  45 + @Autowired
  46 + private TenantProfileService tenantProfileService;
  47 +
  48 + @After
  49 + @Override
  50 + public void teardown() throws Exception {
  51 + super.teardown();
  52 + tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID);
  53 + }
  54 +
  55 + @Test
  56 + public void testSaveTenantProfile() throws Exception {
  57 + loginSysAdmin();
  58 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  59 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  60 + Assert.assertNotNull(savedTenantProfile);
  61 + Assert.assertNotNull(savedTenantProfile.getId());
  62 + Assert.assertTrue(savedTenantProfile.getCreatedTime() > 0);
  63 + Assert.assertEquals(tenantProfile.getName(), savedTenantProfile.getName());
  64 + Assert.assertEquals(tenantProfile.getDescription(), savedTenantProfile.getDescription());
  65 + Assert.assertEquals(tenantProfile.getProfileData(), savedTenantProfile.getProfileData());
  66 + Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault());
  67 + Assert.assertEquals(tenantProfile.isIsolatedTbCore(), savedTenantProfile.isIsolatedTbCore());
  68 + Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine());
  69 +
  70 + savedTenantProfile.setName("New tenant profile");
  71 + doPost("/api/tenantProfile", savedTenantProfile, TenantProfile.class);
  72 + TenantProfile foundTenantProfile = doGet("/api/tenantProfile/"+savedTenantProfile.getId().getId().toString(), TenantProfile.class);
  73 + Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName());
  74 + }
  75 +
  76 + @Test
  77 + public void testFindTenantProfileById() throws Exception {
  78 + loginSysAdmin();
  79 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  80 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  81 + TenantProfile foundTenantProfile = doGet("/api/tenantProfile/"+savedTenantProfile.getId().getId().toString(), TenantProfile.class);
  82 + Assert.assertNotNull(foundTenantProfile);
  83 + Assert.assertEquals(savedTenantProfile, foundTenantProfile);
  84 + }
  85 +
  86 + @Test
  87 + public void testFindTenantProfileInfoById() throws Exception {
  88 + loginSysAdmin();
  89 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  90 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  91 + EntityInfo foundTenantProfileInfo = doGet("/api/tenantProfileInfo/"+savedTenantProfile.getId().getId().toString(), EntityInfo.class);
  92 + Assert.assertNotNull(foundTenantProfileInfo);
  93 + Assert.assertEquals(savedTenantProfile.getId(), foundTenantProfileInfo.getId());
  94 + Assert.assertEquals(savedTenantProfile.getName(), foundTenantProfileInfo.getName());
  95 + }
  96 +
  97 + @Test
  98 + public void testFindDefaultTenantProfileInfo() throws Exception {
  99 + loginSysAdmin();
  100 + EntityInfo foundDefaultTenantProfile = doGet("/api/tenantProfileInfo/default", EntityInfo.class);
  101 + Assert.assertNotNull(foundDefaultTenantProfile);
  102 + Assert.assertEquals("Default", foundDefaultTenantProfile.getName());
  103 + }
  104 +
  105 + @Test
  106 + public void testSetDefaultTenantProfile() throws Exception {
  107 + loginSysAdmin();
  108 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile 1");
  109 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  110 + TenantProfile defaultTenantProfile = doPost("/api/tenantProfile/"+savedTenantProfile.getId().getId().toString()+"/default", null, TenantProfile.class);
  111 + Assert.assertNotNull(defaultTenantProfile);
  112 + EntityInfo foundDefaultTenantProfile = doGet("/api/tenantProfileInfo/default", EntityInfo.class);
  113 + Assert.assertNotNull(foundDefaultTenantProfile);
  114 + Assert.assertEquals(savedTenantProfile.getName(), foundDefaultTenantProfile.getName());
  115 + Assert.assertEquals(savedTenantProfile.getId(), foundDefaultTenantProfile.getId());
  116 + }
  117 +
  118 + @Test
  119 + public void testSaveTenantProfileWithEmptyName() throws Exception {
  120 + loginSysAdmin();
  121 + TenantProfile tenantProfile = new TenantProfile();
  122 + doPost("/api/tenantProfile", tenantProfile).andExpect(status().isBadRequest())
  123 + .andExpect(statusReason(containsString("Tenant profile name should be specified")));
  124 + }
  125 +
  126 + @Test
  127 + public void testSaveTenantProfileWithSameName() throws Exception {
  128 + loginSysAdmin();
  129 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  130 + doPost("/api/tenantProfile", tenantProfile).andExpect(status().isOk());
  131 + TenantProfile tenantProfile2 = this.createTenantProfile("Tenant Profile");
  132 + doPost("/api/tenantProfile", tenantProfile2).andExpect(status().isBadRequest())
  133 + .andExpect(statusReason(containsString("Tenant profile with such name already exists")));
  134 + }
  135 +
  136 + @Test
  137 + public void testSaveSameTenantProfileWithDifferentIsolatedTbRuleEngine() throws Exception {
  138 + loginSysAdmin();
  139 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  140 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  141 + savedTenantProfile.setIsolatedTbRuleEngine(true);
  142 + doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest())
  143 + .andExpect(statusReason(containsString("Can't update isolatedTbRuleEngine property")));
  144 + }
  145 +
  146 + @Test
  147 + public void testSaveSameTenantProfileWithDifferentIsolatedTbCore() throws Exception {
  148 + loginSysAdmin();
  149 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  150 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  151 + savedTenantProfile.setIsolatedTbCore(true);
  152 + doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest())
  153 + .andExpect(statusReason(containsString("Can't update isolatedTbCore property")));
  154 + }
  155 +
  156 + @Test
  157 + public void testDeleteTenantProfileWithExistingTenant() throws Exception {
  158 + loginSysAdmin();
  159 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  160 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  161 +
  162 + Tenant tenant = new Tenant();
  163 + tenant.setTitle("My tenant with tenant profile");
  164 + tenant.setTenantProfileId(savedTenantProfile.getId());
  165 + Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  166 +
  167 + doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
  168 + .andExpect(status().isBadRequest())
  169 + .andExpect(statusReason(containsString("The tenant profile referenced by the tenants cannot be deleted")));
  170 +
  171 + doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
  172 + .andExpect(status().isOk());
  173 + }
  174 +
  175 + @Test
  176 + public void testDeleteTenantProfile() throws Exception {
  177 + loginSysAdmin();
  178 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile");
  179 + TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class);
  180 +
  181 + doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
  182 + .andExpect(status().isOk());
  183 +
  184 + doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString())
  185 + .andExpect(status().isNotFound());
  186 + }
  187 +
  188 + @Test
  189 + public void testFindTenantProfiles() throws Exception {
  190 + loginSysAdmin();
  191 + List<TenantProfile> tenantProfiles = new ArrayList<>();
  192 + PageLink pageLink = new PageLink(17);
  193 + PageData<TenantProfile> pageData = doGetTypedWithPageLink("/api/tenantProfiles?",
  194 + new TypeReference<PageData<TenantProfile>>(){}, pageLink);
  195 + Assert.assertFalse(pageData.hasNext());
  196 + Assert.assertEquals(1, pageData.getTotalElements());
  197 + tenantProfiles.addAll(pageData.getData());
  198 +
  199 + for (int i=0;i<28;i++) {
  200 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"+i);
  201 + tenantProfiles.add(doPost("/api/tenantProfile", tenantProfile, TenantProfile.class));
  202 + }
  203 +
  204 + List<TenantProfile> loadedTenantProfiles = new ArrayList<>();
  205 + pageLink = new PageLink(17);
  206 + do {
  207 + pageData = doGetTypedWithPageLink("/api/tenantProfiles?",
  208 + new TypeReference<PageData<TenantProfile>>(){}, pageLink);
  209 + loadedTenantProfiles.addAll(pageData.getData());
  210 + if (pageData.hasNext()) {
  211 + pageLink = pageLink.nextPageLink();
  212 + }
  213 + } while (pageData.hasNext());
  214 +
  215 + Collections.sort(tenantProfiles, idComparator);
  216 + Collections.sort(loadedTenantProfiles, idComparator);
  217 +
  218 + Assert.assertEquals(tenantProfiles, loadedTenantProfiles);
  219 +
  220 + for (TenantProfile tenantProfile : loadedTenantProfiles) {
  221 + if (!tenantProfile.isDefault()) {
  222 + doDelete("/api/tenantProfile/" + tenantProfile.getId().getId().toString())
  223 + .andExpect(status().isOk());
  224 + }
  225 + }
  226 +
  227 + pageLink = new PageLink(17);
  228 + pageData = doGetTypedWithPageLink("/api/tenantProfiles?",
  229 + new TypeReference<PageData<TenantProfile>>(){}, pageLink);
  230 + Assert.assertFalse(pageData.hasNext());
  231 + Assert.assertEquals(1, pageData.getTotalElements());
  232 + }
  233 +
  234 + @Test
  235 + public void testFindTenantProfileInfos() throws Exception {
  236 + loginSysAdmin();
  237 + List<TenantProfile> tenantProfiles = new ArrayList<>();
  238 + PageLink pageLink = new PageLink(17);
  239 + PageData<TenantProfile> tenantProfilePageData = doGetTypedWithPageLink("/api/tenantProfiles?",
  240 + new TypeReference<PageData<TenantProfile>>(){}, pageLink);
  241 + Assert.assertFalse(tenantProfilePageData.hasNext());
  242 + Assert.assertEquals(1, tenantProfilePageData.getTotalElements());
  243 + tenantProfiles.addAll(tenantProfilePageData.getData());
  244 +
  245 + for (int i=0;i<28;i++) {
  246 + TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"+i);
  247 + tenantProfiles.add(doPost("/api/tenantProfile", tenantProfile, TenantProfile.class));
  248 + }
  249 +
  250 + List<EntityInfo> loadedTenantProfileInfos = new ArrayList<>();
  251 + pageLink = new PageLink(17);
  252 + PageData<EntityInfo> pageData;
  253 + do {
  254 + pageData = doGetTypedWithPageLink("/api/tenantProfileInfos?",
  255 + new TypeReference<PageData<EntityInfo>>(){}, pageLink);
  256 + loadedTenantProfileInfos.addAll(pageData.getData());
  257 + if (pageData.hasNext()) {
  258 + pageLink = pageLink.nextPageLink();
  259 + }
  260 + } while (pageData.hasNext());
  261 +
  262 + Collections.sort(tenantProfiles, idComparator);
  263 + Collections.sort(loadedTenantProfileInfos, tenantProfileInfoIdComparator);
  264 +
  265 + List<EntityInfo> tenantProfileInfos = tenantProfiles.stream().map(tenantProfile -> new EntityInfo(tenantProfile.getId(),
  266 + tenantProfile.getName())).collect(Collectors.toList());
  267 +
  268 + Assert.assertEquals(tenantProfileInfos, loadedTenantProfileInfos);
  269 +
  270 + for (TenantProfile tenantProfile : tenantProfiles) {
  271 + if (!tenantProfile.isDefault()) {
  272 + doDelete("/api/tenantProfile/" + tenantProfile.getId().getId().toString())
  273 + .andExpect(status().isOk());
  274 + }
  275 + }
  276 +
  277 + pageLink = new PageLink(17);
  278 + pageData = doGetTypedWithPageLink("/api/tenantProfileInfos?",
  279 + new TypeReference<PageData<EntityInfo>>(){}, pageLink);
  280 + Assert.assertFalse(pageData.hasNext());
  281 + Assert.assertEquals(1, pageData.getTotalElements());
  282 + }
  283 +
  284 + private TenantProfile createTenantProfile(String name) {
  285 + TenantProfile tenantProfile = new TenantProfile();
  286 + tenantProfile.setName(name);
  287 + tenantProfile.setDescription(name + " Test");
  288 + tenantProfile.setProfileData(new TenantProfileData());
  289 + tenantProfile.setDefault(false);
  290 + tenantProfile.setIsolatedTbCore(false);
  291 + tenantProfile.setIsolatedTbRuleEngine(false);
  292 + return tenantProfile;
  293 + }
  294 +}
@@ -34,7 +34,7 @@ public class ControllerSqlTestSuite { @@ -34,7 +34,7 @@ public class ControllerSqlTestSuite {
34 34
35 @ClassRule 35 @ClassRule
36 public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 36 public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
37 - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"), 37 + Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"),
38 "sql/hsql/drop-all-tables.sql", 38 "sql/hsql/drop-all-tables.sql",
39 "sql-test.properties"); 39 "sql-test.properties");
40 40
  1 +/**
  2 + * Copyright © 2016-2020 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.sql;
  17 +
  18 +import org.thingsboard.server.controller.BaseDeviceProfileControllerTest;
  19 +import org.thingsboard.server.dao.service.DaoSqlTest;
  20 +
  21 +@DaoSqlTest
  22 +public class DeviceProfileControllerSqlTest extends BaseDeviceProfileControllerTest {
  23 +}
  1 +/**
  2 + * Copyright © 2016-2020 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.sql;
  17 +
  18 +import org.thingsboard.server.controller.BaseTenantProfileControllerTest;
  19 +import org.thingsboard.server.dao.service.DaoSqlTest;
  20 +
  21 +@DaoSqlTest
  22 +public class TenantProfileControllerSqlTest extends BaseTenantProfileControllerTest {
  23 +}
  1 +/**
  2 + * Copyright © 2016-2020 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.mqtt;
  17 +
  18 +import com.fasterxml.jackson.databind.node.ObjectNode;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  21 +import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
  22 +import org.eclipse.paho.client.mqttv3.MqttException;
  23 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  24 +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
  25 +import org.junit.Assert;
  26 +import org.springframework.util.StringUtils;
  27 +import org.thingsboard.server.common.data.Device;
  28 +import org.thingsboard.server.common.data.DeviceProfile;
  29 +import org.thingsboard.server.common.data.DeviceProfileType;
  30 +import org.thingsboard.server.common.data.DeviceTransportType;
  31 +import org.thingsboard.server.common.data.Tenant;
  32 +import org.thingsboard.server.common.data.TransportPayloadType;
  33 +import org.thingsboard.server.common.data.User;
  34 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
  35 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
  36 +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
  37 +import org.thingsboard.server.common.data.security.Authority;
  38 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  39 +import org.thingsboard.server.controller.AbstractControllerTest;
  40 +import org.thingsboard.server.gen.transport.TransportProtos;
  41 +
  42 +import java.util.ArrayList;
  43 +import java.util.List;
  44 +import java.util.concurrent.atomic.AtomicInteger;
  45 +
  46 +import static org.junit.Assert.assertEquals;
  47 +import static org.junit.Assert.assertNotNull;
  48 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  49 +
  50 +@Slf4j
  51 +public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest {
  52 +
  53 + protected static final String MQTT_URL = "tcp://localhost:1883";
  54 +
  55 + private static final AtomicInteger atomicInteger = new AtomicInteger(2);
  56 +
  57 + protected Tenant savedTenant;
  58 + protected User tenantAdmin;
  59 +
  60 + protected Device savedDevice;
  61 + protected String accessToken;
  62 +
  63 + protected Device savedGateway;
  64 + protected String gatewayAccessToken;
  65 +
  66 + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception {
  67 + loginSysAdmin();
  68 +
  69 + Tenant tenant = new Tenant();
  70 + tenant.setTitle("My tenant");
  71 + savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  72 + Assert.assertNotNull(savedTenant);
  73 +
  74 + tenantAdmin = new User();
  75 + tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
  76 + tenantAdmin.setTenantId(savedTenant.getId());
  77 + tenantAdmin.setEmail("tenant" + atomicInteger.getAndIncrement() + "@thingsboard.org");
  78 + tenantAdmin.setFirstName("Joe");
  79 + tenantAdmin.setLastName("Downs");
  80 +
  81 + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
  82 +
  83 + Device device = new Device();
  84 + device.setName(deviceName);
  85 + device.setType("default");
  86 +
  87 + Device gateway = new Device();
  88 + gateway.setName(gatewayName);
  89 + gateway.setType("default");
  90 + ObjectNode additionalInfo = mapper.createObjectNode();
  91 + additionalInfo.put("gateway", true);
  92 + gateway.setAdditionalInfo(additionalInfo);
  93 +
  94 + if (payloadType != null) {
  95 + DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic);
  96 + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class);
  97 + device.setType(savedDeviceProfile.getName());
  98 + device.setDeviceProfileId(savedDeviceProfile.getId());
  99 + gateway.setType(savedDeviceProfile.getName());
  100 + gateway.setDeviceProfileId(savedDeviceProfile.getId());
  101 + }
  102 +
  103 + savedDevice = doPost("/api/device", device, Device.class);
  104 +
  105 + DeviceCredentials deviceCredentials =
  106 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
  107 +
  108 + savedGateway = doPost("/api/device", gateway, Device.class);
  109 +
  110 + DeviceCredentials gatewayCredentials =
  111 + doGet("/api/device/" + savedGateway.getId().getId().toString() + "/credentials", DeviceCredentials.class);
  112 +
  113 + assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
  114 + accessToken = deviceCredentials.getCredentialsId();
  115 + assertNotNull(accessToken);
  116 +
  117 + assertEquals(savedGateway.getId(), gatewayCredentials.getDeviceId());
  118 + gatewayAccessToken = gatewayCredentials.getCredentialsId();
  119 + assertNotNull(gatewayAccessToken);
  120 +
  121 + }
  122 +
  123 + protected void processAfterTest() throws Exception {
  124 + loginSysAdmin();
  125 + if (savedTenant != null) {
  126 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk());
  127 + }
  128 + }
  129 +
  130 + protected MqttAsyncClient getMqttAsyncClient(String accessToken) throws MqttException {
  131 + String clientId = MqttAsyncClient.generateClientId();
  132 + MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
  133 +
  134 + MqttConnectOptions options = new MqttConnectOptions();
  135 + options.setUserName(accessToken);
  136 + client.connect(options).waitForCompletion();
  137 + return client;
  138 + }
  139 +
  140 + protected void publishMqttMsg(MqttAsyncClient client, byte[] payload, String topic) throws MqttException {
  141 + MqttMessage message = new MqttMessage();
  142 + message.setPayload(payload);
  143 + client.publish(topic, message);
  144 + }
  145 +
  146 + protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) {
  147 + List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>();
  148 + TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V);
  149 + TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V);
  150 + TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "3.0", TransportProtos.KeyValueType.DOUBLE_V);
  151 + TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "4", TransportProtos.KeyValueType.LONG_V);
  152 + TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V);
  153 + keyValueProtos.add(strKeyValueProto);
  154 + keyValueProtos.add(boolKeyValueProto);
  155 + keyValueProtos.add(dblKeyValueProto);
  156 + keyValueProtos.add(longKeyValueProto);
  157 + keyValueProtos.add(jsonKeyValueProto);
  158 + return keyValueProtos;
  159 + }
  160 +
  161 + protected TransportProtos.KeyValueProto getKeyValueProto(String key, String strValue, TransportProtos.KeyValueType type) {
  162 + TransportProtos.KeyValueProto.Builder keyValueProtoBuilder = TransportProtos.KeyValueProto.newBuilder();
  163 + keyValueProtoBuilder.setKey(key);
  164 + keyValueProtoBuilder.setType(type);
  165 + switch (type) {
  166 + case BOOLEAN_V:
  167 + keyValueProtoBuilder.setBoolV(Boolean.parseBoolean(strValue));
  168 + break;
  169 + case LONG_V:
  170 + keyValueProtoBuilder.setLongV(Long.parseLong(strValue));
  171 + break;
  172 + case DOUBLE_V:
  173 + keyValueProtoBuilder.setDoubleV(Double.parseDouble(strValue));
  174 + break;
  175 + case STRING_V:
  176 + keyValueProtoBuilder.setStringV(strValue);
  177 + break;
  178 + case JSON_V:
  179 + keyValueProtoBuilder.setJsonV(strValue);
  180 + break;
  181 + }
  182 + return keyValueProtoBuilder.build();
  183 + }
  184 +
  185 + protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType, String telemetryTopic, String attributesTopic) {
  186 + DeviceProfile deviceProfile = new DeviceProfile();
  187 + deviceProfile.setName(transportPayloadType.name());
  188 + deviceProfile.setType(DeviceProfileType.DEFAULT);
  189 + deviceProfile.setTransportType(DeviceTransportType.MQTT);
  190 + deviceProfile.setDescription(transportPayloadType.name() + " Test");
  191 + DeviceProfileData deviceProfileData = new DeviceProfileData();
  192 + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
  193 + MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration();
  194 + transportConfiguration.setTransportPayloadType(transportPayloadType);
  195 + if (!StringUtils.isEmpty(telemetryTopic)) {
  196 + transportConfiguration.setDeviceTelemetryTopic(telemetryTopic);
  197 + }
  198 + if (!StringUtils.isEmpty(attributesTopic)) {
  199 + transportConfiguration.setDeviceAttributesTopic(attributesTopic);
  200 + }
  201 + deviceProfileData.setTransportConfiguration(transportConfiguration);
  202 + deviceProfileData.setConfiguration(configuration);
  203 + deviceProfile.setProfileData(deviceProfileData);
  204 + deviceProfile.setDefault(false);
  205 + deviceProfile.setDefaultRuleChainId(null);
  206 + return deviceProfile;
  207 + }
  208 +
  209 + protected TransportProtos.PostAttributeMsg getPostAttributeMsg(List<String> expectedKeys) {
  210 + List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys);
  211 + TransportProtos.PostAttributeMsg.Builder builder = TransportProtos.PostAttributeMsg.newBuilder();
  212 + builder.addAllKv(kvProtos);
  213 + return builder.build();
  214 + }
  215 +
  216 +}
@@ -33,7 +33,7 @@ public class MqttNoSqlTestSuite { @@ -33,7 +33,7 @@ public class MqttNoSqlTestSuite {
33 33
34 @ClassRule 34 @ClassRule
35 public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 35 public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
36 - Arrays.asList("sql/schema-entities-hsql.sql", "sql/system-data.sql"), 36 + Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"),
37 "sql/hsql/drop-all-tables.sql", 37 "sql/hsql/drop-all-tables.sql",
38 "nosql-test.properties"); 38 "nosql-test.properties");
39 39
@@ -27,13 +27,17 @@ import java.util.Arrays; @@ -27,13 +27,17 @@ import java.util.Arrays;
27 @RunWith(ClasspathSuite.class) 27 @RunWith(ClasspathSuite.class)
28 @ClasspathSuite.ClassnameFilters({ 28 @ClasspathSuite.ClassnameFilters({
29 "org.thingsboard.server.mqtt.rpc.sql.*Test", 29 "org.thingsboard.server.mqtt.rpc.sql.*Test",
30 - "org.thingsboard.server.mqtt.telemetry.sql.*Test" 30 + "org.thingsboard.server.mqtt.telemetry.timeseries.sql.*Test",
  31 + "org.thingsboard.server.mqtt.telemetry.attributes.sql.*Test",
  32 + "org.thingsboard.server.mqtt.attributes.updates.sql.*Test",
  33 + "org.thingsboard.server.mqtt.attributes.request.sql.*Test",
  34 + "org.thingsboard.server.mqtt.claim.sql.*Test"
31 }) 35 })
32 public class MqttSqlTestSuite { 36 public class MqttSqlTestSuite {
33 37
34 @ClassRule 38 @ClassRule
35 public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 39 public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
36 - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), 40 + Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"),
37 "sql/hsql/drop-all-tables.sql", 41 "sql/hsql/drop-all-tables.sql",
38 "sql-test.properties"); 42 "sql-test.properties");
39 43
  1 +/**
  2 + * Copyright © 2016-2020 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.mqtt.attributes;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
  20 +import org.eclipse.paho.client.mqttv3.MqttCallback;
  21 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  22 +import org.thingsboard.server.common.data.TransportPayloadType;
  23 +import org.thingsboard.server.gen.transport.TransportProtos;
  24 +import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest;
  25 +
  26 +import java.util.ArrayList;
  27 +import java.util.List;
  28 +import java.util.concurrent.CountDownLatch;
  29 +
  30 +@Slf4j
  31 +public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest {
  32 +
  33 + protected static final String POST_ATTRIBUTES_PAYLOAD = "{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73," +
  34 + "\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}";
  35 +
  36 + protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception {
  37 + super.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic);
  38 + }
  39 +
  40 + protected void processAfterTest() throws Exception {
  41 + super.processAfterTest();
  42 + }
  43 +
  44 + protected List<TransportProtos.TsKvProto> getTsKvProtoList() {
  45 + TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("attribute1", "value1", TransportProtos.KeyValueType.STRING_V);
  46 + TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("attribute2", "true", TransportProtos.KeyValueType.BOOLEAN_V);
  47 + TransportProtos.TsKvProto tsKvProtoAttribute3 = getTsKvProto("attribute3", "42.0", TransportProtos.KeyValueType.DOUBLE_V);
  48 + TransportProtos.TsKvProto tsKvProtoAttribute4 = getTsKvProto("attribute4", "73", TransportProtos.KeyValueType.LONG_V);
  49 + TransportProtos.TsKvProto tsKvProtoAttribute5 = getTsKvProto("attribute5", "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", TransportProtos.KeyValueType.JSON_V);
  50 + List<TransportProtos.TsKvProto> tsKvProtoList = new ArrayList<>();
  51 + tsKvProtoList.add(tsKvProtoAttribute1);
  52 + tsKvProtoList.add(tsKvProtoAttribute2);
  53 + tsKvProtoList.add(tsKvProtoAttribute3);
  54 + tsKvProtoList.add(tsKvProtoAttribute4);
  55 + tsKvProtoList.add(tsKvProtoAttribute5);
  56 + return tsKvProtoList;
  57 + }
  58 +
  59 +
  60 + protected TransportProtos.TsKvProto getTsKvProto(String key, String value, TransportProtos.KeyValueType keyValueType) {
  61 + TransportProtos.TsKvProto.Builder tsKvProtoBuilder = TransportProtos.TsKvProto.newBuilder();
  62 + TransportProtos.KeyValueProto keyValueProto = getKeyValueProto(key, value, keyValueType);
  63 + tsKvProtoBuilder.setKv(keyValueProto);
  64 + return tsKvProtoBuilder.build();
  65 + }
  66 +
  67 + protected TestMqttCallback getTestMqttCallback() {
  68 + CountDownLatch latch = new CountDownLatch(1);
  69 + return new TestMqttCallback(latch);
  70 + }
  71 +
  72 + protected static class TestMqttCallback implements MqttCallback {
  73 +
  74 + private final CountDownLatch latch;
  75 + private Integer qoS;
  76 + private byte[] payloadBytes;
  77 +
  78 + TestMqttCallback(CountDownLatch latch) {
  79 + this.latch = latch;
  80 + }
  81 +
  82 + public int getQoS() {
  83 + return qoS;
  84 + }
  85 +
  86 + public byte[] getPayloadBytes() {
  87 + return payloadBytes;
  88 + }
  89 +
  90 + public CountDownLatch getLatch() {
  91 + return latch;
  92 + }
  93 +
  94 + @Override
  95 + public void connectionLost(Throwable throwable) {
  96 + }
  97 +
  98 + @Override
  99 + public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception {
  100 + qoS = mqttMessage.getQos();
  101 + payloadBytes = mqttMessage.getPayload();
  102 + latch.countDown();
  103 + }
  104 +
  105 + @Override
  106 + public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
  107 +
  108 + }
  109 + }
  110 +
  111 +}
  1 +/**
  2 + * Copyright © 2016-2020 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.mqtt.attributes.request;
  17 +
  18 +import com.google.protobuf.InvalidProtocolBufferException;
  19 +import io.netty.handler.codec.mqtt.MqttQoS;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
  22 +import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
  23 +import org.eclipse.paho.client.mqttv3.MqttCallback;
  24 +import org.eclipse.paho.client.mqttv3.MqttException;
  25 +import org.eclipse.paho.client.mqttv3.MqttMessage;
  26 +import org.junit.After;
  27 +import org.junit.Before;
  28 +import org.junit.Test;
  29 +import org.thingsboard.server.common.data.Device;
  30 +import org.thingsboard.server.common.data.device.profile.MqttTopics;
  31 +import org.thingsboard.server.dao.util.mapping.JacksonUtil;
  32 +import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest;
  33 +
  34 +import java.nio.charset.StandardCharsets;
  35 +import java.util.concurrent.CountDownLatch;
  36 +import java.util.concurrent.TimeUnit;
  37 +
  38 +import static org.junit.Assert.assertEquals;
  39 +import static org.junit.Assert.assertFalse;
  40 +import static org.junit.Assert.assertNotNull;
  41 +import static org.junit.Assert.assertTrue;
  42 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  43 +
  44 +@Slf4j
  45 +public abstract class AbstractMqttAttributesRequestIntegrationTest extends AbstractMqttAttributesIntegrationTest {
  46 +
  47 + @Before
  48 + public void beforeTest() throws Exception {
  49 + processBeforeTest("Test Request attribute values from the server", "Gateway Test Request attribute values from the server", null, null, null);
  50 + }
  51 +
  52 + @After
  53 + public void afterTest() throws Exception {
  54 + processAfterTest();
  55 + }
  56 +
  57 + @Test
  58 + public void testRequestAttributesValuesFromTheServer() throws Exception {
  59 + processTestRequestAttributesValuesFromTheServer();
  60 + }
  61 +
  62 + @Test
  63 + public void testRequestAttributesValuesFromTheServerGateway() throws Exception {
  64 + processTestGatewayRequestAttributesValuesFromTheServer();
  65 + }
  66 +
  67 + protected void processTestRequestAttributesValuesFromTheServer() throws Exception {
  68 +
  69 + MqttAsyncClient client = getMqttAsyncClient(accessToken);
  70 +
  71 + postAttributesAndSubscribeToTopic(savedDevice, client);
  72 +
  73 + Thread.sleep(1000);
  74 +
  75 + TestMqttCallback callback = getTestMqttCallback();
  76 + client.setCallback(callback);
  77 +
  78 + validateResponse(client, callback.getLatch(), callback);
  79 + }
  80 +
  81 + protected void processTestGatewayRequestAttributesValuesFromTheServer() throws Exception {
  82 +
  83 + MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
  84 +
  85 + postGatewayDeviceClientAttributes(client);
  86 +
  87 + Thread.sleep(1000);
  88 +
  89 + Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class);
  90 + assertNotNull(savedDevice);
  91 + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
  92 +
  93 + Thread.sleep(1000);
  94 +
  95 + client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE.value());
  96 +
  97 + TestMqttCallback clientAttributesCallback = getTestMqttCallback();
  98 + client.setCallback(clientAttributesCallback);
  99 + validateClientResponseGateway(client, clientAttributesCallback);
  100 +
  101 + TestMqttCallback sharedAttributesCallback = getTestMqttCallback();
  102 + client.setCallback(sharedAttributesCallback);
  103 + validateSharedResponseGateway(client, sharedAttributesCallback);
  104 + }
  105 +
  106 + protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception {
  107 + doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
  108 + client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(POST_ATTRIBUTES_PAYLOAD.getBytes()));
  109 + client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
  110 + }
  111 +
  112 + protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception {
  113 + String postClientAttributes = "{\"" + "Gateway Device Request Attributes" + "\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
  114 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(postClientAttributes.getBytes()));
  115 + }
  116 +
  117 + protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  118 + String keys = "attribute1,attribute2,attribute3,attribute4,attribute5";
  119 + String payloadStr = "{\"clientKeys\":\"" + keys + "\", \"sharedKeys\":\"" + keys + "\"}";
  120 + MqttMessage mqttMessage = new MqttMessage();
  121 + mqttMessage.setPayload(payloadStr.getBytes());
  122 + client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage);
  123 + latch.await(3, TimeUnit.SECONDS);
  124 + assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS());
  125 + String expectedRequestPayload = "{\"client\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}},\"shared\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
  126 + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
  127 + }
  128 +
  129 + protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  130 + String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": true, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}";
  131 + MqttMessage mqttMessage = new MqttMessage();
  132 + mqttMessage.setPayload(payloadStr.getBytes());
  133 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage);
  134 + callback.getLatch().await(3, TimeUnit.SECONDS);
  135 + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
  136 + String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
  137 + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
  138 + }
  139 +
  140 + protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException {
  141 + String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": false, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}";
  142 + MqttMessage mqttMessage = new MqttMessage();
  143 + mqttMessage.setPayload(payloadStr.getBytes());
  144 + client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage);
  145 + callback.getLatch().await(3, TimeUnit.SECONDS);
  146 + assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS());
  147 + String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}";
  148 + assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8)));
  149 + }
  150 +}
  1 +/**
  2 + * Copyright © 2016-2020 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.mqtt.attributes.request;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.junit.After;
  20 +import org.junit.Before;
  21 +import org.junit.Ignore;
  22 +import org.junit.Test;
  23 +import org.thingsboard.server.common.data.TransportPayloadType;
  24 +
  25 +import static org.junit.Assert.assertEquals;
  26 +import static org.junit.Assert.assertNotNull;
  27 +import static org.junit.Assert.assertTrue;
  28 +
  29 +@Slf4j
  30 +public abstract class AbstractMqttAttributesRequestJsonIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest {
  31 +
  32 + @Before
  33 + public void beforeTest() throws Exception {
  34 + processBeforeTest("Test Request attribute values from the server json", "Gateway Test Request attribute values from the server json", TransportPayloadType.JSON, null, null);
  35 + }
  36 +
  37 + @After
  38 + public void afterTest() throws Exception {
  39 + processAfterTest();
  40 + }
  41 +
  42 + @Test
  43 + public void testRequestAttributesValuesFromTheServer() throws Exception {
  44 + processTestRequestAttributesValuesFromTheServer();
  45 + }
  46 +
  47 + @Test
  48 + public void testRequestAttributesValuesFromTheServerGateway() throws Exception {
  49 + processTestGatewayRequestAttributesValuesFromTheServer();
  50 + }
  51 +}