Commit b02fbe860c7cf4d5541f709aca68ca6ec9589c84
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(); |
application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleNodeActorMessageProcessor.java
@@ -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); |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -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 | +} |
application/src/main/java/org/thingsboard/server/service/profile/DefaultTbDeviceProfileCache.java
0 → 100644
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 | +} |
application/src/main/java/org/thingsboard/server/service/profile/TbDeviceProfileCache.java
0 → 100644
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 | +} |
application/src/main/java/org/thingsboard/server/service/queue/TbRuleNodeProfilerInfo.java
0 → 100644
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 |
application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
0 → 100644
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 | } |
application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java
0 → 100644
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 |
application/src/test/java/org/thingsboard/server/controller/sql/DeviceProfileControllerSqlTest.java
0 → 100644
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 | +} |
application/src/test/java/org/thingsboard/server/controller/sql/TenantProfileControllerSqlTest.java
0 → 100644
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 | +} |