Commit 6b3448d10fc07f95591eec94041f81d5214d9d23
Merge branch 'master' of github.com:thingsboard/thingsboard
Showing
79 changed files
with
2655 additions
and
514 deletions
Too many changes to show.
To preserve performance only 79 of 165 files are displayed.
... | ... | @@ -194,6 +194,14 @@ |
194 | 194 | <artifactId>javax.mail</artifactId> |
195 | 195 | </dependency> |
196 | 196 | <dependency> |
197 | + <groupId>com.twilio.sdk</groupId> | |
198 | + <artifactId>twilio</artifactId> | |
199 | + </dependency> | |
200 | + <dependency> | |
201 | + <groupId>com.amazonaws</groupId> | |
202 | + <artifactId>aws-java-sdk-sns</artifactId> | |
203 | + </dependency> | |
204 | + <dependency> | |
197 | 205 | <groupId>org.apache.curator</groupId> |
198 | 206 | <artifactId>curator-recipes</artifactId> |
199 | 207 | </dependency> | ... | ... |
... | ... | @@ -219,8 +219,8 @@ |
219 | 219 | "defaultPageSize": 10, |
220 | 220 | "defaultSortOrder": "-createdTime", |
221 | 221 | "enableSelectColumnDisplay": false, |
222 | - "enableStatusFilter": true, | |
223 | - "alarmsTitle": "Alarms" | |
222 | + "alarmsTitle": "Alarms", | |
223 | + "enableFilter": true | |
224 | 224 | }, |
225 | 225 | "title": "New Alarms table", |
226 | 226 | "dropShadow": true, |
... | ... | @@ -234,6 +234,9 @@ |
234 | 234 | "showLegend": false, |
235 | 235 | "alarmSource": { |
236 | 236 | "type": "entity", |
237 | + "name": "alarms", | |
238 | + "entityAliasId": "68a058e1-fdda-8482-715b-3ae4a488568e", | |
239 | + "filterId": null, | |
237 | 240 | "dataKeys": [ |
238 | 241 | { |
239 | 242 | "name": "createdTime", |
... | ... | @@ -275,9 +278,7 @@ |
275 | 278 | "settings": {}, |
276 | 279 | "_hash": 0.7977920750136249 |
277 | 280 | } |
278 | - ], | |
279 | - "entityAliasId": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813", | |
280 | - "name": "alarms" | |
281 | + ] | |
281 | 282 | }, |
282 | 283 | "alarmSearchStatus": "ANY", |
283 | 284 | "alarmsPollingInterval": 5, |
... | ... | @@ -1031,7 +1032,8 @@ |
1031 | 1032 | "markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;", |
1032 | 1033 | "useLabelFunction": true, |
1033 | 1034 | "provider": "openstreet-map", |
1034 | - "draggableMarker": true | |
1035 | + "draggableMarker": true, | |
1036 | + "editablePolygon": true | |
1035 | 1037 | }, |
1036 | 1038 | "title": "New Markers Placement - OpenStreetMap", |
1037 | 1039 | "dropShadow": true, |
... | ... | @@ -1062,61 +1064,6 @@ |
1062 | 1064 | "displayTimewindow": true |
1063 | 1065 | }, |
1064 | 1066 | "id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf" |
1065 | - }, | |
1066 | - "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": { | |
1067 | - "isSystemType": true, | |
1068 | - "bundleAlias": "input_widgets", | |
1069 | - "typeAlias": "update_double_timeseries", | |
1070 | - "type": "latest", | |
1071 | - "title": "New widget", | |
1072 | - "sizeX": 7.5, | |
1073 | - "sizeY": 3, | |
1074 | - "config": { | |
1075 | - "datasources": [ | |
1076 | - { | |
1077 | - "type": "entity", | |
1078 | - "name": null, | |
1079 | - "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547", | |
1080 | - "dataKeys": [ | |
1081 | - { | |
1082 | - "name": "temperature", | |
1083 | - "type": "timeseries", | |
1084 | - "label": "temperature", | |
1085 | - "color": "#2196f3", | |
1086 | - "settings": {}, | |
1087 | - "_hash": 0.4164505192982848 | |
1088 | - } | |
1089 | - ] | |
1090 | - } | |
1091 | - ], | |
1092 | - "timewindow": { | |
1093 | - "realtime": { | |
1094 | - "timewindowMs": 60000 | |
1095 | - } | |
1096 | - }, | |
1097 | - "showTitle": true, | |
1098 | - "backgroundColor": "#fff", | |
1099 | - "color": "rgba(0, 0, 0, 0.87)", | |
1100 | - "padding": "8px", | |
1101 | - "settings": { | |
1102 | - "showResultMessage": true, | |
1103 | - "showLabel": true | |
1104 | - }, | |
1105 | - "title": "New Update double timeseries", | |
1106 | - "dropShadow": true, | |
1107 | - "enableFullscreen": false, | |
1108 | - "widgetStyle": {}, | |
1109 | - "titleStyle": { | |
1110 | - "fontSize": "16px", | |
1111 | - "fontWeight": 400 | |
1112 | - }, | |
1113 | - "useDashboardTimewindow": true, | |
1114 | - "showLegend": false, | |
1115 | - "actions": {} | |
1116 | - }, | |
1117 | - "row": 0, | |
1118 | - "col": 0, | |
1119 | - "id": "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3" | |
1120 | 1067 | } |
1121 | 1068 | }, |
1122 | 1069 | "states": { |
... | ... | @@ -1215,12 +1162,6 @@ |
1215 | 1162 | "sizeY": 6, |
1216 | 1163 | "row": 6, |
1217 | 1164 | "col": 0 |
1218 | - }, | |
1219 | - "f4bb2f2d-0164-60bc-f3e8-9b1e7b5a59b3": { | |
1220 | - "sizeX": 7.5, | |
1221 | - "sizeY": 3, | |
1222 | - "row": 12, | |
1223 | - "col": 0 | |
1224 | 1165 | } |
1225 | 1166 | }, |
1226 | 1167 | "gridSettings": { |
... | ... | @@ -1257,16 +1198,6 @@ |
1257 | 1198 | "stateEntityParamName": null, |
1258 | 1199 | "defaultStateEntity": null |
1259 | 1200 | } |
1260 | - }, | |
1261 | - "ce27a9d0-93bf-b7a4-054d-d0369a8cf813": { | |
1262 | - "id": "ce27a9d0-93bf-b7a4-054d-d0369a8cf813", | |
1263 | - "alias": "Thermostat-alarm", | |
1264 | - "filter": { | |
1265 | - "type": "entityName", | |
1266 | - "resolveMultiple": false, | |
1267 | - "entityType": "ASSET", | |
1268 | - "entityNameFilter": "Thermostat Alarms" | |
1269 | - } | |
1270 | 1201 | } |
1271 | 1202 | }, |
1272 | 1203 | "timewindow": { |
... | ... | @@ -1301,7 +1232,8 @@ |
1301 | 1232 | "showDashboardTimewindow": true, |
1302 | 1233 | "showDashboardExport": true, |
1303 | 1234 | "toolbarAlwaysOpen": true |
1304 | - } | |
1235 | + }, | |
1236 | + "filters": {} | |
1305 | 1237 | }, |
1306 | 1238 | "name": "Thermostats" |
1307 | 1239 | } |
\ No newline at end of file | ... | ... |
1 | 1 | { |
2 | 2 | "ruleChain": { |
3 | - "additionalInfo": null, | |
3 | + "additionalInfo": { | |
4 | + "description": "" | |
5 | + }, | |
4 | 6 | "name": "Thermostat Alarms", |
5 | 7 | "firstRuleNodeId": null, |
6 | 8 | "root": false, |
... | ... | @@ -8,131 +10,126 @@ |
8 | 10 | "configuration": null |
9 | 11 | }, |
10 | 12 | "metadata": { |
11 | - "firstNodeIndex": 5, | |
13 | + "firstNodeIndex": 6, | |
12 | 14 | "nodes": [ |
13 | 15 | { |
14 | 16 | "additionalInfo": { |
15 | - "layoutX": 929, | |
16 | - "layoutY": 67 | |
17 | + "layoutX": 822, | |
18 | + "layoutY": 294 | |
17 | 19 | }, |
18 | - "type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode", | |
19 | - "name": "Create Temp Alarm", | |
20 | + "type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", | |
21 | + "name": "Save Timeseries", | |
20 | 22 | "debugMode": false, |
21 | 23 | "configuration": { |
22 | - "alarmType": "High Temperature", | |
23 | - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.temperature;\nreturn details;", | |
24 | - "severity": "MAJOR", | |
25 | - "propagate": true, | |
26 | - "useMessageAlarmData": false, | |
27 | - "relationTypes": [ | |
28 | - "ToAlarmPropagationAsset" | |
29 | - ] | |
24 | + "defaultTTL": 0 | |
30 | 25 | } |
31 | 26 | }, |
32 | 27 | { |
33 | 28 | "additionalInfo": { |
34 | - "layoutX": 930, | |
35 | - "layoutY": 201 | |
29 | + "description": null, | |
30 | + "layoutX": 824, | |
31 | + "layoutY": 221 | |
36 | 32 | }, |
37 | - "type": "org.thingsboard.rule.engine.action.TbClearAlarmNode", | |
38 | - "name": "Clear Temp Alarm", | |
33 | + "type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", | |
34 | + "name": "Save Client Attributes", | |
39 | 35 | "debugMode": false, |
40 | 36 | "configuration": { |
41 | - "alarmType": "High Temperature", | |
42 | - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;" | |
37 | + "scope": "SERVER_SCOPE", | |
38 | + "notifyDevice": null | |
43 | 39 | } |
44 | 40 | }, |
45 | 41 | { |
46 | 42 | "additionalInfo": { |
47 | - "layoutX": 930, | |
48 | - "layoutY": 131 | |
43 | + "layoutX": 494, | |
44 | + "layoutY": 309 | |
49 | 45 | }, |
50 | - "type": "org.thingsboard.rule.engine.action.TbCreateAlarmNode", | |
51 | - "name": "Create Humidity Alarm", | |
46 | + "type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", | |
47 | + "name": "Message Type Switch", | |
52 | 48 | "debugMode": false, |
53 | 49 | "configuration": { |
54 | - "alarmType": "Low Humidity", | |
55 | - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\ndetails.triggerValue = msg.humidity;\nreturn details;", | |
56 | - "severity": "MINOR", | |
57 | - "propagate": true, | |
58 | - "useMessageAlarmData": false, | |
59 | - "relationTypes": [ | |
60 | - "ToAlarmPropagationAsset" | |
61 | - ] | |
50 | + "version": 0 | |
62 | 51 | } |
63 | 52 | }, |
64 | 53 | { |
65 | 54 | "additionalInfo": { |
66 | - "layoutX": 929, | |
67 | - "layoutY": 275 | |
55 | + "layoutX": 824, | |
56 | + "layoutY": 383 | |
68 | 57 | }, |
69 | - "type": "org.thingsboard.rule.engine.action.TbClearAlarmNode", | |
70 | - "name": "Clear Humidity Alarm", | |
58 | + "type": "org.thingsboard.rule.engine.action.TbLogNode", | |
59 | + "name": "Log RPC from Device", | |
71 | 60 | "debugMode": false, |
72 | 61 | "configuration": { |
73 | - "alarmType": "Low Humidity", | |
74 | - "alarmDetailsBuildJs": "var details = {};\nif (metadata.prevAlarmDetails) {\n details = JSON.parse(metadata.prevAlarmDetails);\n}\nreturn details;" | |
62 | + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" | |
75 | 63 | } |
76 | 64 | }, |
77 | 65 | { |
78 | 66 | "additionalInfo": { |
79 | - "layoutX": 586, | |
80 | - "layoutY": 148 | |
67 | + "layoutX": 823, | |
68 | + "layoutY": 444 | |
81 | 69 | }, |
82 | - "type": "org.thingsboard.rule.engine.filter.TbJsSwitchNode", | |
83 | - "name": "Check Alarms", | |
70 | + "type": "org.thingsboard.rule.engine.action.TbLogNode", | |
71 | + "name": "Log Other", | |
84 | 72 | "debugMode": false, |
85 | 73 | "configuration": { |
86 | - "jsScript": "var relations = [];\nif(metadata[\"ss_alarmTemperature\"] === \"true\"){\n if(msg.temperature > metadata[\"ss_thresholdTemperature\"]){\n relations.push(\"NewTempAlarm\");\n } else {\n relations.push(\"ClearTempAlarm\");\n }\n}\nif(metadata[\"ss_alarmHumidity\"] === \"true\"){\n if(msg.humidity < metadata[\"ss_thresholdHumidity\"]){\n relations.push(\"NewHumidityAlarm\");\n } else {\n relations.push(\"ClearHumidityAlarm\");\n }\n}\n\nreturn relations;" | |
74 | + "jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" | |
87 | 75 | } |
88 | 76 | }, |
89 | 77 | { |
90 | 78 | "additionalInfo": { |
91 | - "layoutX": 321, | |
92 | - "layoutY": 149 | |
79 | + "layoutX": 822, | |
80 | + "layoutY": 507 | |
93 | 81 | }, |
94 | - "type": "org.thingsboard.rule.engine.metadata.TbGetAttributesNode", | |
95 | - "name": "Fetch Configuration", | |
82 | + "type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", | |
83 | + "name": "RPC Call Request", | |
96 | 84 | "debugMode": false, |
97 | 85 | "configuration": { |
98 | - "clientAttributeNames": [], | |
99 | - "sharedAttributeNames": [], | |
100 | - "serverAttributeNames": [ | |
101 | - "alarmTemperature", | |
102 | - "thresholdTemperature", | |
103 | - "alarmHumidity", | |
104 | - "thresholdHumidity" | |
105 | - ], | |
106 | - "latestTsKeyNames": [], | |
107 | - "tellFailureIfAbsent": false, | |
108 | - "getLatestValueWithTs": false | |
86 | + "timeoutInSeconds": 60 | |
87 | + } | |
88 | + }, | |
89 | + { | |
90 | + "additionalInfo": { | |
91 | + "description": "", | |
92 | + "layoutX": 209, | |
93 | + "layoutY": 307 | |
94 | + }, | |
95 | + "type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", | |
96 | + "name": "Device Profile Node", | |
97 | + "debugMode": false, | |
98 | + "configuration": { | |
99 | + "persistAlarmRulesState": false, | |
100 | + "fetchAlarmRulesStateOnStart": false | |
109 | 101 | } |
110 | 102 | } |
111 | 103 | ], |
112 | 104 | "connections": [ |
113 | 105 | { |
114 | - "fromIndex": 4, | |
115 | - "toIndex": 0, | |
116 | - "type": "NewTempAlarm" | |
106 | + "fromIndex": 2, | |
107 | + "toIndex": 4, | |
108 | + "type": "Other" | |
117 | 109 | }, |
118 | 110 | { |
119 | - "fromIndex": 4, | |
111 | + "fromIndex": 2, | |
120 | 112 | "toIndex": 1, |
121 | - "type": "ClearTempAlarm" | |
113 | + "type": "Post attributes" | |
122 | 114 | }, |
123 | 115 | { |
124 | - "fromIndex": 4, | |
125 | - "toIndex": 2, | |
126 | - "type": "NewHumidityAlarm" | |
116 | + "fromIndex": 2, | |
117 | + "toIndex": 0, | |
118 | + "type": "Post telemetry" | |
127 | 119 | }, |
128 | 120 | { |
129 | - "fromIndex": 4, | |
121 | + "fromIndex": 2, | |
130 | 122 | "toIndex": 3, |
131 | - "type": "ClearHumidityAlarm" | |
123 | + "type": "RPC Request from Device" | |
132 | 124 | }, |
133 | 125 | { |
134 | - "fromIndex": 5, | |
135 | - "toIndex": 4, | |
126 | + "fromIndex": 2, | |
127 | + "toIndex": 5, | |
128 | + "type": "RPC Request to Device" | |
129 | + }, | |
130 | + { | |
131 | + "fromIndex": 6, | |
132 | + "toIndex": 2, | |
136 | 133 | "type": "Success" |
137 | 134 | } |
138 | 135 | ], | ... | ... |
... | ... | @@ -32,6 +32,8 @@ import org.springframework.data.redis.core.RedisTemplate; |
32 | 32 | import org.springframework.scheduling.annotation.Scheduled; |
33 | 33 | import org.springframework.stereotype.Component; |
34 | 34 | import org.thingsboard.rule.engine.api.MailService; |
35 | +import org.thingsboard.rule.engine.api.SmsService; | |
36 | +import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; | |
35 | 37 | import org.thingsboard.server.actors.service.ActorService; |
36 | 38 | import org.thingsboard.server.actors.tenant.DebugTbRateLimits; |
37 | 39 | import org.thingsboard.server.common.data.DataConstants; |
... | ... | @@ -80,6 +82,7 @@ import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; |
80 | 82 | import org.thingsboard.server.service.script.JsExecutorService; |
81 | 83 | import org.thingsboard.server.service.script.JsInvokeService; |
82 | 84 | import org.thingsboard.server.service.session.DeviceSessionCacheService; |
85 | +import org.thingsboard.server.service.sms.SmsExecutorService; | |
83 | 86 | import org.thingsboard.server.service.state.DeviceStateService; |
84 | 87 | import org.thingsboard.server.service.telemetry.AlarmSubscriptionService; |
85 | 88 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
... | ... | @@ -230,6 +233,10 @@ public class ActorSystemContext { |
230 | 233 | |
231 | 234 | @Autowired |
232 | 235 | @Getter |
236 | + private SmsExecutorService smsExecutor; | |
237 | + | |
238 | + @Autowired | |
239 | + @Getter | |
233 | 240 | private DbCallbackExecutorService dbCallbackExecutor; |
234 | 241 | |
235 | 242 | @Autowired |
... | ... | @@ -246,6 +253,14 @@ public class ActorSystemContext { |
246 | 253 | |
247 | 254 | @Autowired |
248 | 255 | @Getter |
256 | + private SmsService smsService; | |
257 | + | |
258 | + @Autowired | |
259 | + @Getter | |
260 | + private SmsSenderFactory smsSenderFactory; | |
261 | + | |
262 | + @Autowired | |
263 | + @Getter | |
249 | 264 | private ClaimDevicesService claimDevicesService; |
250 | 265 | |
251 | 266 | @Autowired |
... | ... | @@ -325,6 +340,10 @@ public class ActorSystemContext { |
325 | 340 | @Getter |
326 | 341 | private boolean allowSystemMailService; |
327 | 342 | |
343 | + @Value("${actors.rule.allow_system_sms_service}") | |
344 | + @Getter | |
345 | + private boolean allowSystemSmsService; | |
346 | + | |
328 | 347 | @Value("${transport.sessions.inactivity_timeout}") |
329 | 348 | @Getter |
330 | 349 | private long sessionInactivityTimeout; | ... | ... |
... | ... | @@ -28,10 +28,13 @@ import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; |
28 | 28 | import org.thingsboard.rule.engine.api.RuleEngineRpcService; |
29 | 29 | import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; |
30 | 30 | import org.thingsboard.rule.engine.api.ScriptEngine; |
31 | +import org.thingsboard.rule.engine.api.SmsService; | |
31 | 32 | import org.thingsboard.rule.engine.api.TbContext; |
32 | 33 | import org.thingsboard.rule.engine.api.TbRelationTypes; |
34 | +import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; | |
33 | 35 | import org.thingsboard.server.actors.ActorSystemContext; |
34 | 36 | import org.thingsboard.server.actors.TbActorRef; |
37 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
35 | 38 | import org.thingsboard.server.common.data.Customer; |
36 | 39 | import org.thingsboard.server.common.data.DataConstants; |
37 | 40 | import org.thingsboard.server.common.data.Device; |
... | ... | @@ -303,6 +306,11 @@ class DefaultTbContext implements TbContext { |
303 | 306 | } |
304 | 307 | |
305 | 308 | @Override |
309 | + public ListeningExecutor getSmsExecutor() { | |
310 | + return mainCtx.getSmsExecutor(); | |
311 | + } | |
312 | + | |
313 | + @Override | |
306 | 314 | public ListeningExecutor getDbCallbackExecutor() { |
307 | 315 | return mainCtx.getDbCallbackExecutor(); |
308 | 316 | } |
... | ... | @@ -428,6 +436,20 @@ class DefaultTbContext implements TbContext { |
428 | 436 | } |
429 | 437 | |
430 | 438 | @Override |
439 | + public SmsService getSmsService() { | |
440 | + if (mainCtx.isAllowSystemSmsService()) { | |
441 | + return mainCtx.getSmsService(); | |
442 | + } else { | |
443 | + throw new RuntimeException("Access to System SMS Service is forbidden!"); | |
444 | + } | |
445 | + } | |
446 | + | |
447 | + @Override | |
448 | + public SmsSenderFactory getSmsSenderFactory() { | |
449 | + return mainCtx.getSmsSenderFactory(); | |
450 | + } | |
451 | + | |
452 | + @Override | |
431 | 453 | public RuleEngineRpcService getRpcService() { |
432 | 454 | return mainCtx.getTbRuleEngineDeviceRpcService(); |
433 | 455 | } | ... | ... |
... | ... | @@ -25,6 +25,8 @@ import org.springframework.web.bind.annotation.RequestMethod; |
25 | 25 | import org.springframework.web.bind.annotation.ResponseBody; |
26 | 26 | import org.springframework.web.bind.annotation.RestController; |
27 | 27 | import org.thingsboard.rule.engine.api.MailService; |
28 | +import org.thingsboard.rule.engine.api.SmsService; | |
29 | +import org.thingsboard.rule.engine.api.sms.config.TestSmsRequest; | |
28 | 30 | import org.thingsboard.server.common.data.AdminSettings; |
29 | 31 | import org.thingsboard.server.common.data.UpdateMessage; |
30 | 32 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
... | ... | @@ -46,6 +48,9 @@ public class AdminController extends BaseController { |
46 | 48 | private MailService mailService; |
47 | 49 | |
48 | 50 | @Autowired |
51 | + private SmsService smsService; | |
52 | + | |
53 | + @Autowired | |
49 | 54 | private AdminSettingsService adminSettingsService; |
50 | 55 | |
51 | 56 | @Autowired |
... | ... | @@ -80,6 +85,8 @@ public class AdminController extends BaseController { |
80 | 85 | if (adminSettings.getKey().equals("mail")) { |
81 | 86 | mailService.updateMailConfiguration(); |
82 | 87 | ((ObjectNode) adminSettings.getJsonValue()).put("password", ""); |
88 | + } else if (adminSettings.getKey().equals("sms")) { | |
89 | + smsService.updateSmsConfiguration(); | |
83 | 90 | } |
84 | 91 | return adminSettings; |
85 | 92 | } catch (Exception e) { |
... | ... | @@ -128,6 +135,17 @@ public class AdminController extends BaseController { |
128 | 135 | } |
129 | 136 | |
130 | 137 | @PreAuthorize("hasAuthority('SYS_ADMIN')") |
138 | + @RequestMapping(value = "/settings/testSms", method = RequestMethod.POST) | |
139 | + public void sendTestSms(@RequestBody TestSmsRequest testSmsRequest) throws ThingsboardException { | |
140 | + try { | |
141 | + accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); | |
142 | + smsService.sendTestSms(testSmsRequest); | |
143 | + } catch (Exception e) { | |
144 | + throw handleException(e); | |
145 | + } | |
146 | + } | |
147 | + | |
148 | + @PreAuthorize("hasAuthority('SYS_ADMIN')") | |
131 | 149 | @RequestMapping(value = "/updates", method = RequestMethod.GET) |
132 | 150 | @ResponseBody |
133 | 151 | public UpdateMessage checkUpdates() throws ThingsboardException { | ... | ... |
... | ... | @@ -118,6 +118,7 @@ public class DeviceController extends BaseController { |
118 | 118 | |
119 | 119 | Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); |
120 | 120 | |
121 | + tbClusterService.onDeviceChange(savedDevice, null); | |
121 | 122 | tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(), |
122 | 123 | savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); |
123 | 124 | tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), |
... | ... | @@ -150,6 +151,9 @@ public class DeviceController extends BaseController { |
150 | 151 | Device device = checkDeviceId(deviceId, Operation.DELETE); |
151 | 152 | deviceService.deleteDevice(getCurrentUser().getTenantId(), deviceId); |
152 | 153 | |
154 | + tbClusterService.onDeviceDeleted(device, null); | |
155 | + tbClusterService.onEntityStateChange(device.getTenantId(), deviceId, ComponentLifecycleEvent.DELETED); | |
156 | + | |
153 | 157 | logEntityAction(deviceId, device, |
154 | 158 | device.getCustomerId(), |
155 | 159 | ActionType.DELETED, null, strDeviceId); | ... | ... |
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
... | ... | @@ -17,21 +17,21 @@ package org.thingsboard.server.service.apiusage; |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.FutureCallback; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.apache.commons.lang3.StringUtils; | |
20 | 21 | import org.checkerframework.checker.nullness.qual.Nullable; |
21 | 22 | import org.springframework.beans.factory.annotation.Autowired; |
22 | 23 | import org.springframework.beans.factory.annotation.Value; |
23 | -import org.springframework.boot.context.event.ApplicationReadyEvent; | |
24 | 24 | import org.springframework.context.annotation.Lazy; |
25 | -import org.springframework.context.event.EventListener; | |
26 | -import org.springframework.core.annotation.Order; | |
27 | -import org.springframework.data.util.Pair; | |
28 | 25 | import org.springframework.stereotype.Service; |
26 | +import org.thingsboard.rule.engine.api.MailService; | |
29 | 27 | import org.thingsboard.server.common.data.ApiFeature; |
30 | 28 | import org.thingsboard.server.common.data.ApiUsageRecordKey; |
31 | 29 | import org.thingsboard.server.common.data.ApiUsageState; |
30 | +import org.thingsboard.server.common.data.ApiUsageStateMailMessage; | |
32 | 31 | import org.thingsboard.server.common.data.ApiUsageStateValue; |
33 | 32 | import org.thingsboard.server.common.data.Tenant; |
34 | 33 | import org.thingsboard.server.common.data.TenantProfile; |
34 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
35 | 35 | import org.thingsboard.server.common.data.id.ApiUsageStateId; |
36 | 36 | import org.thingsboard.server.common.data.id.TenantId; |
37 | 37 | import org.thingsboard.server.common.data.id.TenantProfileId; |
... | ... | @@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; |
45 | 45 | import org.thingsboard.server.common.msg.queue.ServiceType; |
46 | 46 | import org.thingsboard.server.common.msg.queue.TbCallback; |
47 | 47 | import org.thingsboard.server.common.msg.tools.SchedulerUtils; |
48 | +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
48 | 49 | import org.thingsboard.server.dao.tenant.TenantService; |
49 | 50 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
50 | 51 | import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; |
... | ... | @@ -54,14 +55,12 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
54 | 55 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
55 | 56 | import org.thingsboard.server.queue.discovery.PartitionService; |
56 | 57 | import org.thingsboard.server.queue.scheduler.SchedulerComponent; |
57 | -import org.thingsboard.server.queue.util.TbCoreComponent; | |
58 | -import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | |
59 | 58 | import org.thingsboard.server.service.queue.TbClusterService; |
60 | 59 | import org.thingsboard.server.service.telemetry.InternalTelemetryService; |
61 | 60 | |
62 | 61 | import javax.annotation.PostConstruct; |
62 | +import javax.annotation.PreDestroy; | |
63 | 63 | import java.util.ArrayList; |
64 | -import java.util.HashMap; | |
65 | 64 | import java.util.HashSet; |
66 | 65 | import java.util.List; |
67 | 66 | import java.util.Map; |
... | ... | @@ -69,6 +68,8 @@ import java.util.Set; |
69 | 68 | import java.util.UUID; |
70 | 69 | import java.util.concurrent.ConcurrentHashMap; |
71 | 70 | import java.util.concurrent.ExecutionException; |
71 | +import java.util.concurrent.ExecutorService; | |
72 | +import java.util.concurrent.Executors; | |
72 | 73 | import java.util.concurrent.TimeUnit; |
73 | 74 | import java.util.concurrent.locks.Lock; |
74 | 75 | import java.util.concurrent.locks.ReentrantLock; |
... | ... | @@ -94,6 +95,7 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { |
94 | 95 | private final ApiUsageStateService apiUsageStateService; |
95 | 96 | private final SchedulerComponent scheduler; |
96 | 97 | private final TbTenantProfileCache tenantProfileCache; |
98 | + private final MailService mailService; | |
97 | 99 | |
98 | 100 | @Lazy |
99 | 101 | @Autowired |
... | ... | @@ -112,13 +114,15 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { |
112 | 114 | |
113 | 115 | private final Lock updateLock = new ReentrantLock(); |
114 | 116 | |
117 | + private final ExecutorService mailExecutor; | |
118 | + | |
115 | 119 | public DefaultTbApiUsageStateService(TbClusterService clusterService, |
116 | 120 | PartitionService partitionService, |
117 | 121 | TenantService tenantService, |
118 | 122 | TimeseriesService tsService, |
119 | 123 | ApiUsageStateService apiUsageStateService, |
120 | 124 | SchedulerComponent scheduler, |
121 | - TbTenantProfileCache tenantProfileCache) { | |
125 | + TbTenantProfileCache tenantProfileCache, MailService mailService) { | |
122 | 126 | this.clusterService = clusterService; |
123 | 127 | this.partitionService = partitionService; |
124 | 128 | this.tenantService = tenantService; |
... | ... | @@ -126,6 +130,8 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { |
126 | 130 | this.apiUsageStateService = apiUsageStateService; |
127 | 131 | this.scheduler = scheduler; |
128 | 132 | this.tenantProfileCache = tenantProfileCache; |
133 | + this.mailService = mailService; | |
134 | + this.mailExecutor = Executors.newSingleThreadExecutor(); | |
129 | 135 | } |
130 | 136 | |
131 | 137 | @PostConstruct |
... | ... | @@ -286,7 +292,49 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { |
286 | 292 | List<TsKvEntry> stateTelemetry = new ArrayList<>(); |
287 | 293 | result.forEach(((apiFeature, aState) -> stateTelemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(apiFeature.getApiStateKey(), aState.name()))))); |
288 | 294 | tsWsService.saveAndNotifyInternal(state.getTenantId(), state.getApiUsageState().getId(), stateTelemetry, VOID_CALLBACK); |
289 | - //TODO: notify tenant admin via email! | |
295 | + | |
296 | + String email = tenantService.findTenantById(state.getTenantId()).getEmail(); | |
297 | + | |
298 | + if (StringUtils.isNotEmpty(email)) { | |
299 | + result.forEach((apiFeature, stateValue) -> { | |
300 | + mailExecutor.submit(() -> { | |
301 | + try { | |
302 | + mailService.sendApiFeatureStateEmail(apiFeature, stateValue, email, createStateMailMessage(state, apiFeature, stateValue)); | |
303 | + } catch (ThingsboardException e) { | |
304 | + log.warn("[{}] Can't send update of the API state to tenant with provided email [{}]", state.getTenantId(), email, e); | |
305 | + } | |
306 | + }); | |
307 | + }); | |
308 | + } else { | |
309 | + log.warn("[{}] Can't send update of the API state to tenant with empty email!", state.getTenantId()); | |
310 | + } | |
311 | + } | |
312 | + | |
313 | + private ApiUsageStateMailMessage createStateMailMessage(TenantApiUsageState state, ApiFeature apiFeature, ApiUsageStateValue stateValue) { | |
314 | + StateChecker checker = getStateChecker(stateValue); | |
315 | + for (ApiUsageRecordKey apiUsageRecordKey : ApiUsageRecordKey.getKeys(apiFeature)) { | |
316 | + long threshold = state.getProfileThreshold(apiUsageRecordKey); | |
317 | + long warnThreshold = state.getProfileWarnThreshold(apiUsageRecordKey); | |
318 | + long value = state.get(apiUsageRecordKey); | |
319 | + if (checker.check(threshold, warnThreshold, value)) { | |
320 | + return new ApiUsageStateMailMessage(apiUsageRecordKey, threshold, value); | |
321 | + } | |
322 | + } | |
323 | + return null; | |
324 | + } | |
325 | + | |
326 | + private StateChecker getStateChecker(ApiUsageStateValue stateValue) { | |
327 | + if (ApiUsageStateValue.ENABLED.equals(stateValue)) { | |
328 | + return (t, wt, v) -> true; | |
329 | + } else if (ApiUsageStateValue.WARNING.equals(stateValue)) { | |
330 | + return (t, wt, v) -> v < t && v >= wt; | |
331 | + } else { | |
332 | + return (t, wt, v) -> v >= t; | |
333 | + } | |
334 | + } | |
335 | + | |
336 | + private interface StateChecker { | |
337 | + boolean check(long threshold, long warnThreshold, long value); | |
290 | 338 | } |
291 | 339 | |
292 | 340 | private void checkStartOfNextCycle() { |
... | ... | @@ -367,4 +415,10 @@ public class DefaultTbApiUsageStateService implements TbApiUsageStateService { |
367 | 415 | } |
368 | 416 | } |
369 | 417 | |
418 | + @PreDestroy | |
419 | + private void destroy() { | |
420 | + if (mailExecutor != null) { | |
421 | + mailExecutor.shutdownNow(); | |
422 | + } | |
423 | + } | |
370 | 424 | } | ... | ... |
... | ... | @@ -125,6 +125,10 @@ public class TenantApiUsageState { |
125 | 125 | return apiUsageState.getDbStorageState(); |
126 | 126 | case JS: |
127 | 127 | return apiUsageState.getJsExecState(); |
128 | + case EMAIL: | |
129 | + return apiUsageState.getEmailExecState(); | |
130 | + case SMS: | |
131 | + return apiUsageState.getSmsExecState(); | |
128 | 132 | default: |
129 | 133 | return ApiUsageStateValue.ENABLED; |
130 | 134 | } |
... | ... | @@ -145,6 +149,12 @@ public class TenantApiUsageState { |
145 | 149 | case JS: |
146 | 150 | apiUsageState.setJsExecState(value); |
147 | 151 | break; |
152 | + case EMAIL: | |
153 | + apiUsageState.setEmailExecState(value); | |
154 | + break; | |
155 | + case SMS: | |
156 | + apiUsageState.setSmsExecState(value); | |
157 | + break; | |
148 | 158 | } |
149 | 159 | return !currentValue.equals(value); |
150 | 160 | } | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import com.google.common.util.concurrent.Futures; |
22 | 22 | import com.google.common.util.concurrent.ListenableFuture; |
23 | 23 | import com.google.common.util.concurrent.MoreExecutors; |
24 | 24 | import lombok.extern.slf4j.Slf4j; |
25 | +import org.apache.commons.lang.RandomStringUtils; | |
25 | 26 | import org.springframework.beans.factory.annotation.Autowired; |
26 | 27 | import org.springframework.stereotype.Service; |
27 | 28 | import org.springframework.util.StringUtils; |
... | ... | @@ -188,6 +189,11 @@ public class DeviceProvisionServiceImpl implements DeviceProvisionService { |
188 | 189 | Device device = deviceService.findDeviceByTenantIdAndName(profile.getTenantId(), provisionRequest.getDeviceName()); |
189 | 190 | try { |
190 | 191 | if (device == null) { |
192 | + if (StringUtils.isEmpty(provisionRequest.getDeviceName())) { | |
193 | + String newDeviceName = RandomStringUtils.randomAlphanumeric(20); | |
194 | + log.info("Device name not found in provision request. Generated name is: {}", newDeviceName); | |
195 | + provisionRequest.setDeviceName(newDeviceName); | |
196 | + } | |
191 | 197 | Device savedDevice = deviceService.saveDevice(provisionRequest, profile); |
192 | 198 | |
193 | 199 | deviceStateService.onDeviceAdded(savedDevice); | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -28,12 +28,21 @@ import org.thingsboard.server.common.data.Customer; |
28 | 28 | import org.thingsboard.server.common.data.DataConstants; |
29 | 29 | import org.thingsboard.server.common.data.Device; |
30 | 30 | import org.thingsboard.server.common.data.DeviceProfile; |
31 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | |
32 | +import org.thingsboard.server.common.data.DeviceProfileType; | |
33 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
31 | 34 | import org.thingsboard.server.common.data.Tenant; |
32 | 35 | import org.thingsboard.server.common.data.TenantProfile; |
33 | -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
34 | -import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | |
35 | 36 | import org.thingsboard.server.common.data.User; |
36 | -import org.thingsboard.server.common.data.asset.Asset; | |
37 | +import org.thingsboard.server.common.data.alarm.AlarmSeverity; | |
38 | +import org.thingsboard.server.common.data.device.profile.AlarmCondition; | |
39 | +import org.thingsboard.server.common.data.device.profile.AlarmRule; | |
40 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | |
41 | +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; | |
42 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; | |
43 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | |
44 | +import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; | |
45 | +import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec; | |
37 | 46 | import org.thingsboard.server.common.data.id.CustomerId; |
38 | 47 | import org.thingsboard.server.common.data.id.DeviceId; |
39 | 48 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
... | ... | @@ -42,19 +51,29 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
42 | 51 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
43 | 52 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
44 | 53 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
45 | -import org.thingsboard.server.common.data.relation.EntityRelation; | |
54 | +import org.thingsboard.server.common.data.page.PageLink; | |
55 | +import org.thingsboard.server.common.data.query.BooleanFilterPredicate; | |
56 | +import org.thingsboard.server.common.data.query.DynamicValue; | |
57 | +import org.thingsboard.server.common.data.query.DynamicValueSourceType; | |
58 | +import org.thingsboard.server.common.data.query.EntityKey; | |
59 | +import org.thingsboard.server.common.data.query.EntityKeyType; | |
60 | +import org.thingsboard.server.common.data.query.EntityKeyValueType; | |
61 | +import org.thingsboard.server.common.data.query.FilterPredicateValue; | |
62 | +import org.thingsboard.server.common.data.query.KeyFilter; | |
63 | +import org.thingsboard.server.common.data.query.NumericFilterPredicate; | |
46 | 64 | import org.thingsboard.server.common.data.security.Authority; |
47 | 65 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
48 | 66 | import org.thingsboard.server.common.data.security.UserCredentials; |
67 | +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
68 | +import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; | |
49 | 69 | import org.thingsboard.server.common.data.widget.WidgetsBundle; |
50 | -import org.thingsboard.server.dao.asset.AssetService; | |
51 | 70 | import org.thingsboard.server.dao.attributes.AttributesService; |
52 | 71 | import org.thingsboard.server.dao.customer.CustomerService; |
53 | 72 | import org.thingsboard.server.dao.device.DeviceCredentialsService; |
54 | 73 | import org.thingsboard.server.dao.device.DeviceProfileService; |
55 | 74 | import org.thingsboard.server.dao.device.DeviceService; |
56 | 75 | import org.thingsboard.server.dao.exception.DataValidationException; |
57 | -import org.thingsboard.server.dao.relation.RelationService; | |
76 | +import org.thingsboard.server.dao.rule.RuleChainService; | |
58 | 77 | import org.thingsboard.server.dao.settings.AdminSettingsService; |
59 | 78 | import org.thingsboard.server.dao.tenant.TenantProfileService; |
60 | 79 | import org.thingsboard.server.dao.tenant.TenantService; |
... | ... | @@ -62,6 +81,8 @@ import org.thingsboard.server.dao.user.UserService; |
62 | 81 | import org.thingsboard.server.dao.widget.WidgetsBundleService; |
63 | 82 | |
64 | 83 | import java.util.Arrays; |
84 | +import java.util.Collections; | |
85 | +import java.util.LinkedHashMap; | |
65 | 86 | |
66 | 87 | @Service |
67 | 88 | @Profile("install") |
... | ... | @@ -97,12 +118,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
97 | 118 | private CustomerService customerService; |
98 | 119 | |
99 | 120 | @Autowired |
100 | - private RelationService relationService; | |
101 | - | |
102 | - @Autowired | |
103 | - private AssetService assetService; | |
104 | - | |
105 | - @Autowired | |
106 | 121 | private DeviceService deviceService; |
107 | 122 | |
108 | 123 | @Autowired |
... | ... | @@ -114,6 +129,9 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
114 | 129 | @Autowired |
115 | 130 | private DeviceCredentialsService deviceCredentialsService; |
116 | 131 | |
132 | + @Autowired | |
133 | + private RuleChainService ruleChainService; | |
134 | + | |
117 | 135 | @Bean |
118 | 136 | protected BCryptPasswordEncoder passwordEncoder() { |
119 | 137 | return new BCryptPasswordEncoder(); |
... | ... | @@ -245,19 +263,133 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
245 | 263 | createDevice(demoTenant.getId(), null, defaultDeviceProfile.getId(), "Raspberry Pi Demo Device", "RASPBERRY_PI_DEMO_TOKEN", "Demo device that is used in " + |
246 | 264 | "Raspberry Pi GPIO control sample application"); |
247 | 265 | |
248 | - Asset thermostatAlarms = new Asset(); | |
249 | - thermostatAlarms.setTenantId(demoTenant.getId()); | |
250 | - thermostatAlarms.setName("Thermostat Alarms"); | |
251 | - thermostatAlarms.setType("AlarmPropagationAsset"); | |
252 | - thermostatAlarms = assetService.saveAsset(thermostatAlarms); | |
253 | - | |
254 | - DeviceProfile thermostatDeviceProfile = this.deviceProfileService.findOrCreateDeviceProfile(demoTenant.getId(), "thermostat"); | |
255 | - | |
256 | - DeviceId t1Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); | |
257 | - DeviceId t2Id = createDevice(demoTenant.getId(), null, thermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); | |
258 | - | |
259 | - relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t1Id, "ToAlarmPropagationAsset")); | |
260 | - relationService.saveRelation(thermostatAlarms.getTenantId(), new EntityRelation(thermostatAlarms.getId(), t2Id, "ToAlarmPropagationAsset")); | |
266 | + DeviceProfile thermostatDeviceProfile = new DeviceProfile(); | |
267 | + thermostatDeviceProfile.setTenantId(demoTenant.getId()); | |
268 | + thermostatDeviceProfile.setDefault(false); | |
269 | + thermostatDeviceProfile.setName("thermostat"); | |
270 | + thermostatDeviceProfile.setType(DeviceProfileType.DEFAULT); | |
271 | + thermostatDeviceProfile.setTransportType(DeviceTransportType.DEFAULT); | |
272 | + thermostatDeviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED); | |
273 | + thermostatDeviceProfile.setDescription("Thermostat device profile"); | |
274 | + thermostatDeviceProfile.setDefaultRuleChainId(ruleChainService.findTenantRuleChains( | |
275 | + demoTenant.getId(), new PageLink(1, 0, "Thermostat Alarms")).getData().get(0).getId()); | |
276 | + | |
277 | + DeviceProfileData deviceProfileData = new DeviceProfileData(); | |
278 | + DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); | |
279 | + DefaultDeviceProfileTransportConfiguration transportConfiguration = new DefaultDeviceProfileTransportConfiguration(); | |
280 | + DisabledDeviceProfileProvisionConfiguration provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null); | |
281 | + deviceProfileData.setConfiguration(configuration); | |
282 | + deviceProfileData.setTransportConfiguration(transportConfiguration); | |
283 | + deviceProfileData.setProvisionConfiguration(provisionConfiguration); | |
284 | + thermostatDeviceProfile.setProfileData(deviceProfileData); | |
285 | + | |
286 | + DeviceProfileAlarm highTemperature = new DeviceProfileAlarm(); | |
287 | + highTemperature.setId("highTemperatureAlarmID"); | |
288 | + highTemperature.setAlarmType("High Temperature"); | |
289 | + AlarmRule temperatureRule = new AlarmRule(); | |
290 | + AlarmCondition temperatureCondition = new AlarmCondition(); | |
291 | + temperatureCondition.setSpec(new SimpleAlarmConditionSpec()); | |
292 | + | |
293 | + KeyFilter alarmTemperatureAttributeFilter = new KeyFilter(); | |
294 | + alarmTemperatureAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmTemperature")); | |
295 | + alarmTemperatureAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); | |
296 | + BooleanFilterPredicate alarmTemperatureAttributePredicate = new BooleanFilterPredicate(); | |
297 | + alarmTemperatureAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); | |
298 | + alarmTemperatureAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); | |
299 | + alarmTemperatureAttributeFilter.setPredicate(alarmTemperatureAttributePredicate); | |
300 | + | |
301 | + KeyFilter temperatureTimeseriesFilter = new KeyFilter(); | |
302 | + temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); | |
303 | + temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | |
304 | + NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); | |
305 | + temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); | |
306 | + FilterPredicateValue<Double> temperatureTimeseriesPredicateValue = | |
307 | + new FilterPredicateValue<>(25.0, null, | |
308 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature")); | |
309 | + temperatureTimeseriesFilterPredicate.setValue(temperatureTimeseriesPredicateValue); | |
310 | + temperatureTimeseriesFilter.setPredicate(temperatureTimeseriesFilterPredicate); | |
311 | + temperatureCondition.setCondition(Arrays.asList(alarmTemperatureAttributeFilter, temperatureTimeseriesFilter)); | |
312 | + temperatureRule.setAlarmDetails("Current temperature = ${temperature}"); | |
313 | + temperatureRule.setCondition(temperatureCondition); | |
314 | + highTemperature.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MAJOR, temperatureRule))); | |
315 | + | |
316 | + AlarmRule clearTemperatureRule = new AlarmRule(); | |
317 | + AlarmCondition clearTemperatureCondition = new AlarmCondition(); | |
318 | + clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec()); | |
319 | + | |
320 | + KeyFilter clearTemperatureTimeseriesFilter = new KeyFilter(); | |
321 | + clearTemperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); | |
322 | + clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | |
323 | + NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); | |
324 | + clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL); | |
325 | + FilterPredicateValue<Double> clearTemperatureTimeseriesPredicateValue = | |
326 | + new FilterPredicateValue<>(25.0, null, | |
327 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdTemperature")); | |
328 | + | |
329 | + clearTemperatureTimeseriesFilterPredicate.setValue(clearTemperatureTimeseriesPredicateValue); | |
330 | + clearTemperatureTimeseriesFilter.setPredicate(clearTemperatureTimeseriesFilterPredicate); | |
331 | + clearTemperatureCondition.setCondition(Collections.singletonList(clearTemperatureTimeseriesFilter)); | |
332 | + clearTemperatureRule.setCondition(clearTemperatureCondition); | |
333 | + clearTemperatureRule.setAlarmDetails("Current temperature = ${temperature}"); | |
334 | + highTemperature.setClearRule(clearTemperatureRule); | |
335 | + | |
336 | + DeviceProfileAlarm lowHumidity = new DeviceProfileAlarm(); | |
337 | + lowHumidity.setId("lowHumidityAlarmID"); | |
338 | + lowHumidity.setAlarmType("Low Humidity"); | |
339 | + AlarmRule humidityRule = new AlarmRule(); | |
340 | + AlarmCondition humidityCondition = new AlarmCondition(); | |
341 | + humidityCondition.setSpec(new SimpleAlarmConditionSpec()); | |
342 | + | |
343 | + KeyFilter alarmHumidityAttributeFilter = new KeyFilter(); | |
344 | + alarmHumidityAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmHumidity")); | |
345 | + alarmHumidityAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); | |
346 | + BooleanFilterPredicate alarmHumidityAttributePredicate = new BooleanFilterPredicate(); | |
347 | + alarmHumidityAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); | |
348 | + alarmHumidityAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); | |
349 | + alarmHumidityAttributeFilter.setPredicate(alarmHumidityAttributePredicate); | |
350 | + | |
351 | + KeyFilter humidityTimeseriesFilter = new KeyFilter(); | |
352 | + humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); | |
353 | + humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | |
354 | + NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate(); | |
355 | + humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); | |
356 | + FilterPredicateValue<Double> humidityTimeseriesPredicateValue = | |
357 | + new FilterPredicateValue<>(60.0, null, | |
358 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity")); | |
359 | + humidityTimeseriesFilterPredicate.setValue(humidityTimeseriesPredicateValue); | |
360 | + humidityTimeseriesFilter.setPredicate(humidityTimeseriesFilterPredicate); | |
361 | + humidityCondition.setCondition(Arrays.asList(alarmHumidityAttributeFilter, humidityTimeseriesFilter)); | |
362 | + | |
363 | + humidityRule.setCondition(humidityCondition); | |
364 | + humidityRule.setAlarmDetails("Current humidity = ${humidity}"); | |
365 | + lowHumidity.setCreateRules(new LinkedHashMap<>(Collections.singletonMap(AlarmSeverity.MINOR, humidityRule))); | |
366 | + | |
367 | + AlarmRule clearHumidityRule = new AlarmRule(); | |
368 | + AlarmCondition clearHumidityCondition = new AlarmCondition(); | |
369 | + clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec()); | |
370 | + | |
371 | + KeyFilter clearHumidityTimeseriesFilter = new KeyFilter(); | |
372 | + clearHumidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); | |
373 | + clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | |
374 | + NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate(); | |
375 | + clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL); | |
376 | + FilterPredicateValue<Double> clearHumidityTimeseriesPredicateValue = | |
377 | + new FilterPredicateValue<>(60.0, null, | |
378 | + new DynamicValue<>(DynamicValueSourceType.CURRENT_DEVICE, "thresholdHumidity")); | |
379 | + | |
380 | + clearHumidityTimeseriesFilterPredicate.setValue(clearHumidityTimeseriesPredicateValue); | |
381 | + clearHumidityTimeseriesFilter.setPredicate(clearHumidityTimeseriesFilterPredicate); | |
382 | + clearHumidityCondition.setCondition(Collections.singletonList(clearHumidityTimeseriesFilter)); | |
383 | + clearHumidityRule.setCondition(clearHumidityCondition); | |
384 | + clearHumidityRule.setAlarmDetails("Current humidity = ${humidity}"); | |
385 | + lowHumidity.setClearRule(clearHumidityRule); | |
386 | + | |
387 | + deviceProfileData.setAlarms(Arrays.asList(highTemperature, lowHumidity)); | |
388 | + | |
389 | + DeviceProfile savedThermostatDeviceProfile = deviceProfileService.saveDeviceProfile(thermostatDeviceProfile); | |
390 | + | |
391 | + DeviceId t1Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T1", "T1_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); | |
392 | + DeviceId t2Id = createDevice(demoTenant.getId(), null, savedThermostatDeviceProfile.getId(), "Thermostat T2", "T2_TEST_TOKEN", "Demo device for Thermostats dashboard").getId(); | |
261 | 393 | |
262 | 394 | attributesService.save(demoTenant.getId(), t1Id, DataConstants.SERVER_SCOPE, |
263 | 395 | Arrays.asList(new BaseAttributeKvEntry(System.currentTimeMillis(), new DoubleDataEntry("latitude", 37.3948)), | ... | ... |
... | ... | @@ -367,6 +367,8 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService |
367 | 367 | " db_storage varchar(32)," + |
368 | 368 | " re_exec varchar(32)," + |
369 | 369 | " js_exec varchar(32)," + |
370 | + " email_exec varchar(32)," + | |
371 | + " sms_exec varchar(32)," + | |
370 | 372 | " CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)\n" + |
371 | 373 | ");"); |
372 | 374 | } catch (Exception e) { | ... | ... |
... | ... | @@ -20,8 +20,10 @@ import freemarker.template.Configuration; |
20 | 20 | import freemarker.template.Template; |
21 | 21 | import lombok.extern.slf4j.Slf4j; |
22 | 22 | import org.apache.commons.lang3.StringUtils; |
23 | +import org.jetbrains.annotations.NotNull; | |
23 | 24 | import org.springframework.beans.factory.annotation.Autowired; |
24 | 25 | import org.springframework.context.MessageSource; |
26 | +import org.springframework.context.annotation.Lazy; | |
25 | 27 | import org.springframework.core.NestedRuntimeException; |
26 | 28 | import org.springframework.mail.javamail.JavaMailSenderImpl; |
27 | 29 | import org.springframework.mail.javamail.MimeMessageHelper; |
... | ... | @@ -29,12 +31,18 @@ import org.springframework.stereotype.Service; |
29 | 31 | import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; |
30 | 32 | import org.thingsboard.rule.engine.api.MailService; |
31 | 33 | import org.thingsboard.server.common.data.AdminSettings; |
34 | +import org.thingsboard.server.common.data.ApiFeature; | |
35 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
36 | +import org.thingsboard.server.common.data.ApiUsageStateMailMessage; | |
37 | +import org.thingsboard.server.common.data.ApiUsageStateValue; | |
32 | 38 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
33 | 39 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
34 | 40 | import org.thingsboard.server.common.data.id.EntityId; |
35 | 41 | import org.thingsboard.server.common.data.id.TenantId; |
36 | 42 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
37 | 43 | import org.thingsboard.server.dao.settings.AdminSettingsService; |
44 | +import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | |
45 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
38 | 46 | |
39 | 47 | import javax.annotation.PostConstruct; |
40 | 48 | import javax.mail.MessagingException; |
... | ... | @@ -51,18 +59,28 @@ public class DefaultMailService implements MailService { |
51 | 59 | public static final String MAIL_PROP = "mail."; |
52 | 60 | public static final String TARGET_EMAIL = "targetEmail"; |
53 | 61 | public static final String UTF_8 = "UTF-8"; |
54 | - @Autowired | |
55 | - private MessageSource messages; | |
62 | + public static final int _10K = 10000; | |
63 | + public static final int _1M = 1000000; | |
64 | + | |
65 | + private final MessageSource messages; | |
66 | + private final Configuration freemarkerConfig; | |
67 | + private final AdminSettingsService adminSettingsService; | |
68 | + private final TbApiUsageClient apiUsageClient; | |
56 | 69 | |
70 | + @Lazy | |
57 | 71 | @Autowired |
58 | - private Configuration freemarkerConfig; | |
72 | + private TbApiUsageStateService apiUsageStateService; | |
59 | 73 | |
60 | 74 | private JavaMailSenderImpl mailSender; |
61 | 75 | |
62 | 76 | private String mailFrom; |
63 | 77 | |
64 | - @Autowired | |
65 | - private AdminSettingsService adminSettingsService; | |
78 | + public DefaultMailService(MessageSource messages, Configuration freemarkerConfig, AdminSettingsService adminSettingsService, TbApiUsageClient apiUsageClient) { | |
79 | + this.messages = messages; | |
80 | + this.freemarkerConfig = freemarkerConfig; | |
81 | + this.adminSettingsService = adminSettingsService; | |
82 | + this.apiUsageClient = apiUsageClient; | |
83 | + } | |
66 | 84 | |
67 | 85 | @PostConstruct |
68 | 86 | private void init() { |
... | ... | @@ -141,7 +159,7 @@ public class DefaultMailService implements MailService { |
141 | 159 | } |
142 | 160 | |
143 | 161 | @Override |
144 | - public void sendEmail(String email, String subject, String message) throws ThingsboardException { | |
162 | + public void sendEmail(TenantId tenantId, String email, String subject, String message) throws ThingsboardException { | |
145 | 163 | sendMail(mailSender, mailFrom, email, subject, message); |
146 | 164 | } |
147 | 165 | |
... | ... | @@ -216,20 +234,23 @@ public class DefaultMailService implements MailService { |
216 | 234 | } |
217 | 235 | |
218 | 236 | @Override |
219 | - public void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException { | |
220 | - MimeMessage mailMsg = mailSender.createMimeMessage(); | |
221 | - MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8"); | |
222 | - helper.setFrom(StringUtils.isBlank(from) ? mailFrom : from); | |
223 | - helper.setTo(to.split("\\s*,\\s*")); | |
224 | - if (!StringUtils.isBlank(cc)) { | |
225 | - helper.setCc(cc.split("\\s*,\\s*")); | |
226 | - } | |
227 | - if (!StringUtils.isBlank(bcc)) { | |
228 | - helper.setBcc(bcc.split("\\s*,\\s*")); | |
237 | + public void send(TenantId tenantId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException { | |
238 | + if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) { | |
239 | + MimeMessage mailMsg = mailSender.createMimeMessage(); | |
240 | + MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8"); | |
241 | + helper.setFrom(StringUtils.isBlank(from) ? mailFrom : from); | |
242 | + helper.setTo(to.split("\\s*,\\s*")); | |
243 | + if (!StringUtils.isBlank(cc)) { | |
244 | + helper.setCc(cc.split("\\s*,\\s*")); | |
245 | + } | |
246 | + if (!StringUtils.isBlank(bcc)) { | |
247 | + helper.setBcc(bcc.split("\\s*,\\s*")); | |
248 | + } | |
249 | + helper.setSubject(subject); | |
250 | + helper.setText(body); | |
251 | + mailSender.send(helper.getMimeMessage()); | |
252 | + apiUsageClient.report(tenantId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1); | |
229 | 253 | } |
230 | - helper.setSubject(subject); | |
231 | - helper.setText(body); | |
232 | - mailSender.send(helper.getMimeMessage()); | |
233 | 254 | } |
234 | 255 | |
235 | 256 | @Override |
... | ... | @@ -246,6 +267,122 @@ public class DefaultMailService implements MailService { |
246 | 267 | sendMail(mailSender, mailFrom, email, subject, message); |
247 | 268 | } |
248 | 269 | |
270 | + @Override | |
271 | + public void sendApiFeatureStateEmail(ApiFeature apiFeature, ApiUsageStateValue stateValue, String email, ApiUsageStateMailMessage msg) throws ThingsboardException { | |
272 | + String subject = messages.getMessage("api.usage.state", null, Locale.US); | |
273 | + | |
274 | + Map<String, Object> model = new HashMap<>(); | |
275 | + model.put("apiFeature", apiFeature.getLabel()); | |
276 | + model.put(TARGET_EMAIL, email); | |
277 | + | |
278 | + String message = null; | |
279 | + | |
280 | + switch (stateValue) { | |
281 | + case ENABLED: | |
282 | + model.put("apiLabel", toEnabledValueLabel(apiFeature)); | |
283 | + message = mergeTemplateIntoString("state.enabled.ftl", model); | |
284 | + break; | |
285 | + case WARNING: | |
286 | + model.put("apiValueLabel", toDisabledValueLabel(apiFeature) + " " + toWarningValueLabel(msg.getKey(), msg.getValue(), msg.getThreshold())); | |
287 | + message = mergeTemplateIntoString("state.warning.ftl", model); | |
288 | + break; | |
289 | + case DISABLED: | |
290 | + model.put("apiLimitValueLabel", toDisabledValueLabel(apiFeature) + " " + toDisabledValueLabel(msg.getKey(), msg.getThreshold())); | |
291 | + message = mergeTemplateIntoString("state.disabled.ftl", model); | |
292 | + break; | |
293 | + } | |
294 | + sendMail(mailSender, mailFrom, email, subject, message); | |
295 | + } | |
296 | + | |
297 | + private String toEnabledValueLabel(ApiFeature apiFeature) { | |
298 | + switch (apiFeature) { | |
299 | + case DB: | |
300 | + return "save"; | |
301 | + case TRANSPORT: | |
302 | + return "receive"; | |
303 | + case JS: | |
304 | + return "invoke"; | |
305 | + case RE: | |
306 | + return "process"; | |
307 | + case EMAIL: | |
308 | + case SMS: | |
309 | + return "send"; | |
310 | + default: | |
311 | + throw new RuntimeException("Not implemented!"); | |
312 | + } | |
313 | + } | |
314 | + | |
315 | + private String toDisabledValueLabel(ApiFeature apiFeature) { | |
316 | + switch (apiFeature) { | |
317 | + case DB: | |
318 | + return "saved"; | |
319 | + case TRANSPORT: | |
320 | + return "received"; | |
321 | + case JS: | |
322 | + return "invoked"; | |
323 | + case RE: | |
324 | + return "processed"; | |
325 | + case EMAIL: | |
326 | + case SMS: | |
327 | + return "sent"; | |
328 | + default: | |
329 | + throw new RuntimeException("Not implemented!"); | |
330 | + } | |
331 | + } | |
332 | + | |
333 | + private String toWarningValueLabel(ApiUsageRecordKey key, long value, long threshold) { | |
334 | + String valueInM = getValueAsString(value); | |
335 | + String thresholdInM = getValueAsString(threshold); | |
336 | + switch (key) { | |
337 | + case STORAGE_DP_COUNT: | |
338 | + case TRANSPORT_DP_COUNT: | |
339 | + return valueInM + " out of " + thresholdInM + " allowed data points"; | |
340 | + case TRANSPORT_MSG_COUNT: | |
341 | + return valueInM + " out of " + thresholdInM + " allowed messages"; | |
342 | + case JS_EXEC_COUNT: | |
343 | + return valueInM + " out of " + thresholdInM + " allowed JavaScript functions"; | |
344 | + case RE_EXEC_COUNT: | |
345 | + return valueInM + " out of " + thresholdInM + " allowed Rule Engine messages"; | |
346 | + case EMAIL_EXEC_COUNT: | |
347 | + return valueInM + " out of " + thresholdInM + " allowed Email messages"; | |
348 | + case SMS_EXEC_COUNT: | |
349 | + return valueInM + " out of " + thresholdInM + " allowed SMS messages"; | |
350 | + default: | |
351 | + throw new RuntimeException("Not implemented!"); | |
352 | + } | |
353 | + } | |
354 | + | |
355 | + private String toDisabledValueLabel(ApiUsageRecordKey key, long value) { | |
356 | + switch (key) { | |
357 | + case STORAGE_DP_COUNT: | |
358 | + case TRANSPORT_DP_COUNT: | |
359 | + return getValueAsString(value) + " data points"; | |
360 | + case TRANSPORT_MSG_COUNT: | |
361 | + return getValueAsString(value) + " messages"; | |
362 | + case JS_EXEC_COUNT: | |
363 | + return "JavaScript functions " + getValueAsString(value) + " times"; | |
364 | + case RE_EXEC_COUNT: | |
365 | + return getValueAsString(value) + " Rule Engine messages"; | |
366 | + case EMAIL_EXEC_COUNT: | |
367 | + return getValueAsString(value) + " Email messages"; | |
368 | + case SMS_EXEC_COUNT: | |
369 | + return getValueAsString(value) + " SMS messages"; | |
370 | + default: | |
371 | + throw new RuntimeException("Not implemented!"); | |
372 | + } | |
373 | + } | |
374 | + | |
375 | + @NotNull | |
376 | + private String getValueAsString(long value) { | |
377 | + if (value > _1M && value % _1M < _10K) { | |
378 | + return value / _1M + "M"; | |
379 | + } else if (value > _10K) { | |
380 | + return String.format("%.2fM", ((double) value) / 1000000); | |
381 | + } else { | |
382 | + return value + ""; | |
383 | + } | |
384 | + } | |
385 | + | |
249 | 386 | private void sendMail(JavaMailSenderImpl mailSender, |
250 | 387 | String mailFrom, String email, |
251 | 388 | String subject, String message) throws ThingsboardException { | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import org.springframework.scheduling.annotation.Scheduled; |
22 | 22 | import org.springframework.stereotype.Service; |
23 | 23 | import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; |
24 | 24 | import org.thingsboard.server.common.data.ApiUsageState; |
25 | +import org.thingsboard.server.common.data.Device; | |
25 | 26 | import org.thingsboard.server.common.data.DeviceProfile; |
26 | 27 | import org.thingsboard.server.common.data.EntityType; |
27 | 28 | import org.thingsboard.server.common.data.HasName; |
... | ... | @@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; |
32 | 33 | import org.thingsboard.server.common.data.id.EntityId; |
33 | 34 | import org.thingsboard.server.common.data.id.RuleChainId; |
34 | 35 | import org.thingsboard.server.common.data.id.TenantId; |
35 | -import org.thingsboard.server.common.data.id.TenantProfileId; | |
36 | 36 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
37 | 37 | import org.thingsboard.server.common.msg.TbMsg; |
38 | 38 | import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; |
... | ... | @@ -237,6 +237,16 @@ public class DefaultTbClusterService implements TbClusterService { |
237 | 237 | onEntityDelete(TenantId.SYS_TENANT_ID, entity.getId(), entity.getName(), callback); |
238 | 238 | } |
239 | 239 | |
240 | + @Override | |
241 | + public void onDeviceChange(Device entity, TbQueueCallback callback) { | |
242 | + onEntityChange(entity.getTenantId(), entity.getId(), entity, callback); | |
243 | + } | |
244 | + | |
245 | + @Override | |
246 | + public void onDeviceDeleted(Device entity, TbQueueCallback callback) { | |
247 | + onEntityDelete(entity.getTenantId(), entity.getId(), entity.getName(), callback); | |
248 | + } | |
249 | + | |
240 | 250 | public <T> void onEntityChange(TenantId tenantId, EntityId entityid, T entity, TbQueueCallback callback) { |
241 | 251 | String entityName = (entity instanceof HasName) ? ((HasName) entity).getName() : entity.getClass().getName(); |
242 | 252 | log.trace("[{}][{}][{}] Processing [{}] change event", tenantId, entityid.getEntityType(), entityid.getId(), entityName); | ... | ... |
... | ... | @@ -15,6 +15,8 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.service.queue; |
17 | 17 | |
18 | +import lombok.Getter; | |
19 | +import lombok.Setter; | |
18 | 20 | import lombok.extern.slf4j.Slf4j; |
19 | 21 | import org.springframework.beans.factory.annotation.Value; |
20 | 22 | import org.springframework.boot.context.event.ApplicationReadyEvent; |
... | ... | @@ -76,6 +78,7 @@ import java.util.concurrent.ConcurrentMap; |
76 | 78 | import java.util.concurrent.CountDownLatch; |
77 | 79 | import java.util.concurrent.ExecutorService; |
78 | 80 | import java.util.concurrent.Executors; |
81 | +import java.util.concurrent.Future; | |
79 | 82 | import java.util.concurrent.TimeUnit; |
80 | 83 | import java.util.function.Function; |
81 | 84 | import java.util.stream.Collectors; |
... | ... | @@ -175,39 +178,48 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
175 | 178 | CountDownLatch processingTimeoutLatch = new CountDownLatch(1); |
176 | 179 | TbPackProcessingContext<TbProtoQueueMsg<ToCoreMsg>> ctx = new TbPackProcessingContext<>( |
177 | 180 | processingTimeoutLatch, pendingMap, new ConcurrentHashMap<>()); |
178 | - pendingMap.forEach((id, msg) -> { | |
179 | - log.trace("[{}] Creating main callback for message: {}", id, msg.getValue()); | |
180 | - TbCallback callback = new TbPackCallback<>(id, ctx); | |
181 | - try { | |
182 | - ToCoreMsg toCoreMsg = msg.getValue(); | |
183 | - if (toCoreMsg.hasToSubscriptionMgrMsg()) { | |
184 | - log.trace("[{}] Forwarding message to subscription manager service {}", id, toCoreMsg.getToSubscriptionMgrMsg()); | |
185 | - forwardToSubMgrService(toCoreMsg.getToSubscriptionMgrMsg(), callback); | |
186 | - } else if (toCoreMsg.hasToDeviceActorMsg()) { | |
187 | - log.trace("[{}] Forwarding message to device actor {}", id, toCoreMsg.getToDeviceActorMsg()); | |
188 | - forwardToDeviceActor(toCoreMsg.getToDeviceActorMsg(), callback); | |
189 | - } else if (toCoreMsg.hasDeviceStateServiceMsg()) { | |
190 | - log.trace("[{}] Forwarding message to state service {}", id, toCoreMsg.getDeviceStateServiceMsg()); | |
191 | - forwardToStateService(toCoreMsg.getDeviceStateServiceMsg(), callback); | |
192 | - } else if (toCoreMsg.getToDeviceActorNotificationMsg() != null && !toCoreMsg.getToDeviceActorNotificationMsg().isEmpty()) { | |
193 | - Optional<TbActorMsg> actorMsg = encodingService.decode(toCoreMsg.getToDeviceActorNotificationMsg().toByteArray()); | |
194 | - if (actorMsg.isPresent()) { | |
195 | - TbActorMsg tbActorMsg = actorMsg.get(); | |
196 | - if (tbActorMsg.getMsgType().equals(MsgType.DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG)) { | |
197 | - tbCoreDeviceRpcService.forwardRpcRequestToDeviceActor((ToDeviceRpcRequestActorMsg) tbActorMsg); | |
198 | - } else { | |
199 | - log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get()); | |
200 | - actorContext.tell(actorMsg.get()); | |
181 | + PendingMsgHolder pendingMsgHolder = new PendingMsgHolder(); | |
182 | + Future<?> packSubmitFuture = consumersExecutor.submit(() -> { | |
183 | + pendingMap.forEach((id, msg) -> { | |
184 | + log.trace("[{}] Creating main callback for message: {}", id, msg.getValue()); | |
185 | + TbCallback callback = new TbPackCallback<>(id, ctx); | |
186 | + try { | |
187 | + ToCoreMsg toCoreMsg = msg.getValue(); | |
188 | + pendingMsgHolder.setToCoreMsg(toCoreMsg); | |
189 | + if (toCoreMsg.hasToSubscriptionMgrMsg()) { | |
190 | + log.trace("[{}] Forwarding message to subscription manager service {}", id, toCoreMsg.getToSubscriptionMgrMsg()); | |
191 | + forwardToSubMgrService(toCoreMsg.getToSubscriptionMgrMsg(), callback); | |
192 | + } else if (toCoreMsg.hasToDeviceActorMsg()) { | |
193 | + log.trace("[{}] Forwarding message to device actor {}", id, toCoreMsg.getToDeviceActorMsg()); | |
194 | + forwardToDeviceActor(toCoreMsg.getToDeviceActorMsg(), callback); | |
195 | + } else if (toCoreMsg.hasDeviceStateServiceMsg()) { | |
196 | + log.trace("[{}] Forwarding message to state service {}", id, toCoreMsg.getDeviceStateServiceMsg()); | |
197 | + forwardToStateService(toCoreMsg.getDeviceStateServiceMsg(), callback); | |
198 | + } else if (toCoreMsg.getToDeviceActorNotificationMsg() != null && !toCoreMsg.getToDeviceActorNotificationMsg().isEmpty()) { | |
199 | + Optional<TbActorMsg> actorMsg = encodingService.decode(toCoreMsg.getToDeviceActorNotificationMsg().toByteArray()); | |
200 | + if (actorMsg.isPresent()) { | |
201 | + TbActorMsg tbActorMsg = actorMsg.get(); | |
202 | + if (tbActorMsg.getMsgType().equals(MsgType.DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG)) { | |
203 | + tbCoreDeviceRpcService.forwardRpcRequestToDeviceActor((ToDeviceRpcRequestActorMsg) tbActorMsg); | |
204 | + } else { | |
205 | + log.trace("[{}] Forwarding message to App Actor {}", id, actorMsg.get()); | |
206 | + actorContext.tell(actorMsg.get()); | |
207 | + } | |
201 | 208 | } |
209 | + callback.onSuccess(); | |
202 | 210 | } |
203 | - callback.onSuccess(); | |
211 | + } catch (Throwable e) { | |
212 | + log.warn("[{}] Failed to process message: {}", id, msg, e); | |
213 | + callback.onFailure(e); | |
204 | 214 | } |
205 | - } catch (Throwable e) { | |
206 | - log.warn("[{}] Failed to process message: {}", id, msg, e); | |
207 | - callback.onFailure(e); | |
208 | - } | |
215 | + }); | |
209 | 216 | }); |
210 | 217 | if (!processingTimeoutLatch.await(packProcessingTimeout, TimeUnit.MILLISECONDS)) { |
218 | + if (!packSubmitFuture.isDone()) { | |
219 | + packSubmitFuture.cancel(true); | |
220 | + ToCoreMsg lastSubmitMsg = pendingMsgHolder.getToCoreMsg(); | |
221 | + log.info("Timeout to process message: {}", lastSubmitMsg); | |
222 | + } | |
211 | 223 | ctx.getAckMap().forEach((id, msg) -> log.debug("[{}] Timeout to process message: {}", id, msg.getValue())); |
212 | 224 | ctx.getFailedMap().forEach((id, msg) -> log.warn("[{}] Failed to process message: {}", id, msg.getValue())); |
213 | 225 | } |
... | ... | @@ -227,6 +239,12 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService<ToCore |
227 | 239 | }); |
228 | 240 | } |
229 | 241 | |
242 | + private static class PendingMsgHolder { | |
243 | + @Getter | |
244 | + @Setter | |
245 | + private volatile ToCoreMsg toCoreMsg; | |
246 | + } | |
247 | + | |
230 | 248 | @Override |
231 | 249 | protected ServiceType getServiceType() { |
232 | 250 | return ServiceType.TB_CORE; | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.service.queue; |
17 | 17 | |
18 | 18 | import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; |
19 | 19 | import org.thingsboard.server.common.data.ApiUsageState; |
20 | +import org.thingsboard.server.common.data.Device; | |
20 | 21 | import org.thingsboard.server.common.data.DeviceProfile; |
21 | 22 | import org.thingsboard.server.common.data.Tenant; |
22 | 23 | import org.thingsboard.server.common.data.TenantProfile; |
... | ... | @@ -66,4 +67,8 @@ public interface TbClusterService { |
66 | 67 | void onTenantDelete(Tenant tenant, TbQueueCallback callback); |
67 | 68 | |
68 | 69 | void onApiStateChange(ApiUsageState apiUsageState, TbQueueCallback callback); |
70 | + | |
71 | + void onDeviceChange(Device device, TbQueueCallback callback); | |
72 | + | |
73 | + void onDeviceDeleted(Device device, TbQueueCallback callback); | |
69 | 74 | } | ... | ... |
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.sms; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.thingsboard.rule.engine.api.sms.SmsSender; | |
20 | +import org.thingsboard.rule.engine.api.sms.exception.SmsParseException; | |
21 | + | |
22 | +import java.util.regex.Pattern; | |
23 | + | |
24 | +@Slf4j | |
25 | +public abstract class AbstractSmsSender implements SmsSender { | |
26 | + | |
27 | + private static final Pattern E_164_PHONE_NUMBER_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$"); | |
28 | + | |
29 | + private static final int MAX_SMS_MESSAGE_LENGTH = 1600; | |
30 | + private static final int MAX_SMS_SEGMENT_LENGTH = 70; | |
31 | + | |
32 | + protected String validatePhoneNumber(String phoneNumber) throws SmsParseException { | |
33 | + phoneNumber = phoneNumber.trim(); | |
34 | + if (!E_164_PHONE_NUMBER_PATTERN.matcher(phoneNumber).matches()) { | |
35 | + throw new SmsParseException("Invalid phone number format. Phone number must be in E.164 format."); | |
36 | + } | |
37 | + return phoneNumber; | |
38 | + } | |
39 | + | |
40 | + protected String prepareMessage(String message) { | |
41 | + message = message.replaceAll("^\"|\"$", "").replaceAll("\\\\n", "\n"); | |
42 | + if (message.length() > MAX_SMS_MESSAGE_LENGTH) { | |
43 | + log.warn("SMS message exceeds maximum symbols length and will be truncated"); | |
44 | + message = message.substring(0, MAX_SMS_MESSAGE_LENGTH); | |
45 | + } | |
46 | + return message; | |
47 | + } | |
48 | + | |
49 | + protected int countMessageSegments(String message) { | |
50 | + return (int)Math.ceil((double) message.length() / (double) MAX_SMS_SEGMENT_LENGTH); | |
51 | + } | |
52 | + | |
53 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsSenderFactory.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.sms; | |
17 | + | |
18 | +import org.springframework.stereotype.Component; | |
19 | +import org.thingsboard.rule.engine.api.sms.SmsSender; | |
20 | +import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; | |
21 | +import org.thingsboard.rule.engine.api.sms.config.AwsSnsSmsProviderConfiguration; | |
22 | +import org.thingsboard.rule.engine.api.sms.config.SmsProviderConfiguration; | |
23 | +import org.thingsboard.rule.engine.api.sms.config.TwilioSmsProviderConfiguration; | |
24 | +import org.thingsboard.server.service.sms.aws.AwsSmsSender; | |
25 | +import org.thingsboard.server.service.sms.twilio.TwilioSmsSender; | |
26 | + | |
27 | +@Component | |
28 | +public class DefaultSmsSenderFactory implements SmsSenderFactory { | |
29 | + | |
30 | + @Override | |
31 | + public SmsSender createSmsSender(SmsProviderConfiguration config) { | |
32 | + switch (config.getType()) { | |
33 | + case AWS_SNS: | |
34 | + return new AwsSmsSender((AwsSnsSmsProviderConfiguration)config); | |
35 | + case TWILIO: | |
36 | + return new TwilioSmsSender((TwilioSmsProviderConfiguration)config); | |
37 | + default: | |
38 | + throw new RuntimeException("Unknown SMS provider type " + config.getType()); | |
39 | + } | |
40 | + } | |
41 | + | |
42 | +} | ... | ... |
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.sms; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.JsonNode; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.springframework.core.NestedRuntimeException; | |
21 | +import org.springframework.stereotype.Service; | |
22 | +import org.thingsboard.rule.engine.api.SmsService; | |
23 | +import org.thingsboard.rule.engine.api.sms.SmsSender; | |
24 | +import org.thingsboard.rule.engine.api.sms.SmsSenderFactory; | |
25 | +import org.thingsboard.rule.engine.api.sms.config.SmsProviderConfiguration; | |
26 | +import org.thingsboard.rule.engine.api.sms.config.TestSmsRequest; | |
27 | +import org.thingsboard.server.common.data.AdminSettings; | |
28 | +import org.thingsboard.server.common.data.ApiUsageRecordKey; | |
29 | +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | |
30 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
31 | +import org.thingsboard.server.common.data.id.EntityId; | |
32 | +import org.thingsboard.server.common.data.id.TenantId; | |
33 | +import org.thingsboard.server.dao.settings.AdminSettingsService; | |
34 | +import org.thingsboard.server.dao.util.mapping.JacksonUtil; | |
35 | +import org.thingsboard.server.queue.usagestats.TbApiUsageClient; | |
36 | +import org.thingsboard.server.service.apiusage.TbApiUsageStateService; | |
37 | + | |
38 | +import javax.annotation.PostConstruct; | |
39 | +import javax.annotation.PreDestroy; | |
40 | + | |
41 | +@Service | |
42 | +@Slf4j | |
43 | +public class DefaultSmsService implements SmsService { | |
44 | + | |
45 | + private final SmsSenderFactory smsSenderFactory; | |
46 | + private final AdminSettingsService adminSettingsService; | |
47 | + private final TbApiUsageStateService apiUsageStateService; | |
48 | + private final TbApiUsageClient apiUsageClient; | |
49 | + | |
50 | + private SmsSender smsSender; | |
51 | + | |
52 | + public DefaultSmsService(SmsSenderFactory smsSenderFactory, AdminSettingsService adminSettingsService, TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient) { | |
53 | + this.smsSenderFactory = smsSenderFactory; | |
54 | + this.adminSettingsService = adminSettingsService; | |
55 | + this.apiUsageStateService = apiUsageStateService; | |
56 | + this.apiUsageClient = apiUsageClient; | |
57 | + } | |
58 | + | |
59 | + @PostConstruct | |
60 | + private void init() { | |
61 | + updateSmsConfiguration(); | |
62 | + } | |
63 | + | |
64 | + @PreDestroy | |
65 | + private void destroy() { | |
66 | + if (this.smsSender != null) { | |
67 | + this.smsSender.destroy(); | |
68 | + } | |
69 | + } | |
70 | + | |
71 | + @Override | |
72 | + public void updateSmsConfiguration() { | |
73 | + AdminSettings settings = adminSettingsService.findAdminSettingsByKey(new TenantId(EntityId.NULL_UUID), "sms"); | |
74 | + if (settings != null) { | |
75 | + try { | |
76 | + JsonNode jsonConfig = settings.getJsonValue(); | |
77 | + SmsProviderConfiguration configuration = JacksonUtil.convertValue(jsonConfig, SmsProviderConfiguration.class); | |
78 | + SmsSender newSmsSender = this.smsSenderFactory.createSmsSender(configuration); | |
79 | + if (this.smsSender != null) { | |
80 | + this.smsSender.destroy(); | |
81 | + } | |
82 | + this.smsSender = newSmsSender; | |
83 | + } catch (Exception e) { | |
84 | + log.error("Failed to create SMS sender", e); | |
85 | + } | |
86 | + } | |
87 | + } | |
88 | + | |
89 | + private int sendSms(String numberTo, String message) throws ThingsboardException { | |
90 | + if (this.smsSender == null) { | |
91 | + throw new ThingsboardException("Unable to send SMS: no SMS provider configured!", ThingsboardErrorCode.GENERAL); | |
92 | + } | |
93 | + return this.sendSms(this.smsSender, numberTo, message); | |
94 | + } | |
95 | + | |
96 | + @Override | |
97 | + public void sendSms(TenantId tenantId, String[] numbersTo, String message) throws ThingsboardException { | |
98 | + if (apiUsageStateService.getApiUsageState(tenantId).isSmsSendEnabled()) { | |
99 | + int smsCount = 0; | |
100 | + try { | |
101 | + for (String numberTo : numbersTo) { | |
102 | + smsCount += this.sendSms(numberTo, message); | |
103 | + } | |
104 | + } finally { | |
105 | + if (smsCount > 0) { | |
106 | + apiUsageClient.report(tenantId, ApiUsageRecordKey.SMS_EXEC_COUNT, smsCount); | |
107 | + } | |
108 | + } | |
109 | + } | |
110 | + } | |
111 | + | |
112 | + @Override | |
113 | + public void sendTestSms(TestSmsRequest testSmsRequest) throws ThingsboardException { | |
114 | + SmsSender testSmsSender; | |
115 | + try { | |
116 | + testSmsSender = this.smsSenderFactory.createSmsSender(testSmsRequest.getProviderConfiguration()); | |
117 | + } catch (Exception e) { | |
118 | + throw handleException(e); | |
119 | + } | |
120 | + this.sendSms(testSmsSender, testSmsRequest.getNumberTo(), testSmsRequest.getMessage()); | |
121 | + testSmsSender.destroy(); | |
122 | + } | |
123 | + | |
124 | + private int sendSms(SmsSender smsSender, String numberTo, String message) throws ThingsboardException { | |
125 | + try { | |
126 | + return smsSender.sendSms(numberTo, message); | |
127 | + } catch (Exception e) { | |
128 | + throw handleException(e); | |
129 | + } | |
130 | + } | |
131 | + | |
132 | + private ThingsboardException handleException(Exception exception) { | |
133 | + String message; | |
134 | + if (exception instanceof NestedRuntimeException) { | |
135 | + message = ((NestedRuntimeException) exception).getMostSpecificCause().getMessage(); | |
136 | + } else { | |
137 | + message = exception.getMessage(); | |
138 | + } | |
139 | + log.warn("Unable to send SMS: {}", message); | |
140 | + return new ThingsboardException(String.format("Unable to send SMS: %s", message), | |
141 | + ThingsboardErrorCode.GENERAL); | |
142 | + } | |
143 | +} | ... | ... |
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.sms; | |
17 | + | |
18 | +import org.springframework.beans.factory.annotation.Value; | |
19 | +import org.springframework.stereotype.Component; | |
20 | +import org.thingsboard.common.util.AbstractListeningExecutor; | |
21 | + | |
22 | +@Component | |
23 | +public class SmsExecutorService extends AbstractListeningExecutor { | |
24 | + | |
25 | + @Value("${actors.rule.sms_thread_pool_size}") | |
26 | + private int smsExecutorThreadPoolSize; | |
27 | + | |
28 | + @Override | |
29 | + protected int getThreadPollSize() { | |
30 | + return smsExecutorThreadPoolSize; | |
31 | + } | |
32 | + | |
33 | +} | ... | ... |
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.sms.aws; | |
17 | + | |
18 | +import com.amazonaws.auth.AWSCredentials; | |
19 | +import com.amazonaws.auth.AWSStaticCredentialsProvider; | |
20 | +import com.amazonaws.auth.BasicAWSCredentials; | |
21 | +import com.amazonaws.services.sns.AmazonSNS; | |
22 | +import com.amazonaws.services.sns.AmazonSNSClient; | |
23 | +import com.amazonaws.services.sns.model.PublishRequest; | |
24 | +import lombok.extern.slf4j.Slf4j; | |
25 | +import org.apache.commons.lang3.StringUtils; | |
26 | +import org.thingsboard.rule.engine.api.sms.config.AwsSnsSmsProviderConfiguration; | |
27 | +import org.thingsboard.rule.engine.api.sms.exception.SmsException; | |
28 | +import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; | |
29 | +import org.thingsboard.server.service.sms.AbstractSmsSender; | |
30 | + | |
31 | +@Slf4j | |
32 | +public class AwsSmsSender extends AbstractSmsSender { | |
33 | + | |
34 | + private AmazonSNS snsClient; | |
35 | + | |
36 | + public AwsSmsSender(AwsSnsSmsProviderConfiguration config) { | |
37 | + if (StringUtils.isEmpty(config.getAccessKeyId()) || StringUtils.isEmpty(config.getSecretAccessKey()) || StringUtils.isEmpty(config.getRegion())) { | |
38 | + throw new IllegalArgumentException("Invalid AWS sms provider configuration: aws accessKeyId, aws secretAccessKey and aws region should be specified!"); | |
39 | + } | |
40 | + AWSCredentials awsCredentials = new BasicAWSCredentials(config.getAccessKeyId(), config.getSecretAccessKey()); | |
41 | + AWSStaticCredentialsProvider credProvider = new AWSStaticCredentialsProvider(awsCredentials); | |
42 | + this.snsClient = AmazonSNSClient.builder() | |
43 | + .withCredentials(credProvider) | |
44 | + .withRegion(config.getRegion()) | |
45 | + .build(); | |
46 | + } | |
47 | + | |
48 | + @Override | |
49 | + public int sendSms(String numberTo, String message) throws SmsException { | |
50 | + numberTo = this.validatePhoneNumber(numberTo); | |
51 | + message = this.prepareMessage(message); | |
52 | + try { | |
53 | + PublishRequest publishRequest = new PublishRequest() | |
54 | + .withPhoneNumber(numberTo) | |
55 | + .withMessage(message); | |
56 | + this.snsClient.publish(publishRequest); | |
57 | + return this.countMessageSegments(message); | |
58 | + } catch (Exception e) { | |
59 | + throw new SmsSendException("Failed to send SMS message - " + e.getMessage(), e); | |
60 | + } | |
61 | + } | |
62 | + | |
63 | + @Override | |
64 | + public void destroy() { | |
65 | + if (this.snsClient != null) { | |
66 | + try { | |
67 | + this.snsClient.shutdown(); | |
68 | + } catch (Exception e) { | |
69 | + log.error("Failed to shutdown SNS client during destroy()", e); | |
70 | + } | |
71 | + } | |
72 | + } | |
73 | +} | ... | ... |
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.sms.twilio; | |
17 | + | |
18 | +import com.twilio.http.TwilioRestClient; | |
19 | +import com.twilio.rest.api.v2010.account.Message; | |
20 | +import com.twilio.type.PhoneNumber; | |
21 | +import org.apache.commons.lang3.StringUtils; | |
22 | +import org.thingsboard.rule.engine.api.sms.config.TwilioSmsProviderConfiguration; | |
23 | +import org.thingsboard.rule.engine.api.sms.exception.SmsException; | |
24 | +import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; | |
25 | +import org.thingsboard.server.service.sms.AbstractSmsSender; | |
26 | + | |
27 | +public class TwilioSmsSender extends AbstractSmsSender { | |
28 | + | |
29 | + private TwilioRestClient twilioRestClient; | |
30 | + private String numberFrom; | |
31 | + | |
32 | + public TwilioSmsSender(TwilioSmsProviderConfiguration config) { | |
33 | + if (StringUtils.isEmpty(config.getAccountSid()) || StringUtils.isEmpty(config.getAccountToken()) || StringUtils.isEmpty(config.getNumberFrom())) { | |
34 | + throw new IllegalArgumentException("Invalid twilio sms provider configuration: accountSid, accountToken and numberFrom should be specified!"); | |
35 | + } | |
36 | + this.numberFrom = this.validatePhoneNumber(config.getNumberFrom()); | |
37 | + this.twilioRestClient = new TwilioRestClient.Builder(config.getAccountSid(), config.getAccountToken()).build(); | |
38 | + } | |
39 | + | |
40 | + @Override | |
41 | + public int sendSms(String numberTo, String message) throws SmsException { | |
42 | + numberTo = this.validatePhoneNumber(numberTo); | |
43 | + message = this.prepareMessage(message); | |
44 | + try { | |
45 | + String numSegments = Message.creator(new PhoneNumber(numberTo), new PhoneNumber(this.numberFrom), message).create(this.twilioRestClient).getNumSegments(); | |
46 | + return Integer.valueOf(numSegments); | |
47 | + } catch (Exception e) { | |
48 | + throw new SmsSendException("Failed to send SMS message - " + e.getMessage(), e); | |
49 | + } | |
50 | + } | |
51 | + | |
52 | + @Override | |
53 | + public void destroy() { | |
54 | + | |
55 | + } | |
56 | +} | ... | ... |
... | ... | @@ -21,12 +21,6 @@ import org.springframework.context.annotation.Lazy; |
21 | 21 | import org.springframework.context.event.EventListener; |
22 | 22 | import org.springframework.stereotype.Service; |
23 | 23 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
24 | -import org.thingsboard.server.common.data.EntityType; | |
25 | -import org.thingsboard.server.common.data.EntityView; | |
26 | -import org.thingsboard.server.common.data.id.EntityId; | |
27 | -import org.thingsboard.server.common.data.id.EntityViewId; | |
28 | -import org.thingsboard.server.common.data.id.TenantId; | |
29 | -import org.thingsboard.server.dao.entityview.EntityViewService; | |
30 | 24 | import org.thingsboard.server.gen.transport.TransportProtos; |
31 | 25 | import org.thingsboard.server.queue.discovery.ClusterTopologyChangeEvent; |
32 | 26 | import org.thingsboard.server.queue.discovery.PartitionChangeEvent; |
... | ... | @@ -36,7 +30,6 @@ import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
36 | 30 | import org.thingsboard.server.common.msg.queue.TbCallback; |
37 | 31 | import org.thingsboard.server.queue.util.TbCoreComponent; |
38 | 32 | import org.thingsboard.server.service.queue.TbClusterService; |
39 | -import org.thingsboard.server.service.telemetry.DefaultTelemetryWebSocketService; | |
40 | 33 | import org.thingsboard.server.service.telemetry.sub.AlarmSubscriptionUpdate; |
41 | 34 | import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; |
42 | 35 | |
... | ... | @@ -49,7 +42,6 @@ import java.util.Set; |
49 | 42 | import java.util.concurrent.ConcurrentHashMap; |
50 | 43 | import java.util.concurrent.ExecutorService; |
51 | 44 | import java.util.concurrent.Executors; |
52 | -import java.util.stream.Collectors; | |
53 | 45 | |
54 | 46 | @Slf4j |
55 | 47 | @TbCoreComponent |
... | ... | @@ -60,9 +52,6 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer |
60 | 52 | private final Map<String, Map<Integer, TbSubscription>> subscriptionsBySessionId = new ConcurrentHashMap<>(); |
61 | 53 | |
62 | 54 | @Autowired |
63 | - private EntityViewService entityViewService; | |
64 | - | |
65 | - @Autowired | |
66 | 55 | private PartitionService partitionService; |
67 | 56 | |
68 | 57 | @Autowired |
... | ... | @@ -72,17 +61,17 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer |
72 | 61 | @Lazy |
73 | 62 | private SubscriptionManagerService subscriptionManagerService; |
74 | 63 | |
75 | - private ExecutorService wsCallBackExecutor; | |
64 | + private ExecutorService subscriptionUpdateExecutor; | |
76 | 65 | |
77 | 66 | @PostConstruct |
78 | 67 | public void initExecutor() { |
79 | - wsCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("ws-sub-callback")); | |
68 | + subscriptionUpdateExecutor = Executors.newWorkStealingPool(20); | |
80 | 69 | } |
81 | 70 | |
82 | 71 | @PreDestroy |
83 | 72 | public void shutdownExecutor() { |
84 | - if (wsCallBackExecutor != null) { | |
85 | - wsCallBackExecutor.shutdownNow(); | |
73 | + if (subscriptionUpdateExecutor != null) { | |
74 | + subscriptionUpdateExecutor.shutdownNow(); | |
86 | 75 | } |
87 | 76 | } |
88 | 77 | |
... | ... | @@ -148,7 +137,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer |
148 | 137 | update.getLatestValues().forEach((key, value) -> attrSub.getKeyStates().put(key, value)); |
149 | 138 | break; |
150 | 139 | } |
151 | - subscription.getUpdateConsumer().accept(sessionId, update); | |
140 | + subscriptionUpdateExecutor.submit(() -> subscription.getUpdateConsumer().accept(sessionId, update)); | |
152 | 141 | } |
153 | 142 | callback.onSuccess(); |
154 | 143 | } |
... | ... | @@ -158,7 +147,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer |
158 | 147 | TbSubscription subscription = subscriptionsBySessionId |
159 | 148 | .getOrDefault(sessionId, Collections.emptyMap()).get(update.getSubscriptionId()); |
160 | 149 | if (subscription != null && subscription.getType() == TbSubscriptionType.ALARMS) { |
161 | - subscription.getUpdateConsumer().accept(sessionId, update); | |
150 | + subscriptionUpdateExecutor.submit(() -> subscription.getUpdateConsumer().accept(sessionId, update)); | |
162 | 151 | } |
163 | 152 | callback.onSuccess(); |
164 | 153 | } | ... | ... |
... | ... | @@ -54,12 +54,16 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
54 | 54 | import org.thingsboard.server.dao.relation.RelationService; |
55 | 55 | import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
56 | 56 | import org.thingsboard.server.gen.transport.TransportProtos; |
57 | +import org.thingsboard.server.gen.transport.TransportProtos.DeviceCredentialsProto; | |
57 | 58 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
58 | 59 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; |
59 | 60 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; |
60 | 61 | import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg; |
61 | 62 | import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg; |
62 | 63 | import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceRequestMsg; |
64 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; | |
65 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsgOrBuilder; | |
66 | +import org.thingsboard.server.gen.transport.TransportProtos.ProvisionResponseStatus; | |
63 | 67 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; |
64 | 68 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; |
65 | 69 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; |
... | ... | @@ -174,6 +178,8 @@ public class DefaultTransportApiService implements TransportApiService { |
174 | 178 | if (!checkMqttCredentials(mqtt, credentials)) { |
175 | 179 | credentials = null; |
176 | 180 | } |
181 | + } else { | |
182 | + return getEmptyTransportApiResponseFuture(); | |
177 | 183 | } |
178 | 184 | } |
179 | 185 | if (credentials == null) { |
... | ... | @@ -292,30 +298,32 @@ public class DefaultTransportApiService implements TransportApiService { |
292 | 298 | requestMsg.getProvisionDeviceCredentialsMsg().getProvisionDeviceSecret())))); |
293 | 299 | } catch (ProvisionFailedException e) { |
294 | 300 | return Futures.immediateFuture(getTransportApiResponseMsg( |
295 | - TransportProtos.DeviceCredentialsProto.getDefaultInstance(), | |
301 | + new DeviceCredentials(), | |
296 | 302 | TransportProtos.ProvisionResponseStatus.valueOf(e.getMessage()))); |
297 | 303 | } |
298 | - return Futures.transform(provisionResponseFuture, provisionResponse -> getTransportApiResponseMsg( | |
299 | - getDeviceCredentials(provisionResponse.getDeviceCredentials()), TransportProtos.ProvisionResponseStatus.SUCCESS), | |
304 | + return Futures.transform(provisionResponseFuture, provisionResponse -> getTransportApiResponseMsg(provisionResponse.getDeviceCredentials(), TransportProtos.ProvisionResponseStatus.SUCCESS), | |
300 | 305 | dbCallbackExecutorService); |
301 | 306 | } |
302 | 307 | |
303 | - private TransportApiResponseMsg getTransportApiResponseMsg(TransportProtos.DeviceCredentialsProto deviceCredentials, TransportProtos.ProvisionResponseStatus status) { | |
304 | - return TransportApiResponseMsg.newBuilder() | |
305 | - .setProvisionDeviceResponseMsg(TransportProtos.ProvisionDeviceResponseMsg.newBuilder() | |
306 | - .setDeviceCredentials(deviceCredentials) | |
307 | - .setProvisionResponseStatus(status) | |
308 | - .build()) | |
309 | - .build(); | |
310 | - } | |
308 | + private TransportApiResponseMsg getTransportApiResponseMsg(DeviceCredentials deviceCredentials, TransportProtos.ProvisionResponseStatus status) { | |
309 | + if (!status.equals(ProvisionResponseStatus.SUCCESS)) { | |
310 | + return TransportApiResponseMsg.newBuilder().setProvisionDeviceResponseMsg(TransportProtos.ProvisionDeviceResponseMsg.newBuilder().setStatus(status).build()).build(); | |
311 | + } | |
312 | + TransportProtos.ProvisionDeviceResponseMsg.Builder provisionResponse = TransportProtos.ProvisionDeviceResponseMsg.newBuilder() | |
313 | + .setCredentialsType(TransportProtos.CredentialsType.valueOf(deviceCredentials.getCredentialsType().name())) | |
314 | + .setStatus(status); | |
315 | + switch (deviceCredentials.getCredentialsType()){ | |
316 | + case ACCESS_TOKEN: | |
317 | + provisionResponse.setCredentialsValue(deviceCredentials.getCredentialsId()); | |
318 | + break; | |
319 | + case MQTT_BASIC: | |
320 | + case X509_CERTIFICATE: | |
321 | + provisionResponse.setCredentialsValue(deviceCredentials.getCredentialsValue()); | |
322 | + break; | |
323 | + } | |
311 | 324 | |
312 | - private TransportProtos.DeviceCredentialsProto getDeviceCredentials(DeviceCredentials deviceCredentials) { | |
313 | - return TransportProtos.DeviceCredentialsProto.newBuilder() | |
314 | - .setDeviceIdMSB(deviceCredentials.getDeviceId().getId().getMostSignificantBits()) | |
315 | - .setDeviceIdLSB(deviceCredentials.getDeviceId().getId().getLeastSignificantBits()) | |
316 | - .setCredentialsType(TransportProtos.CredentialsType.valueOf(deviceCredentials.getCredentialsType().name())) | |
317 | - .setCredentialsId(deviceCredentials.getCredentialsId()) | |
318 | - .setCredentialsValue(deviceCredentials.getCredentialsValue() != null ? deviceCredentials.getCredentialsValue() : "") | |
325 | + return TransportApiResponseMsg.newBuilder() | |
326 | + .setProvisionDeviceResponseMsg(provisionResponse.build()) | |
319 | 327 | .build(); |
320 | 328 | } |
321 | 329 | ... | ... |
... | ... | @@ -3,4 +3,5 @@ activation.subject=Your account activation on Thingsboard |
3 | 3 | account.activated.subject=Thingsboard - your account has been activated |
4 | 4 | reset.password.subject=Thingsboard - Password reset has been requested |
5 | 5 | password.was.reset.subject=Thingsboard - your account password has been reset |
6 | -account.lockout.subject=Thingsboard - User account has been lockout | |
\ No newline at end of file | ||
6 | +account.lockout.subject=Thingsboard - User account has been lockout | |
7 | +api.usage.state=Thingsboard - Api Usage State for tenant has been updated | |
\ No newline at end of file | ... | ... |
1 | +<#-- | |
2 | + | |
3 | + Copyright © 2016-2020 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
19 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
20 | +<html xmlns="http://www.w3.org/1999/xhtml" | |
21 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
22 | +<head> | |
23 | + <meta name="viewport" content="width=device-width"/> | |
24 | + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | |
25 | + <title>Thingsboard - Api Usage State</title> | |
26 | + | |
27 | + | |
28 | + <style type="text/css"> | |
29 | + img { | |
30 | + max-width: 100%; | |
31 | + } | |
32 | + | |
33 | + body { | |
34 | + -webkit-font-smoothing: antialiased; | |
35 | + -webkit-text-size-adjust: none; | |
36 | + width: 100% !important; | |
37 | + height: 100%; | |
38 | + line-height: 1.6em; | |
39 | + } | |
40 | + | |
41 | + body { | |
42 | + background-color: #f6f6f6; | |
43 | + } | |
44 | + | |
45 | + @media only screen and (max-width: 640px) { | |
46 | + body { | |
47 | + padding: 0 !important; | |
48 | + } | |
49 | + | |
50 | + h1 { | |
51 | + font-weight: 800 !important; | |
52 | + margin: 20px 0 5px !important; | |
53 | + } | |
54 | + | |
55 | + h2 { | |
56 | + font-weight: 800 !important; | |
57 | + margin: 20px 0 5px !important; | |
58 | + } | |
59 | + | |
60 | + h3 { | |
61 | + font-weight: 800 !important; | |
62 | + margin: 20px 0 5px !important; | |
63 | + } | |
64 | + | |
65 | + h4 { | |
66 | + font-weight: 800 !important; | |
67 | + margin: 20px 0 5px !important; | |
68 | + } | |
69 | + | |
70 | + h1 { | |
71 | + font-size: 22px !important; | |
72 | + } | |
73 | + | |
74 | + h2 { | |
75 | + font-size: 18px !important; | |
76 | + } | |
77 | + | |
78 | + h3 { | |
79 | + font-size: 16px !important; | |
80 | + } | |
81 | + | |
82 | + .container { | |
83 | + padding: 0 !important; | |
84 | + width: 100% !important; | |
85 | + } | |
86 | + | |
87 | + .content { | |
88 | + padding: 0 !important; | |
89 | + } | |
90 | + | |
91 | + .content-wrap { | |
92 | + padding: 10px !important; | |
93 | + } | |
94 | + | |
95 | + .invoice { | |
96 | + width: 100% !important; | |
97 | + } | |
98 | + } | |
99 | + </style> | |
100 | +</head> | |
101 | + | |
102 | +<body itemscope itemtype="http://schema.org/EmailMessage" | |
103 | + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" | |
104 | + bgcolor="#f6f6f6"> | |
105 | + | |
106 | +<table class="main" | |
107 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; border-radius: 3px; width: 100%; background-color: #f6f6f6; margin: 0px auto;" | |
108 | + cellspacing="0" cellpadding="0" bgcolor="#f6f6f6"> | |
109 | + <tbody> | |
110 | + <tr style="box-sizing: border-box; margin: 0px;"> | |
111 | + <td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" | |
112 | + align="center" valign="top"> | |
113 | + <table style="box-sizing: border-box; border: 1px solid #e9e9e9; border-radius: 3px; margin: 0px; height: 223px; padding: 20px; background-color: #ffffff; width: 600px; max-width: 600px !important;" | |
114 | + width="600" cellspacing="0" cellpadding="0"> | |
115 | + <tbody> | |
116 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
117 | + <td class="content-block" | |
118 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 84px;" | |
119 | + valign="top"> | |
120 | + <h2>Your ThingsBoard account feature was disabled</h2> | |
121 | + </td> | |
122 | + </tr> | |
123 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
124 | + <td class="content-block" | |
125 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | |
126 | + valign="top">We have disabled the ${apiFeature} for your account because ThingsBoard has already ${apiLimitValueLabel}. | |
127 | + </td> | |
128 | + </tr> | |
129 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
130 | + <td class="content-block" | |
131 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 59px;" | |
132 | + valign="top">Please contact your system administrator to resolve the issue. | |
133 | + </td> | |
134 | + </tr> | |
135 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
136 | + <td class="content-block" | |
137 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | |
138 | + valign="top">— The ThingsBoard | |
139 | + </td> | |
140 | + </tr> | |
141 | + </tbody> | |
142 | + </table> | |
143 | + </td> | |
144 | + </tr> | |
145 | + </tbody> | |
146 | +</table> | |
147 | +<table style="color: #999999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; margin: 0px auto; height: 64px; background-color: #f6f6f6; width: 100%;" | |
148 | + cellpadding="0px 0px 20px"> | |
149 | + <tbody> | |
150 | + <tr style="box-sizing: border-box; margin: 0px;"> | |
151 | + <td class="aligncenter content-block" | |
152 | + style="box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0px 0px 20px; width: 600px; text-align: center; vertical-align: middle;" | |
153 | + align="center" valign="top">This email was sent to <a | |
154 | + style="box-sizing: border-box; color: #999999; margin: 0px;" | |
155 | + href="mailto:${targetEmail}">${targetEmail}</a> by ThingsBoard. | |
156 | + </td> | |
157 | + </tr> | |
158 | + </tbody> | |
159 | +</table> | |
160 | +</body> | |
161 | +</html> | ... | ... |
1 | +<#-- | |
2 | + | |
3 | + Copyright © 2016-2020 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
19 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
20 | +<html xmlns="http://www.w3.org/1999/xhtml" | |
21 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
22 | +<head> | |
23 | + <meta name="viewport" content="width=device-width"/> | |
24 | + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | |
25 | + <title>Thingsboard - Api Usage State</title> | |
26 | + | |
27 | + | |
28 | + <style type="text/css"> | |
29 | + img { | |
30 | + max-width: 100%; | |
31 | + } | |
32 | + | |
33 | + body { | |
34 | + -webkit-font-smoothing: antialiased; | |
35 | + -webkit-text-size-adjust: none; | |
36 | + width: 100% !important; | |
37 | + height: 100%; | |
38 | + line-height: 1.6em; | |
39 | + } | |
40 | + | |
41 | + body { | |
42 | + background-color: #f6f6f6; | |
43 | + } | |
44 | + | |
45 | + @media only screen and (max-width: 640px) { | |
46 | + body { | |
47 | + padding: 0 !important; | |
48 | + } | |
49 | + | |
50 | + h1 { | |
51 | + font-weight: 800 !important; | |
52 | + margin: 20px 0 5px !important; | |
53 | + } | |
54 | + | |
55 | + h2 { | |
56 | + font-weight: 800 !important; | |
57 | + margin: 20px 0 5px !important; | |
58 | + } | |
59 | + | |
60 | + h3 { | |
61 | + font-weight: 800 !important; | |
62 | + margin: 20px 0 5px !important; | |
63 | + } | |
64 | + | |
65 | + h4 { | |
66 | + font-weight: 800 !important; | |
67 | + margin: 20px 0 5px !important; | |
68 | + } | |
69 | + | |
70 | + h1 { | |
71 | + font-size: 22px !important; | |
72 | + } | |
73 | + | |
74 | + h2 { | |
75 | + font-size: 18px !important; | |
76 | + } | |
77 | + | |
78 | + h3 { | |
79 | + font-size: 16px !important; | |
80 | + } | |
81 | + | |
82 | + .container { | |
83 | + padding: 0 !important; | |
84 | + width: 100% !important; | |
85 | + } | |
86 | + | |
87 | + .content { | |
88 | + padding: 0 !important; | |
89 | + } | |
90 | + | |
91 | + .content-wrap { | |
92 | + padding: 10px !important; | |
93 | + } | |
94 | + | |
95 | + .invoice { | |
96 | + width: 100% !important; | |
97 | + } | |
98 | + } | |
99 | + </style> | |
100 | +</head> | |
101 | + | |
102 | +<body itemscope itemtype="http://schema.org/EmailMessage" | |
103 | + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" | |
104 | + bgcolor="#f6f6f6"> | |
105 | + | |
106 | +<table class="main" | |
107 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; border-radius: 3px; width: 100%; background-color: #f6f6f6; margin: 0px auto;" | |
108 | + cellspacing="0" cellpadding="0" bgcolor="#f6f6f6"> | |
109 | + <tbody> | |
110 | + <tr style="box-sizing: border-box; margin: 0px;"> | |
111 | + <td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" | |
112 | + align="center" valign="top"> | |
113 | + <table style="box-sizing: border-box; border: 1px solid #e9e9e9; border-radius: 3px; margin: 0px; height: 223px; padding: 20px; background-color: #ffffff; width: 600px; max-width: 600px !important;" | |
114 | + width="600" cellspacing="0" cellpadding="0"> | |
115 | + <tbody> | |
116 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
117 | + <td class="content-block" | |
118 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 84px;" | |
119 | + valign="top"> | |
120 | + <h2>Your ThingsBoard account feature was enabled</h2> | |
121 | + </td> | |
122 | + </tr> | |
123 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
124 | + <td class="content-block" | |
125 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | |
126 | + valign="top">We have enabled the ${apiFeature} for your account and ThingsBoard is already able to ${apiLabel} messages. | |
127 | + </td> | |
128 | + </tr> | |
129 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
130 | + <td class="content-block" | |
131 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | |
132 | + valign="top">— The ThingsBoard | |
133 | + </td> | |
134 | + </tr> | |
135 | + </tbody> | |
136 | + </table> | |
137 | + </td> | |
138 | + </tr> | |
139 | + </tbody> | |
140 | +</table> | |
141 | +<table style="color: #999999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; margin: 0px auto; height: 64px; background-color: #f6f6f6; width: 100%;" | |
142 | + cellpadding="0px 0px 20px"> | |
143 | + <tbody> | |
144 | + <tr style="box-sizing: border-box; margin: 0px;"> | |
145 | + <td class="aligncenter content-block" | |
146 | + style="box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0px 0px 20px; width: 600px; text-align: center; vertical-align: middle;" | |
147 | + align="center" valign="top">This email was sent to <a | |
148 | + style="box-sizing: border-box; color: #999999; margin: 0px;" | |
149 | + href="mailto:${targetEmail}">${targetEmail}</a> by ThingsBoard. | |
150 | + </td> | |
151 | + </tr> | |
152 | + </tbody> | |
153 | +</table> | |
154 | +</body> | |
155 | +</html> | ... | ... |
1 | +<#-- | |
2 | + | |
3 | + Copyright © 2016-2020 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
19 | + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
20 | +<html xmlns="http://www.w3.org/1999/xhtml" | |
21 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
22 | +<head> | |
23 | + <meta name="viewport" content="width=device-width"/> | |
24 | + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | |
25 | + <title>Thingsboard - Api Usage State</title> | |
26 | + | |
27 | + | |
28 | + <style type="text/css"> | |
29 | + img { | |
30 | + max-width: 100%; | |
31 | + } | |
32 | + | |
33 | + body { | |
34 | + -webkit-font-smoothing: antialiased; | |
35 | + -webkit-text-size-adjust: none; | |
36 | + width: 100% !important; | |
37 | + height: 100%; | |
38 | + line-height: 1.6em; | |
39 | + } | |
40 | + | |
41 | + body { | |
42 | + background-color: #f6f6f6; | |
43 | + } | |
44 | + | |
45 | + @media only screen and (max-width: 640px) { | |
46 | + body { | |
47 | + padding: 0 !important; | |
48 | + } | |
49 | + | |
50 | + h1 { | |
51 | + font-weight: 800 !important; | |
52 | + margin: 20px 0 5px !important; | |
53 | + } | |
54 | + | |
55 | + h2 { | |
56 | + font-weight: 800 !important; | |
57 | + margin: 20px 0 5px !important; | |
58 | + } | |
59 | + | |
60 | + h3 { | |
61 | + font-weight: 800 !important; | |
62 | + margin: 20px 0 5px !important; | |
63 | + } | |
64 | + | |
65 | + h4 { | |
66 | + font-weight: 800 !important; | |
67 | + margin: 20px 0 5px !important; | |
68 | + } | |
69 | + | |
70 | + h1 { | |
71 | + font-size: 22px !important; | |
72 | + } | |
73 | + | |
74 | + h2 { | |
75 | + font-size: 18px !important; | |
76 | + } | |
77 | + | |
78 | + h3 { | |
79 | + font-size: 16px !important; | |
80 | + } | |
81 | + | |
82 | + .container { | |
83 | + padding: 0 !important; | |
84 | + width: 100% !important; | |
85 | + } | |
86 | + | |
87 | + .content { | |
88 | + padding: 0 !important; | |
89 | + } | |
90 | + | |
91 | + .content-wrap { | |
92 | + padding: 10px !important; | |
93 | + } | |
94 | + | |
95 | + .invoice { | |
96 | + width: 100% !important; | |
97 | + } | |
98 | + } | |
99 | + </style> | |
100 | +</head> | |
101 | + | |
102 | +<body itemscope itemtype="http://schema.org/EmailMessage" | |
103 | + style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;" | |
104 | + bgcolor="#f6f6f6"> | |
105 | + | |
106 | +<table class="main" | |
107 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; border-radius: 3px; width: 100%; background-color: #f6f6f6; margin: 0px auto;" | |
108 | + cellspacing="0" cellpadding="0" bgcolor="#f6f6f6"> | |
109 | + <tbody> | |
110 | + <tr style="box-sizing: border-box; margin: 0px;"> | |
111 | + <td class="content-wrap" style="box-sizing: border-box; vertical-align: top; margin: 0px; padding: 20px;" | |
112 | + align="center" valign="top"> | |
113 | + <table style="box-sizing: border-box; border: 1px solid #e9e9e9; border-radius: 3px; margin: 0px; height: 223px; padding: 20px; background-color: #ffffff; width: 600px; max-width: 600px !important;" | |
114 | + width="600" cellspacing="0" cellpadding="0"> | |
115 | + <tbody> | |
116 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
117 | + <td class="content-block" | |
118 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; color: #348eda; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 84px;" | |
119 | + valign="top"> | |
120 | + <h2>Your ThingsBoard account feature may be disabled soon</h2> | |
121 | + </td> | |
122 | + </tr> | |
123 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
124 | + <td class="content-block" | |
125 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | |
126 | + valign="top"> | |
127 | + ThingsBoard has already ${apiValueLabel}.<br> ${apiFeature} will be disabled for your account once the limit will be reached. | |
128 | + </td> | |
129 | + </tr> | |
130 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
131 | + <td class="content-block" | |
132 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 59px;" | |
133 | + valign="top">Please contact your system administrator to resolve the issue. | |
134 | + </td> | |
135 | + </tr> | |
136 | + <tr style="font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;"> | |
137 | + <td class="content-block" | |
138 | + style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0px; padding: 0px 0px 20px; height: 40px;" | |
139 | + valign="top">— The ThingsBoard | |
140 | + </td> | |
141 | + </tr> | |
142 | + </tbody> | |
143 | + </table> | |
144 | + </td> | |
145 | + </tr> | |
146 | + </tbody> | |
147 | +</table> | |
148 | +<table style="color: #999999; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; box-sizing: border-box; margin: 0px auto; height: 64px; background-color: #f6f6f6; width: 100%;" | |
149 | + cellpadding="0px 0px 20px"> | |
150 | + <tbody> | |
151 | + <tr style="box-sizing: border-box; margin: 0px;"> | |
152 | + <td class="aligncenter content-block" | |
153 | + style="box-sizing: border-box; font-size: 12px; margin: 0px; padding: 0px 0px 20px; width: 600px; text-align: center; vertical-align: middle;" | |
154 | + align="center" valign="top">This email was sent to <a | |
155 | + style="box-sizing: border-box; color: #999999; margin: 0px;" | |
156 | + href="mailto:${targetEmail}">${targetEmail}</a> by ThingsBoard. | |
157 | + </td> | |
158 | + </tr> | |
159 | + </tbody> | |
160 | +</table> | |
161 | +</body> | |
162 | +</html> | ... | ... |
... | ... | @@ -281,8 +281,12 @@ actors: |
281 | 281 | js_thread_pool_size: "${ACTORS_RULE_JS_THREAD_POOL_SIZE:50}" |
282 | 282 | # Specify thread pool size for mail sender executor service |
283 | 283 | mail_thread_pool_size: "${ACTORS_RULE_MAIL_THREAD_POOL_SIZE:50}" |
284 | + # Specify thread pool size for sms sender executor service | |
285 | + sms_thread_pool_size: "${ACTORS_RULE_SMS_THREAD_POOL_SIZE:50}" | |
284 | 286 | # Whether to allow usage of system mail service for rules |
285 | 287 | allow_system_mail_service: "${ACTORS_RULE_ALLOW_SYSTEM_MAIL_SERVICE:true}" |
288 | + # Whether to allow usage of system sms service for rules | |
289 | + allow_system_sms_service: "${ACTORS_RULE_ALLOW_SYSTEM_SMS_SERVICE:true}" | |
286 | 290 | # Specify thread pool size for external call service |
287 | 291 | external_call_thread_pool_size: "${ACTORS_RULE_EXTERNAL_CALL_THREAD_POOL_SIZE:50}" |
288 | 292 | chain: |
... | ... | @@ -591,6 +595,7 @@ queue: |
591 | 595 | linger.ms: "${TB_KAFKA_LINGER_MS:1}" |
592 | 596 | buffer.memory: "${TB_BUFFER_MEMORY:33554432}" |
593 | 597 | replication_factor: "${TB_QUEUE_KAFKA_REPLICATION_FACTOR:1}" |
598 | + max_poll_interval_ms: "${TB_QUEUE_KAFKA_MAX_POLL_INTERVAL_MS:0}" | |
594 | 599 | max_poll_records: "${TB_QUEUE_KAFKA_MAX_POLL_RECORDS:8192}" |
595 | 600 | max_partition_fetch_bytes: "${TB_QUEUE_KAFKA_MAX_PARTITION_FETCH_BYTES:16777216}" |
596 | 601 | fetch_max_bytes: "${TB_QUEUE_KAFKA_FETCH_MAX_BYTES:134217728}" | ... | ... |
... | ... | @@ -15,7 +15,6 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | -import com.datastax.oss.driver.api.core.uuid.Uuids; | |
19 | 18 | import com.fasterxml.jackson.core.type.TypeReference; |
20 | 19 | import com.fasterxml.jackson.databind.JsonNode; |
21 | 20 | import com.fasterxml.jackson.databind.ObjectMapper; |
... | ... | @@ -33,12 +32,7 @@ import org.junit.Rule; |
33 | 32 | import org.junit.rules.TestRule; |
34 | 33 | import org.junit.rules.TestWatcher; |
35 | 34 | import org.junit.runner.Description; |
36 | -import org.junit.runner.RunWith; | |
37 | 35 | import org.springframework.beans.factory.annotation.Autowired; |
38 | -import org.springframework.boot.test.context.SpringBootContextLoader; | |
39 | -import org.springframework.boot.test.context.SpringBootTest; | |
40 | -import org.springframework.context.annotation.ComponentScan; | |
41 | -import org.springframework.context.annotation.Configuration; | |
42 | 36 | import org.springframework.http.HttpHeaders; |
43 | 37 | import org.springframework.http.MediaType; |
44 | 38 | import org.springframework.http.converter.HttpMessageConverter; |
... | ... | @@ -46,10 +40,6 @@ import org.springframework.http.converter.StringHttpMessageConverter; |
46 | 40 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; |
47 | 41 | import org.springframework.mock.http.MockHttpInputMessage; |
48 | 42 | import org.springframework.mock.http.MockHttpOutputMessage; |
49 | -import org.springframework.test.annotation.DirtiesContext; | |
50 | -import org.springframework.test.context.ActiveProfiles; | |
51 | -import org.springframework.test.context.ContextConfiguration; | |
52 | -import org.springframework.test.context.junit4.SpringRunner; | |
53 | 43 | import org.springframework.test.web.servlet.MockMvc; |
54 | 44 | import org.springframework.test.web.servlet.MvcResult; |
55 | 45 | import org.springframework.test.web.servlet.ResultActions; |
... | ... | @@ -58,7 +48,6 @@ import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilde |
58 | 48 | import org.springframework.util.LinkedMultiValueMap; |
59 | 49 | import org.springframework.util.MultiValueMap; |
60 | 50 | import org.springframework.web.context.WebApplicationContext; |
61 | -import org.thingsboard.server.common.data.BaseData; | |
62 | 51 | import org.thingsboard.server.common.data.Customer; |
63 | 52 | import org.thingsboard.server.common.data.DeviceProfile; |
64 | 53 | import org.thingsboard.server.common.data.DeviceProfileType; |
... | ... | @@ -68,11 +57,13 @@ import org.thingsboard.server.common.data.User; |
68 | 57 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
69 | 58 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; |
70 | 59 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
71 | -import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; | |
60 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | |
61 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | |
62 | +import org.thingsboard.server.common.data.device.profile.MqttTopics; | |
63 | +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; | |
64 | +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; | |
72 | 65 | import org.thingsboard.server.common.data.id.HasId; |
73 | -import org.thingsboard.server.common.data.id.RuleChainId; | |
74 | 66 | import org.thingsboard.server.common.data.id.TenantId; |
75 | -import org.thingsboard.server.common.data.id.UUIDBased; | |
76 | 67 | import org.thingsboard.server.common.data.page.PageLink; |
77 | 68 | import org.thingsboard.server.common.data.page.TimePageLink; |
78 | 69 | import org.thingsboard.server.common.data.security.Authority; |
... | ... | @@ -330,7 +321,7 @@ public abstract class AbstractWebTest { |
330 | 321 | } |
331 | 322 | } |
332 | 323 | |
333 | - protected DeviceProfile createDeviceProfile(String name) { | |
324 | + protected DeviceProfile createDeviceProfile(String name, DeviceProfileTransportConfiguration deviceProfileTransportConfiguration) { | |
334 | 325 | DeviceProfile deviceProfile = new DeviceProfile(); |
335 | 326 | deviceProfile.setName(name); |
336 | 327 | deviceProfile.setType(DeviceProfileType.DEFAULT); |
... | ... | @@ -338,15 +329,34 @@ public abstract class AbstractWebTest { |
338 | 329 | deviceProfile.setDescription(name + " Test"); |
339 | 330 | DeviceProfileData deviceProfileData = new DeviceProfileData(); |
340 | 331 | DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); |
341 | - DefaultDeviceProfileTransportConfiguration transportConfiguration = new DefaultDeviceProfileTransportConfiguration(); | |
342 | 332 | deviceProfileData.setConfiguration(configuration); |
343 | - deviceProfileData.setTransportConfiguration(transportConfiguration); | |
333 | + if (deviceProfileTransportConfiguration != null) { | |
334 | + deviceProfileData.setTransportConfiguration(deviceProfileTransportConfiguration); | |
335 | + } else { | |
336 | + deviceProfileData.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration()); | |
337 | + } | |
344 | 338 | deviceProfile.setProfileData(deviceProfileData); |
345 | 339 | deviceProfile.setDefault(false); |
346 | 340 | deviceProfile.setDefaultRuleChainId(null); |
347 | 341 | return deviceProfile; |
348 | 342 | } |
349 | 343 | |
344 | + protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration) { | |
345 | + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration(); | |
346 | + mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_TELEMETRY_TOPIC); | |
347 | + mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_ATTRIBUTES_TOPIC); | |
348 | + mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration); | |
349 | + return mqttDeviceProfileTransportConfiguration; | |
350 | + } | |
351 | + | |
352 | + protected ProtoTransportPayloadConfiguration createProtoTransportPayloadConfiguration(String deviceAttributesProtoSchema, String deviceTelemetryProtoSchema) { | |
353 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = new ProtoTransportPayloadConfiguration(); | |
354 | + protoTransportPayloadConfiguration.setDeviceAttributesProtoSchema(deviceAttributesProtoSchema); | |
355 | + protoTransportPayloadConfiguration.setDeviceTelemetryProtoSchema(deviceTelemetryProtoSchema); | |
356 | + return protoTransportPayloadConfiguration; | |
357 | + } | |
358 | + | |
359 | + | |
350 | 360 | protected ResultActions doGet(String urlTemplate, Object... urlVariables) throws Exception { |
351 | 361 | MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables); |
352 | 362 | setJwtToken(getRequest); | ... | ... |
... | ... | @@ -16,6 +16,12 @@ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.core.type.TypeReference; |
19 | +import com.github.os72.protobuf.dynamic.DynamicSchema; | |
20 | +import com.google.protobuf.Descriptors; | |
21 | +import com.google.protobuf.DynamicMessage; | |
22 | +import com.google.protobuf.InvalidProtocolBufferException; | |
23 | +import com.google.protobuf.util.JsonFormat; | |
24 | +import com.squareup.wire.schema.internal.parser.ProtoFileElement; | |
19 | 25 | import org.junit.After; |
20 | 26 | import org.junit.Assert; |
21 | 27 | import org.junit.Before; |
... | ... | @@ -28,7 +34,10 @@ import org.thingsboard.server.common.data.DeviceProfileType; |
28 | 34 | import org.thingsboard.server.common.data.DeviceTransportType; |
29 | 35 | import org.thingsboard.server.common.data.Tenant; |
30 | 36 | import org.thingsboard.server.common.data.User; |
31 | -import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; | |
37 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | |
38 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | |
39 | +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; | |
40 | +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; | |
32 | 41 | import org.thingsboard.server.common.data.page.PageData; |
33 | 42 | import org.thingsboard.server.common.data.page.PageLink; |
34 | 43 | import org.thingsboard.server.common.data.security.Authority; |
... | ... | @@ -36,9 +45,13 @@ import org.thingsboard.server.common.data.security.Authority; |
36 | 45 | import java.util.ArrayList; |
37 | 46 | import java.util.Collections; |
38 | 47 | import java.util.List; |
48 | +import java.util.Set; | |
39 | 49 | import java.util.stream.Collectors; |
40 | 50 | |
41 | 51 | import static org.hamcrest.Matchers.containsString; |
52 | +import static org.junit.Assert.assertEquals; | |
53 | +import static org.junit.Assert.assertNotNull; | |
54 | +import static org.junit.Assert.assertTrue; | |
42 | 55 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
43 | 56 | |
44 | 57 | public abstract class BaseDeviceProfileControllerTest extends AbstractControllerTest { |
... | ... | @@ -78,7 +91,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
78 | 91 | |
79 | 92 | @Test |
80 | 93 | public void testSaveDeviceProfile() throws Exception { |
81 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); | |
94 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); | |
82 | 95 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
83 | 96 | Assert.assertNotNull(savedDeviceProfile); |
84 | 97 | Assert.assertNotNull(savedDeviceProfile.getId()); |
... | ... | @@ -96,7 +109,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
96 | 109 | |
97 | 110 | @Test |
98 | 111 | public void testFindDeviceProfileById() throws Exception { |
99 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); | |
112 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); | |
100 | 113 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
101 | 114 | DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString(), DeviceProfile.class); |
102 | 115 | Assert.assertNotNull(foundDeviceProfile); |
... | ... | @@ -105,7 +118,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
105 | 118 | |
106 | 119 | @Test |
107 | 120 | public void testFindDeviceProfileInfoById() throws Exception { |
108 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); | |
121 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); | |
109 | 122 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
110 | 123 | DeviceProfileInfo foundDeviceProfileInfo = doGet("/api/deviceProfileInfo/"+savedDeviceProfile.getId().getId().toString(), DeviceProfileInfo.class); |
111 | 124 | Assert.assertNotNull(foundDeviceProfileInfo); |
... | ... | @@ -127,7 +140,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
127 | 140 | |
128 | 141 | @Test |
129 | 142 | public void testSetDefaultDeviceProfile() throws Exception { |
130 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile 1"); | |
143 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile 1", null); | |
131 | 144 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
132 | 145 | DeviceProfile defaultDeviceProfile = doPost("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString()+"/default", null, DeviceProfile.class); |
133 | 146 | Assert.assertNotNull(defaultDeviceProfile); |
... | ... | @@ -147,19 +160,19 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
147 | 160 | |
148 | 161 | @Test |
149 | 162 | public void testSaveDeviceProfileWithSameName() throws Exception { |
150 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); | |
163 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); | |
151 | 164 | doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk()); |
152 | - DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile"); | |
165 | + DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile", null); | |
153 | 166 | doPost("/api/deviceProfile", deviceProfile2).andExpect(status().isBadRequest()) |
154 | 167 | .andExpect(statusReason(containsString("Device profile with such name already exists"))); |
155 | 168 | } |
156 | 169 | |
157 | 170 | @Test |
158 | 171 | public void testSaveDeviceProfileWithSameProvisionDeviceKey() throws Exception { |
159 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); | |
172 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); | |
160 | 173 | deviceProfile.setProvisionDeviceKey("testProvisionDeviceKey"); |
161 | 174 | doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk()); |
162 | - DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2"); | |
175 | + DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile 2", null); | |
163 | 176 | deviceProfile2.setProvisionDeviceKey("testProvisionDeviceKey"); |
164 | 177 | doPost("/api/deviceProfile", deviceProfile2).andExpect(status().isBadRequest()) |
165 | 178 | .andExpect(statusReason(containsString("Device profile with such provision device key already exists"))); |
... | ... | @@ -168,7 +181,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
168 | 181 | @Ignore |
169 | 182 | @Test |
170 | 183 | public void testChangeDeviceProfileTypeWithExistingDevices() throws Exception { |
171 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); | |
184 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); | |
172 | 185 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
173 | 186 | Device device = new Device(); |
174 | 187 | device.setName("Test device"); |
... | ... | @@ -183,7 +196,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
183 | 196 | |
184 | 197 | @Test |
185 | 198 | public void testChangeDeviceProfileTransportTypeWithExistingDevices() throws Exception { |
186 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); | |
199 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); | |
187 | 200 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
188 | 201 | Device device = new Device(); |
189 | 202 | device.setName("Test device"); |
... | ... | @@ -197,7 +210,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
197 | 210 | |
198 | 211 | @Test |
199 | 212 | public void testDeleteDeviceProfileWithExistingDevice() throws Exception { |
200 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); | |
213 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); | |
201 | 214 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
202 | 215 | |
203 | 216 | Device device = new Device(); |
... | ... | @@ -214,7 +227,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
214 | 227 | |
215 | 228 | @Test |
216 | 229 | public void testDeleteDeviceProfile() throws Exception { |
217 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); | |
230 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); | |
218 | 231 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
219 | 232 | |
220 | 233 | doDelete("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString()) |
... | ... | @@ -235,7 +248,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
235 | 248 | deviceProfiles.addAll(pageData.getData()); |
236 | 249 | |
237 | 250 | for (int i=0;i<28;i++) { |
238 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i); | |
251 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i, null); | |
239 | 252 | deviceProfiles.add(doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class)); |
240 | 253 | } |
241 | 254 | |
... | ... | @@ -280,7 +293,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
280 | 293 | deviceProfiles.addAll(deviceProfilePageData.getData()); |
281 | 294 | |
282 | 295 | for (int i=0;i<28;i++) { |
283 | - DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i); | |
296 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i, null); | |
284 | 297 | deviceProfiles.add(doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class)); |
285 | 298 | } |
286 | 299 | |
... | ... | @@ -318,4 +331,341 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
318 | 331 | Assert.assertEquals(1, pageData.getTotalElements()); |
319 | 332 | } |
320 | 333 | |
334 | + @Test | |
335 | + public void testSaveProtoDeviceProfileWithInvalidProtoFile() throws Exception { | |
336 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
337 | + "\n" + | |
338 | + "package schemavalidation;\n" + | |
339 | + "\n" + | |
340 | + "message SchemaValidationTest {\n" + | |
341 | + " required int32 parameter = 1;\n" + | |
342 | + "}", "[Transport Configuration] failed to parse attributes proto schema due to: Syntax error in :6:4: 'required' label forbidden in proto3 field declarations"); | |
343 | + } | |
344 | + | |
345 | + @Test | |
346 | + public void testSaveProtoDeviceProfileWithInvalidProtoSyntax() throws Exception { | |
347 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto2\";\n" + | |
348 | + "\n" + | |
349 | + "package schemavalidation;\n" + | |
350 | + "\n" + | |
351 | + "message SchemaValidationTest {\n" + | |
352 | + " required int32 parameter = 1;\n" + | |
353 | + "}", "[Transport Configuration] invalid schema syntax: proto2 for attributes proto schema provided! Only proto3 allowed!"); | |
354 | + } | |
355 | + | |
356 | + @Test | |
357 | + public void testSaveProtoDeviceProfileOptionsNotSupported() throws Exception { | |
358 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
359 | + "\n" + | |
360 | + "option java_package = \"com.test.schemavalidation\";\n" + | |
361 | + "option java_multiple_files = true;\n" + | |
362 | + "\n" + | |
363 | + "package schemavalidation;\n" + | |
364 | + "\n" + | |
365 | + "message SchemaValidationTest {\n" + | |
366 | + " int32 parameter = 1;\n" + | |
367 | + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema options don't support!"); | |
368 | + } | |
369 | + | |
370 | + @Test | |
371 | + public void testSaveProtoDeviceProfilePublicImportsNotSupported() throws Exception { | |
372 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
373 | + "\n" + | |
374 | + "import public \"oldschema.proto\";\n" + | |
375 | + "\n" + | |
376 | + "package schemavalidation;\n" + | |
377 | + "\n" + | |
378 | + "message SchemaValidationTest {\n" + | |
379 | + " int32 parameter = 1;\n" + | |
380 | + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema public imports don't support!"); | |
381 | + } | |
382 | + | |
383 | + @Test | |
384 | + public void testSaveProtoDeviceProfileImportsNotSupported() throws Exception { | |
385 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
386 | + "\n" + | |
387 | + "import \"oldschema.proto\";\n" + | |
388 | + "\n" + | |
389 | + "package schemavalidation;\n" + | |
390 | + "\n" + | |
391 | + "message SchemaValidationTest {\n" + | |
392 | + " int32 parameter = 1;\n" + | |
393 | + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema imports don't support!"); | |
394 | + } | |
395 | + | |
396 | + @Test | |
397 | + public void testSaveProtoDeviceProfileExtendDeclarationsNotSupported() throws Exception { | |
398 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
399 | + "\n" + | |
400 | + "package schemavalidation;\n" + | |
401 | + "\n" + | |
402 | + "extend google.protobuf.MethodOptions {\n" + | |
403 | + " MyMessage my_method_option = 50007;\n" + | |
404 | + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema extend declarations don't support!"); | |
405 | + } | |
406 | + | |
407 | + @Test | |
408 | + public void testSaveProtoDeviceProfileEnumOptionsNotSupported() throws Exception { | |
409 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
410 | + "\n" + | |
411 | + "package schemavalidation;\n" + | |
412 | + "\n" + | |
413 | + "enum testEnum {\n" + | |
414 | + " option allow_alias = true;\n" + | |
415 | + " DEFAULT = 0;\n" + | |
416 | + " STARTED = 1;\n" + | |
417 | + " RUNNING = 2;\n" + | |
418 | + "}\n" + | |
419 | + "\n" + | |
420 | + "message testMessage {\n" + | |
421 | + " int32 parameter = 1;\n" + | |
422 | + "}", "[Transport Configuration] invalid attributes proto schema provided! Enum definitions options are not supported!"); | |
423 | + } | |
424 | + | |
425 | + @Test | |
426 | + public void testSaveProtoDeviceProfileNoOneMessageTypeExists() throws Exception { | |
427 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
428 | + "\n" + | |
429 | + "package schemavalidation;\n" + | |
430 | + "\n" + | |
431 | + "enum testEnum {\n" + | |
432 | + " DEFAULT = 0;\n" + | |
433 | + " STARTED = 1;\n" + | |
434 | + " RUNNING = 2;\n" + | |
435 | + "}", "[Transport Configuration] invalid attributes proto schema provided! At least one Message definition should exists!"); | |
436 | + } | |
437 | + | |
438 | + @Test | |
439 | + public void testSaveProtoDeviceProfileMessageTypeOptionsNotSupported() throws Exception { | |
440 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
441 | + "\n" + | |
442 | + "package schemavalidation;\n" + | |
443 | + "\n" + | |
444 | + "message testMessage {\n" + | |
445 | + " option allow_alias = true;\n" + | |
446 | + " int32 parameter = 1;\n" + | |
447 | + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition options don't support!"); | |
448 | + } | |
449 | + | |
450 | + @Test | |
451 | + public void testSaveProtoDeviceProfileMessageTypeExtensionsNotSupported() throws Exception { | |
452 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
453 | + "\n" + | |
454 | + "package schemavalidation;\n" + | |
455 | + "\n" + | |
456 | + "message TestMessage {\n" + | |
457 | + " extensions 100 to 199;\n" + | |
458 | + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition extensions don't support!"); | |
459 | + } | |
460 | + | |
461 | + @Test | |
462 | + public void testSaveProtoDeviceProfileMessageTypeReservedElementsNotSupported() throws Exception { | |
463 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
464 | + "\n" + | |
465 | + "package schemavalidation;\n" + | |
466 | + "\n" + | |
467 | + "message Foo {\n" + | |
468 | + " reserved 2, 15, 9 to 11;\n" + | |
469 | + " reserved \"foo\", \"bar\";\n" + | |
470 | + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition reserved elements don't support!"); | |
471 | + } | |
472 | + | |
473 | + @Test | |
474 | + public void testSaveProtoDeviceProfileMessageTypeGroupsElementsNotSupported() throws Exception { | |
475 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
476 | + "\n" + | |
477 | + "package schemavalidation;\n" + | |
478 | + "\n" + | |
479 | + "message TestMessage {\n" + | |
480 | + " repeated group Result = 1 {\n" + | |
481 | + " string url = 2;\n" + | |
482 | + " string title = 3;\n" + | |
483 | + " repeated string snippets = 4;\n" + | |
484 | + " }\n" + | |
485 | + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition groups don't support!"); | |
486 | + } | |
487 | + | |
488 | + @Test | |
489 | + public void testSaveProtoDeviceProfileOneOfsGroupsElementsNotSupported() throws Exception { | |
490 | + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + | |
491 | + "\n" + | |
492 | + "package schemavalidation;\n" + | |
493 | + "\n" + | |
494 | + "message SampleMessage {\n" + | |
495 | + " oneof test_oneof {\n" + | |
496 | + " string name = 1;\n" + | |
497 | + " group Result = 2 {\n" + | |
498 | + " \tstring url = 3;\n" + | |
499 | + " \tstring title = 4;\n" + | |
500 | + " \trepeated string snippets = 5;\n" + | |
501 | + " }\n" + | |
502 | + " }" + | |
503 | + "}", "[Transport Configuration] invalid attributes proto schema provided! OneOf definition groups don't support!"); | |
504 | + } | |
505 | + | |
506 | + @Test | |
507 | + public void testSaveProtoDeviceProfileWithMessageNestedTypes() throws Exception { | |
508 | + String schema = "syntax = \"proto3\";\n" + | |
509 | + "\n" + | |
510 | + "package testnested;\n" + | |
511 | + "\n" + | |
512 | + "message Outer {\n" + | |
513 | + " message MiddleAA {\n" + | |
514 | + " message Inner {\n" + | |
515 | + " int64 ival = 1;\n" + | |
516 | + " bool booly = 2;\n" + | |
517 | + " }\n" + | |
518 | + " Inner inner = 1;\n" + | |
519 | + " }\n" + | |
520 | + " message MiddleBB {\n" + | |
521 | + " message Inner {\n" + | |
522 | + " int32 ival = 1;\n" + | |
523 | + " bool booly = 2;\n" + | |
524 | + " }\n" + | |
525 | + " Inner inner = 1;\n" + | |
526 | + " }\n" + | |
527 | + " MiddleAA middleAA = 1;\n" + | |
528 | + " MiddleBB middleBB = 2;\n" + | |
529 | + "}"; | |
530 | + DynamicSchema dynamicSchema = getDynamicSchema(schema); | |
531 | + assertNotNull(dynamicSchema); | |
532 | + Set<String> messageTypes = dynamicSchema.getMessageTypes(); | |
533 | + assertEquals(5, messageTypes.size()); | |
534 | + assertTrue(messageTypes.contains("testnested.Outer")); | |
535 | + assertTrue(messageTypes.contains("testnested.Outer.MiddleAA")); | |
536 | + assertTrue(messageTypes.contains("testnested.Outer.MiddleAA.Inner")); | |
537 | + assertTrue(messageTypes.contains("testnested.Outer.MiddleBB")); | |
538 | + assertTrue(messageTypes.contains("testnested.Outer.MiddleBB.Inner")); | |
539 | + | |
540 | + DynamicMessage.Builder middleAAInnerMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleAA.Inner"); | |
541 | + Descriptors.Descriptor middleAAInnerMsgDescriptor = middleAAInnerMsgBuilder.getDescriptorForType(); | |
542 | + DynamicMessage middleAAInnerMsg = middleAAInnerMsgBuilder | |
543 | + .setField(middleAAInnerMsgDescriptor.findFieldByName("ival"), 1L) | |
544 | + .setField(middleAAInnerMsgDescriptor.findFieldByName("booly"), true) | |
545 | + .build(); | |
546 | + | |
547 | + DynamicMessage.Builder middleAAMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleAA"); | |
548 | + Descriptors.Descriptor middleAAMsgDescriptor = middleAAMsgBuilder.getDescriptorForType(); | |
549 | + DynamicMessage middleAAMsg = middleAAMsgBuilder | |
550 | + .setField(middleAAMsgDescriptor.findFieldByName("inner"), middleAAInnerMsg) | |
551 | + .build(); | |
552 | + | |
553 | + DynamicMessage.Builder middleBBInnerMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleAA.Inner"); | |
554 | + Descriptors.Descriptor middleBBInnerMsgDescriptor = middleBBInnerMsgBuilder.getDescriptorForType(); | |
555 | + DynamicMessage middleBBInnerMsg = middleBBInnerMsgBuilder | |
556 | + .setField(middleBBInnerMsgDescriptor.findFieldByName("ival"), 0L) | |
557 | + .setField(middleBBInnerMsgDescriptor.findFieldByName("booly"), false) | |
558 | + .build(); | |
559 | + | |
560 | + DynamicMessage.Builder middleBBMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer.MiddleBB"); | |
561 | + Descriptors.Descriptor middleBBMsgDescriptor = middleBBMsgBuilder.getDescriptorForType(); | |
562 | + DynamicMessage middleBBMsg = middleBBMsgBuilder | |
563 | + .setField(middleBBMsgDescriptor.findFieldByName("inner"), middleBBInnerMsg) | |
564 | + .build(); | |
565 | + | |
566 | + | |
567 | + DynamicMessage.Builder outerMsgBuilder = dynamicSchema.newMessageBuilder("testnested.Outer"); | |
568 | + Descriptors.Descriptor outerMsgBuilderDescriptor = outerMsgBuilder.getDescriptorForType(); | |
569 | + DynamicMessage outerMsg = outerMsgBuilder | |
570 | + .setField(outerMsgBuilderDescriptor.findFieldByName("middleAA"), middleAAMsg) | |
571 | + .setField(outerMsgBuilderDescriptor.findFieldByName("middleBB"), middleBBMsg) | |
572 | + .build(); | |
573 | + | |
574 | + assertEquals("{\n" + | |
575 | + " \"middleAA\": {\n" + | |
576 | + " \"inner\": {\n" + | |
577 | + " \"ival\": \"1\",\n" + | |
578 | + " \"booly\": true\n" + | |
579 | + " }\n" + | |
580 | + " },\n" + | |
581 | + " \"middleBB\": {\n" + | |
582 | + " \"inner\": {\n" + | |
583 | + " \"ival\": 0,\n" + | |
584 | + " \"booly\": false\n" + | |
585 | + " }\n" + | |
586 | + " }\n" + | |
587 | + "}", dynamicMsgToJson(outerMsgBuilderDescriptor, outerMsg.toByteArray())); | |
588 | + } | |
589 | + | |
590 | + @Test | |
591 | + public void testSaveProtoDeviceProfileWithMessageOneOfs() throws Exception { | |
592 | + String schema = "syntax = \"proto3\";\n" + | |
593 | + "\n" + | |
594 | + "package testoneofs;\n" + | |
595 | + "\n" + | |
596 | + "message SubMessage {\n" + | |
597 | + " repeated string name = 1;\n" + | |
598 | + "}\n" + | |
599 | + "\n" + | |
600 | + "message SampleMessage {\n" + | |
601 | + " oneof testOneOf {\n" + | |
602 | + " string name = 4;\n" + | |
603 | + " SubMessage subMessage = 9;\n" + | |
604 | + " }\n" + | |
605 | + "}"; | |
606 | + DynamicSchema dynamicSchema = getDynamicSchema(schema); | |
607 | + assertNotNull(dynamicSchema); | |
608 | + Set<String> messageTypes = dynamicSchema.getMessageTypes(); | |
609 | + assertEquals(2, messageTypes.size()); | |
610 | + assertTrue(messageTypes.contains("testoneofs.SubMessage")); | |
611 | + assertTrue(messageTypes.contains("testoneofs.SampleMessage")); | |
612 | + | |
613 | + DynamicMessage.Builder sampleMsgBuilder = dynamicSchema.newMessageBuilder("testoneofs.SampleMessage"); | |
614 | + Descriptors.Descriptor sampleMsgDescriptor = sampleMsgBuilder.getDescriptorForType(); | |
615 | + assertNotNull(sampleMsgDescriptor); | |
616 | + | |
617 | + List<Descriptors.FieldDescriptor> fields = sampleMsgDescriptor.getFields(); | |
618 | + assertEquals(2, fields.size()); | |
619 | + DynamicMessage sampleMsg = sampleMsgBuilder | |
620 | + .setField(sampleMsgDescriptor.findFieldByName("name"), "Bob") | |
621 | + .build(); | |
622 | + assertEquals("{\n" + " \"name\": \"Bob\"\n" + "}", dynamicMsgToJson(sampleMsgDescriptor, sampleMsg.toByteArray())); | |
623 | + | |
624 | + DynamicMessage.Builder subMsgBuilder = dynamicSchema.newMessageBuilder("testoneofs.SubMessage"); | |
625 | + Descriptors.Descriptor subMsgDescriptor = subMsgBuilder.getDescriptorForType(); | |
626 | + DynamicMessage subMsg = subMsgBuilder | |
627 | + .addRepeatedField(subMsgDescriptor.findFieldByName("name"), "Alice") | |
628 | + .addRepeatedField(subMsgDescriptor.findFieldByName("name"), "John") | |
629 | + .build(); | |
630 | + | |
631 | + DynamicMessage sampleMsgWithOneOfSubMessage = sampleMsgBuilder.setField(sampleMsgDescriptor.findFieldByName("subMessage"), subMsg).build(); | |
632 | + assertEquals("{\n" + " \"subMessage\": {\n" + " \"name\": [\"Alice\", \"John\"]\n" + " }\n" + "}", | |
633 | + dynamicMsgToJson(sampleMsgDescriptor, sampleMsgWithOneOfSubMessage.toByteArray())); | |
634 | + } | |
635 | + | |
636 | + private DeviceProfile testSaveDeviceProfileWithProtoPayloadType(String schema) throws Exception { | |
637 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema); | |
638 | + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration); | |
639 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration); | |
640 | + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); | |
641 | + DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString(), DeviceProfile.class); | |
642 | + Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfile.getName()); | |
643 | + return savedDeviceProfile; | |
644 | + } | |
645 | + | |
646 | + private void testSaveDeviceProfileWithInvalidProtoSchema(String schema, String errorMsg) throws Exception { | |
647 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = this.createProtoTransportPayloadConfiguration(schema, schema); | |
648 | + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration); | |
649 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration); | |
650 | + doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest()) | |
651 | + .andExpect(statusReason(containsString(errorMsg))); | |
652 | + } | |
653 | + | |
654 | + private DynamicSchema getDynamicSchema(String schema) throws Exception { | |
655 | + DeviceProfile deviceProfile = testSaveDeviceProfileWithProtoPayloadType(schema); | |
656 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
657 | + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); | |
658 | + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; | |
659 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttDeviceProfileTransportConfiguration.getTransportPayloadTypeConfiguration(); | |
660 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
661 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
662 | + ProtoFileElement protoFile = protoTransportPayloadConfiguration.getTransportProtoSchema(schema); | |
663 | + return protoTransportPayloadConfiguration.getDynamicSchema(protoFile, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); | |
664 | + } | |
665 | + | |
666 | + private String dynamicMsgToJson(Descriptors.Descriptor descriptor, byte[] payload) throws InvalidProtocolBufferException { | |
667 | + DynamicMessage dynamicMessage = DynamicMessage.parseFrom(descriptor, payload); | |
668 | + return JsonFormat.printer().includingDefaultValueFields().print(dynamicMessage); | |
669 | + } | |
670 | + | |
321 | 671 | } | ... | ... |
... | ... | @@ -38,7 +38,10 @@ import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileCon |
38 | 38 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
39 | 39 | import org.thingsboard.server.common.data.device.profile.DeviceProfileProvisionConfiguration; |
40 | 40 | import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration; |
41 | +import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; | |
41 | 42 | import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; |
43 | +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; | |
44 | +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; | |
42 | 45 | import org.thingsboard.server.common.data.security.Authority; |
43 | 46 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
44 | 47 | import org.thingsboard.server.controller.AbstractControllerTest; |
... | ... | @@ -47,7 +50,6 @@ import org.thingsboard.server.gen.transport.TransportProtos; |
47 | 50 | import java.util.ArrayList; |
48 | 51 | import java.util.List; |
49 | 52 | import java.util.concurrent.atomic.AtomicInteger; |
50 | -import java.util.function.Supplier; | |
51 | 53 | |
52 | 54 | import static org.junit.Assert.assertEquals; |
53 | 55 | import static org.junit.Assert.assertNotNull; |
... | ... | @@ -60,6 +62,48 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest |
60 | 62 | |
61 | 63 | private static final AtomicInteger atomicInteger = new AtomicInteger(2); |
62 | 64 | |
65 | + public static final String DEVICE_TELEMETRY_PROTO_SCHEMA = "syntax =\"proto3\";\n" + | |
66 | + "\n" + | |
67 | + "package test;\n" + | |
68 | + "\n" + | |
69 | + "message PostTelemetry {\n" + | |
70 | + " string key1 = 1;\n" + | |
71 | + " bool key2 = 2;\n" + | |
72 | + " double key3 = 3;\n" + | |
73 | + " int32 key4 = 4;\n" + | |
74 | + " JsonObject key5 = 5;\n" + | |
75 | + "\n" + | |
76 | + " message JsonObject {\n" + | |
77 | + " int32 someNumber = 6;\n" + | |
78 | + " repeated int32 someArray = 7;\n" + | |
79 | + " NestedJsonObject someNestedObject = 8;\n" + | |
80 | + " message NestedJsonObject {\n" + | |
81 | + " string key = 9;\n" + | |
82 | + " }\n" + | |
83 | + " }\n" + | |
84 | + "}"; | |
85 | + | |
86 | + public static final String DEVICE_ATTRIBUTES_PROTO_SCHEMA = "syntax =\"proto3\";\n" + | |
87 | + "\n" + | |
88 | + "package test;\n" + | |
89 | + "\n" + | |
90 | + "message PostAttributes {\n" + | |
91 | + " string key1 = 1;\n" + | |
92 | + " bool key2 = 2;\n" + | |
93 | + " double key3 = 3;\n" + | |
94 | + " int32 key4 = 4;\n" + | |
95 | + " JsonObject key5 = 5;\n" + | |
96 | + "\n" + | |
97 | + " message JsonObject {\n" + | |
98 | + " int32 someNumber = 6;\n" + | |
99 | + " repeated int32 someArray = 7;\n" + | |
100 | + " NestedJsonObject someNestedObject = 8;\n" + | |
101 | + " message NestedJsonObject {\n" + | |
102 | + " string key = 9;\n" + | |
103 | + " }\n" + | |
104 | + " }\n" + | |
105 | + "}"; | |
106 | + | |
63 | 107 | protected Tenant savedTenant; |
64 | 108 | protected User tenantAdmin; |
65 | 109 | |
... | ... | @@ -69,8 +113,10 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest |
69 | 113 | protected Device savedGateway; |
70 | 114 | protected String gatewayAccessToken; |
71 | 115 | |
116 | + protected DeviceProfile deviceProfile; | |
117 | + | |
72 | 118 | protected void processBeforeTest (String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { |
73 | - this.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic, DeviceProfileProvisionType.DISABLED, null, null); | |
119 | + this.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic, null, null, DeviceProfileProvisionType.DISABLED, null, null); | |
74 | 120 | } |
75 | 121 | |
76 | 122 | protected void processBeforeTest(String deviceName, |
... | ... | @@ -78,6 +124,8 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest |
78 | 124 | TransportPayloadType payloadType, |
79 | 125 | String telemetryTopic, |
80 | 126 | String attributesTopic, |
127 | + String telemetryProtoSchema, | |
128 | + String attributesProtoSchema, | |
81 | 129 | DeviceProfileProvisionType provisionType, |
82 | 130 | String provisionKey, String provisionSecret |
83 | 131 | ) throws Exception { |
... | ... | @@ -109,12 +157,12 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest |
109 | 157 | gateway.setAdditionalInfo(additionalInfo); |
110 | 158 | |
111 | 159 | if (payloadType != null) { |
112 | - DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic, provisionType, provisionKey, provisionSecret); | |
113 | - DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class); | |
114 | - device.setType(savedDeviceProfile.getName()); | |
115 | - device.setDeviceProfileId(savedDeviceProfile.getId()); | |
116 | - gateway.setType(savedDeviceProfile.getName()); | |
117 | - gateway.setDeviceProfileId(savedDeviceProfile.getId()); | |
160 | + DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic, telemetryProtoSchema, attributesProtoSchema, provisionType, provisionKey, provisionSecret); | |
161 | + deviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class); | |
162 | + device.setType(deviceProfile.getName()); | |
163 | + device.setDeviceProfileId(deviceProfile.getId()); | |
164 | + gateway.setType(deviceProfile.getName()); | |
165 | + gateway.setDeviceProfileId(deviceProfile.getId()); | |
118 | 166 | } |
119 | 167 | |
120 | 168 | savedDevice = doPost("/api/device", device, Device.class); |
... | ... | @@ -201,9 +249,9 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest |
201 | 249 | |
202 | 250 | protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType, |
203 | 251 | String telemetryTopic, String attributesTopic, |
252 | + String telemetryProtoSchema, String attributesProtoSchema, | |
204 | 253 | DeviceProfileProvisionType provisionType, |
205 | - String provisionKey, String provisionSecret | |
206 | - ) { | |
254 | + String provisionKey, String provisionSecret) { | |
207 | 255 | DeviceProfile deviceProfile = new DeviceProfile(); |
208 | 256 | deviceProfile.setName(transportPayloadType.name()); |
209 | 257 | deviceProfile.setType(DeviceProfileType.DEFAULT); |
... | ... | @@ -213,15 +261,30 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest |
213 | 261 | deviceProfile.setDescription(transportPayloadType.name() + " Test"); |
214 | 262 | DeviceProfileData deviceProfileData = new DeviceProfileData(); |
215 | 263 | DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); |
216 | - MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration(); | |
217 | - transportConfiguration.setTransportPayloadType(transportPayloadType); | |
264 | + MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration(); | |
218 | 265 | if (!StringUtils.isEmpty(telemetryTopic)) { |
219 | - transportConfiguration.setDeviceTelemetryTopic(telemetryTopic); | |
266 | + mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(telemetryTopic); | |
220 | 267 | } |
221 | 268 | if (!StringUtils.isEmpty(attributesTopic)) { |
222 | - transportConfiguration.setDeviceAttributesTopic(attributesTopic); | |
269 | + mqttDeviceProfileTransportConfiguration.setDeviceAttributesTopic(attributesTopic); | |
223 | 270 | } |
224 | - deviceProfileData.setTransportConfiguration(transportConfiguration); | |
271 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration; | |
272 | + if (TransportPayloadType.JSON.equals(transportPayloadType)) { | |
273 | + transportPayloadTypeConfiguration = new JsonTransportPayloadConfiguration(); | |
274 | + } else { | |
275 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = new ProtoTransportPayloadConfiguration(); | |
276 | + if (StringUtils.isEmpty(telemetryProtoSchema)) { | |
277 | + telemetryProtoSchema = DEVICE_TELEMETRY_PROTO_SCHEMA; | |
278 | + } | |
279 | + if (StringUtils.isEmpty(attributesProtoSchema)) { | |
280 | + attributesProtoSchema = DEVICE_ATTRIBUTES_PROTO_SCHEMA; | |
281 | + } | |
282 | + protoTransportPayloadConfiguration.setDeviceTelemetryProtoSchema(telemetryProtoSchema); | |
283 | + protoTransportPayloadConfiguration.setDeviceAttributesProtoSchema(attributesProtoSchema); | |
284 | + transportPayloadTypeConfiguration = protoTransportPayloadConfiguration; | |
285 | + } | |
286 | + mqttDeviceProfileTransportConfiguration.setTransportPayloadTypeConfiguration(transportPayloadTypeConfiguration); | |
287 | + deviceProfileData.setTransportConfiguration(mqttDeviceProfileTransportConfiguration); | |
225 | 288 | DeviceProfileProvisionConfiguration provisionConfiguration; |
226 | 289 | switch (provisionType) { |
227 | 290 | case ALLOW_CREATE_NEW_DEVICES: |
... | ... | @@ -233,6 +296,7 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest |
233 | 296 | case DISABLED: |
234 | 297 | default: |
235 | 298 | provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(provisionSecret); |
299 | + break; | |
236 | 300 | } |
237 | 301 | deviceProfileData.setProvisionConfiguration(provisionConfiguration); |
238 | 302 | deviceProfileData.setConfiguration(configuration); | ... | ... |
... | ... | @@ -56,7 +56,6 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt |
56 | 56 | return tsKvProtoList; |
57 | 57 | } |
58 | 58 | |
59 | - | |
60 | 59 | protected TransportProtos.TsKvProto getTsKvProto(String key, String value, TransportProtos.KeyValueType keyValueType) { |
61 | 60 | TransportProtos.TsKvProto.Builder tsKvProtoBuilder = TransportProtos.TsKvProto.newBuilder(); |
62 | 61 | TransportProtos.KeyValueProto keyValueProto = getKeyValueProto(key, value, keyValueType); | ... | ... |
... | ... | @@ -18,9 +18,7 @@ package org.thingsboard.server.mqtt.attributes.request; |
18 | 18 | import com.google.protobuf.InvalidProtocolBufferException; |
19 | 19 | import io.netty.handler.codec.mqtt.MqttQoS; |
20 | 20 | import lombok.extern.slf4j.Slf4j; |
21 | -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | |
22 | 21 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
23 | -import org.eclipse.paho.client.mqttv3.MqttCallback; | |
24 | 22 | import org.eclipse.paho.client.mqttv3.MqttException; |
25 | 23 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
26 | 24 | import org.junit.After; |
... | ... | @@ -36,9 +34,7 @@ import java.util.concurrent.CountDownLatch; |
36 | 34 | import java.util.concurrent.TimeUnit; |
37 | 35 | |
38 | 36 | import static org.junit.Assert.assertEquals; |
39 | -import static org.junit.Assert.assertFalse; | |
40 | 37 | import static org.junit.Assert.assertNotNull; |
41 | -import static org.junit.Assert.assertTrue; | |
42 | 38 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
43 | 39 | |
44 | 40 | @Slf4j | ... | ... |
... | ... | @@ -18,14 +18,9 @@ package org.thingsboard.server.mqtt.attributes.request; |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.junit.After; |
20 | 20 | import org.junit.Before; |
21 | -import org.junit.Ignore; | |
22 | 21 | import org.junit.Test; |
23 | 22 | import org.thingsboard.server.common.data.TransportPayloadType; |
24 | 23 | |
25 | -import static org.junit.Assert.assertEquals; | |
26 | -import static org.junit.Assert.assertNotNull; | |
27 | -import static org.junit.Assert.assertTrue; | |
28 | - | |
29 | 24 | @Slf4j |
30 | 25 | public abstract class AbstractMqttAttributesRequestJsonIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { |
31 | 26 | ... | ... |
... | ... | @@ -15,18 +15,26 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.mqtt.attributes.request; |
17 | 17 | |
18 | +import com.github.os72.protobuf.dynamic.DynamicSchema; | |
19 | +import com.google.protobuf.Descriptors; | |
20 | +import com.google.protobuf.DynamicMessage; | |
18 | 21 | import com.google.protobuf.InvalidProtocolBufferException; |
22 | +import com.squareup.wire.schema.internal.parser.ProtoFileElement; | |
19 | 23 | import io.netty.handler.codec.mqtt.MqttQoS; |
20 | 24 | import lombok.extern.slf4j.Slf4j; |
21 | 25 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
22 | 26 | import org.eclipse.paho.client.mqttv3.MqttException; |
23 | 27 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
24 | 28 | import org.junit.After; |
25 | -import org.junit.Before; | |
26 | 29 | import org.junit.Test; |
27 | 30 | import org.thingsboard.server.common.data.Device; |
31 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | |
28 | 32 | import org.thingsboard.server.common.data.TransportPayloadType; |
33 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | |
34 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | |
35 | +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; | |
29 | 36 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
37 | +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; | |
30 | 38 | import org.thingsboard.server.gen.transport.TransportApiProtos; |
31 | 39 | import org.thingsboard.server.gen.transport.TransportProtos; |
32 | 40 | |
... | ... | @@ -38,16 +46,33 @@ import java.util.concurrent.TimeUnit; |
38 | 46 | import java.util.stream.Collectors; |
39 | 47 | |
40 | 48 | import static org.junit.Assert.assertEquals; |
49 | +import static org.junit.Assert.assertNotNull; | |
41 | 50 | import static org.junit.Assert.assertTrue; |
42 | 51 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
43 | 52 | |
44 | 53 | @Slf4j |
45 | 54 | public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { |
46 | 55 | |
47 | - @Before | |
48 | - public void beforeTest() throws Exception { | |
49 | - processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", TransportPayloadType.PROTOBUF, null, null); | |
50 | - } | |
56 | + public static final String ATTRIBUTES_SCHEMA_STR = "syntax =\"proto3\";\n" + | |
57 | + "\n" + | |
58 | + "package test;\n" + | |
59 | + "\n" + | |
60 | + "message PostAttributes {\n" + | |
61 | + " string attribute1 = 1;\n" + | |
62 | + " bool attribute2 = 2;\n" + | |
63 | + " double attribute3 = 3;\n" + | |
64 | + " int32 attribute4 = 4;\n" + | |
65 | + " JsonObject attribute5 = 5;\n" + | |
66 | + "\n" + | |
67 | + " message JsonObject {\n" + | |
68 | + " int32 someNumber = 6;\n" + | |
69 | + " repeated int32 someArray = 7;\n" + | |
70 | + " NestedJsonObject someNestedObject = 8;\n" + | |
71 | + " message NestedJsonObject {\n" + | |
72 | + " string key = 9;\n" + | |
73 | + " }\n" + | |
74 | + " }\n" + | |
75 | + "}"; | |
51 | 76 | |
52 | 77 | @After |
53 | 78 | public void afterTest() throws Exception { |
... | ... | @@ -56,21 +81,55 @@ public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends |
56 | 81 | |
57 | 82 | @Test |
58 | 83 | public void testRequestAttributesValuesFromTheServer() throws Exception { |
84 | + super.processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", | |
85 | + TransportPayloadType.PROTOBUF, null, null, null, ATTRIBUTES_SCHEMA_STR, DeviceProfileProvisionType.DISABLED, null, null); | |
59 | 86 | processTestRequestAttributesValuesFromTheServer(); |
60 | 87 | } |
61 | 88 | |
62 | - | |
63 | 89 | @Test |
64 | 90 | public void testRequestAttributesValuesFromTheServerGateway() throws Exception { |
91 | + super.processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", TransportPayloadType.PROTOBUF, null, null); | |
65 | 92 | processTestGatewayRequestAttributesValuesFromTheServer(); |
66 | 93 | } |
67 | 94 | |
68 | 95 | protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception { |
69 | 96 | doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
70 | - String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; | |
71 | - List<String> expectedKeys = Arrays.asList(keys.split(",")); | |
72 | - TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys); | |
73 | - byte[] payload = postAttributeMsg.toByteArray(); | |
97 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
98 | + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); | |
99 | + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; | |
100 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); | |
101 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
102 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
103 | + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(ATTRIBUTES_SCHEMA_STR); | |
104 | + DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); | |
105 | + | |
106 | + DynamicMessage.Builder nestedJsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject.NestedJsonObject"); | |
107 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | |
108 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | |
109 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | |
110 | + | |
111 | + DynamicMessage.Builder jsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject"); | |
112 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | |
113 | + assertNotNull(jsonObjectBuilderDescriptor); | |
114 | + DynamicMessage jsonObject = jsonObjectBuilder | |
115 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) | |
116 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | |
117 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | |
118 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | |
119 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | |
120 | + .build(); | |
121 | + | |
122 | + DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes"); | |
123 | + Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType(); | |
124 | + assertNotNull(postAttributesMsgDescriptor); | |
125 | + DynamicMessage postAttributesMsg = postAttributesBuilder | |
126 | + .setField(postAttributesMsgDescriptor.findFieldByName("attribute1"), "value1") | |
127 | + .setField(postAttributesMsgDescriptor.findFieldByName("attribute2"), true) | |
128 | + .setField(postAttributesMsgDescriptor.findFieldByName("attribute3"), 42.0) | |
129 | + .setField(postAttributesMsgDescriptor.findFieldByName("attribute4"), 73) | |
130 | + .setField(postAttributesMsgDescriptor.findFieldByName("attribute5"), jsonObject) | |
131 | + .build(); | |
132 | + byte[] payload = postAttributesMsg.toByteArray(); | |
74 | 133 | client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(payload)); |
75 | 134 | client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
76 | 135 | } | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.attributes.request.sql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; | |
20 | 19 | import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; |
21 | 20 | |
22 | 21 | @DaoSqlTest | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.attributes.request.sql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; | |
20 | 19 | import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestProtoIntegrationTest; |
21 | 20 | |
22 | 21 | @DaoSqlTest | ... | ... |
... | ... | @@ -18,11 +18,7 @@ package org.thingsboard.server.mqtt.attributes.updates; |
18 | 18 | import com.google.protobuf.InvalidProtocolBufferException; |
19 | 19 | import io.netty.handler.codec.mqtt.MqttQoS; |
20 | 20 | import lombok.extern.slf4j.Slf4j; |
21 | -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | |
22 | 21 | 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 | 22 | import org.junit.After; |
27 | 23 | import org.junit.Before; |
28 | 24 | import org.junit.Test; |
... | ... | @@ -33,12 +29,10 @@ import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
33 | 29 | import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest; |
34 | 30 | |
35 | 31 | import java.nio.charset.StandardCharsets; |
36 | -import java.util.concurrent.CountDownLatch; | |
37 | 32 | import java.util.concurrent.TimeUnit; |
38 | 33 | |
39 | 34 | import static org.junit.Assert.assertEquals; |
40 | 35 | import static org.junit.Assert.assertNotNull; |
41 | -import static org.junit.Assert.assertTrue; | |
42 | 36 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
43 | 37 | |
44 | 38 | @Slf4j | ... | ... |
... | ... | @@ -21,11 +21,6 @@ import org.junit.Before; |
21 | 21 | import org.junit.Test; |
22 | 22 | import org.thingsboard.server.common.data.TransportPayloadType; |
23 | 23 | |
24 | -import static org.junit.Assert.assertEquals; | |
25 | -import static org.junit.Assert.assertFalse; | |
26 | -import static org.junit.Assert.assertNotNull; | |
27 | -import static org.junit.Assert.assertTrue; | |
28 | - | |
29 | 24 | @Slf4j |
30 | 25 | public abstract class AbstractMqttAttributesUpdatesJsonIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { |
31 | 26 | ... | ... |
... | ... | @@ -21,11 +21,9 @@ import org.junit.After; |
21 | 21 | import org.junit.Before; |
22 | 22 | import org.junit.Test; |
23 | 23 | import org.thingsboard.server.common.data.TransportPayloadType; |
24 | -import org.thingsboard.server.common.data.device.profile.MqttTopics; | |
25 | 24 | import org.thingsboard.server.gen.transport.TransportApiProtos; |
26 | 25 | import org.thingsboard.server.gen.transport.TransportProtos; |
27 | 26 | |
28 | -import java.nio.charset.StandardCharsets; | |
29 | 27 | import java.util.List; |
30 | 28 | import java.util.stream.Collectors; |
31 | 29 | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.attributes.updates.sql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; | |
20 | 19 | import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; |
21 | 20 | |
22 | 21 | @DaoSqlTest | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.attributes.updates.sql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; | |
20 | 19 | import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesProtoIntegrationTest; |
21 | 20 | |
22 | 21 | @DaoSqlTest | ... | ... |
... | ... | @@ -20,7 +20,6 @@ import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
20 | 20 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
21 | 21 | import org.junit.After; |
22 | 22 | import org.junit.Before; |
23 | -import org.junit.Ignore; | |
24 | 23 | import org.junit.Test; |
25 | 24 | import org.thingsboard.server.common.data.ClaimRequest; |
26 | 25 | import org.thingsboard.server.common.data.Customer; | ... | ... |
... | ... | @@ -18,7 +18,6 @@ package org.thingsboard.server.mqtt.claim; |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.junit.After; |
20 | 20 | import org.junit.Before; |
21 | -import org.junit.Ignore; | |
22 | 21 | import org.junit.Test; |
23 | 22 | import org.thingsboard.server.common.data.TransportPayloadType; |
24 | 23 | ... | ... |
... | ... | @@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
20 | 20 | import org.junit.After; |
21 | 21 | import org.junit.Before; |
22 | -import org.junit.Ignore; | |
23 | 22 | import org.junit.Test; |
24 | 23 | import org.thingsboard.server.common.data.TransportPayloadType; |
25 | 24 | import org.thingsboard.server.gen.transport.TransportApiProtos; | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.claim.sql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; | |
20 | 19 | import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; |
21 | 20 | |
22 | 21 | @DaoSqlTest | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.claim.sql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; | |
20 | 19 | import org.thingsboard.server.mqtt.claim.AbstractMqttClaimProtoDeviceTest; |
21 | 20 | |
22 | 21 | @DaoSqlTest | ... | ... |
... | ... | @@ -94,34 +94,32 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn |
94 | 94 | |
95 | 95 | |
96 | 96 | protected void processTestProvisioningDisabledDevice() throws Exception { |
97 | - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.DISABLED, null, null); | |
97 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.DISABLED, null, null); | |
98 | 98 | byte[] result = createMqttClientAndPublish().getPayloadBytes(); |
99 | 99 | JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
100 | 100 | Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); |
101 | - Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("provisionDeviceStatus").getAsString()); | |
101 | + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("status").getAsString()); | |
102 | 102 | } |
103 | 103 | |
104 | 104 | |
105 | 105 | protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { |
106 | - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
106 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
107 | 107 | byte[] result = createMqttClientAndPublish().getPayloadBytes(); |
108 | 108 | JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
109 | 109 | |
110 | 110 | Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
111 | 111 | |
112 | 112 | Assert.assertNotNull(createdDevice); |
113 | - Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); | |
114 | 113 | |
115 | 114 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
116 | 115 | |
117 | 116 | Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); |
118 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | |
119 | - Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); | |
117 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString()); | |
120 | 118 | } |
121 | 119 | |
122 | 120 | |
123 | 121 | protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { |
124 | - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
122 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
125 | 123 | String requestCredentials = ",\"credentialsType\": \"ACCESS_TOKEN\",\"token\": \"test_token\""; |
126 | 124 | byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); |
127 | 125 | JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
... | ... | @@ -129,20 +127,18 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn |
129 | 127 | Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
130 | 128 | |
131 | 129 | Assert.assertNotNull(createdDevice); |
132 | - Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); | |
133 | 130 | |
134 | 131 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
135 | 132 | |
136 | 133 | Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); |
137 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | |
138 | 134 | Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "ACCESS_TOKEN"); |
139 | 135 | Assert.assertEquals(deviceCredentials.getCredentialsId(), "test_token"); |
140 | - Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); | |
136 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString()); | |
141 | 137 | } |
142 | 138 | |
143 | 139 | |
144 | 140 | protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception { |
145 | - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
141 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
146 | 142 | String requestCredentials = ",\"credentialsType\": \"X509_CERTIFICATE\",\"hash\": \"testHash\""; |
147 | 143 | byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); |
148 | 144 | JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
... | ... | @@ -150,12 +146,10 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn |
150 | 146 | Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
151 | 147 | |
152 | 148 | Assert.assertNotNull(createdDevice); |
153 | - Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); | |
154 | 149 | |
155 | 150 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
156 | 151 | |
157 | 152 | Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); |
158 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | |
159 | 153 | Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "X509_CERTIFICATE"); |
160 | 154 | |
161 | 155 | String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue()); |
... | ... | @@ -164,12 +158,12 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn |
164 | 158 | Assert.assertEquals(deviceCredentials.getCredentialsId(), sha3Hash); |
165 | 159 | |
166 | 160 | Assert.assertEquals(deviceCredentials.getCredentialsValue(), "testHash"); |
167 | - Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); | |
161 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString()); | |
168 | 162 | } |
169 | 163 | |
170 | 164 | |
171 | 165 | protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception { |
172 | - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
166 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
173 | 167 | String requestCredentials = ",\"credentialsType\": \"MQTT_BASIC\",\"clientId\": \"test_clientId\",\"username\": \"test_username\",\"password\": \"test_password\""; |
174 | 168 | byte[] result = createMqttClientAndPublish(requestCredentials).getPayloadBytes(); |
175 | 169 | JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
... | ... | @@ -177,12 +171,10 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn |
177 | 171 | Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
178 | 172 | |
179 | 173 | Assert.assertNotNull(createdDevice); |
180 | - Assert.assertEquals(createdDevice.getId().toString(), response.get("deviceId").getAsString()); | |
181 | 174 | |
182 | 175 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
183 | 176 | |
184 | 177 | Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); |
185 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | |
186 | 178 | Assert.assertEquals(deviceCredentials.getCredentialsType().name(), "MQTT_BASIC"); |
187 | 179 | Assert.assertEquals(deviceCredentials.getCredentialsId(), EncryptionUtil.getSha3Hash("|", "test_clientId", "test_username")); |
188 | 180 | |
... | ... | @@ -192,29 +184,26 @@ public abstract class AbstractMqttProvisionJsonDeviceTest extends AbstractMqttIn |
192 | 184 | mqttCredentials.setPassword("test_password"); |
193 | 185 | |
194 | 186 | Assert.assertEquals(deviceCredentials.getCredentialsValue(), JacksonUtil.toString(mqttCredentials)); |
195 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | |
196 | - Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); | |
187 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString()); | |
197 | 188 | } |
198 | 189 | |
199 | 190 | protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception { |
200 | - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
191 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
201 | 192 | byte[] result = createMqttClientAndPublish().getPayloadBytes(); |
202 | 193 | JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
203 | - Assert.assertEquals(savedDevice.getId().toString(), response.get("deviceId").getAsString()); | |
204 | 194 | |
205 | 195 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId()); |
206 | 196 | |
207 | 197 | Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.get("credentialsType").getAsString()); |
208 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.get("credentialsId").getAsString()); | |
209 | - Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("provisionDeviceStatus").getAsString()); | |
198 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.get("status").getAsString()); | |
210 | 199 | } |
211 | 200 | |
212 | 201 | protected void processTestProvisioningWithBadKeyDevice() throws Exception { |
213 | - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); | |
202 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.JSON, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); | |
214 | 203 | byte[] result = createMqttClientAndPublish().getPayloadBytes(); |
215 | 204 | JsonObject response = JsonUtils.parse(new String(result)).getAsJsonObject(); |
216 | 205 | Assert.assertEquals("Provision data was not found!", response.get("errorMsg").getAsString()); |
217 | - Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("provisionDeviceStatus").getAsString()); | |
206 | + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.get("status").getAsString()); | |
218 | 207 | } |
219 | 208 | |
220 | 209 | protected TestMqttCallback createMqttClientAndPublish() throws Exception { | ... | ... |
... | ... | @@ -102,30 +102,28 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI |
102 | 102 | |
103 | 103 | |
104 | 104 | protected void processTestProvisioningDisabledDevice() throws Exception { |
105 | - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.DISABLED, null, null); | |
105 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.DISABLED, null, null); | |
106 | 106 | ProvisionDeviceResponseMsg result = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); |
107 | 107 | Assert.assertNotNull(result); |
108 | - Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getProvisionResponseStatus().toString()); | |
108 | + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), result.getStatus().toString()); | |
109 | 109 | } |
110 | 110 | |
111 | 111 | protected void processTestProvisioningCreateNewDeviceWithoutCredentials() throws Exception { |
112 | - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
112 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
113 | 113 | ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); |
114 | 114 | |
115 | 115 | Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
116 | 116 | |
117 | 117 | Assert.assertNotNull(createdDevice); |
118 | - Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); | |
119 | 118 | |
120 | 119 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
121 | 120 | |
122 | - Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); | |
123 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | |
124 | - Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); | |
121 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString()); | |
122 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString()); | |
125 | 123 | } |
126 | 124 | |
127 | 125 | protected void processTestProvisioningCreateNewDeviceWithAccessToken() throws Exception { |
128 | - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
126 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null,null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
129 | 127 | CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceTokenRequestMsg(ValidateDeviceTokenRequestMsg.newBuilder().setToken("test_token").build()).build(); |
130 | 128 | |
131 | 129 | ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.ACCESS_TOKEN, requestCredentials)).getPayloadBytes()); |
... | ... | @@ -133,19 +131,17 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI |
133 | 131 | Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
134 | 132 | |
135 | 133 | Assert.assertNotNull(createdDevice); |
136 | - Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); | |
137 | 134 | |
138 | 135 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
139 | 136 | |
140 | - Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); | |
141 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | |
137 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString()); | |
142 | 138 | Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.ACCESS_TOKEN); |
143 | 139 | Assert.assertEquals(deviceCredentials.getCredentialsId(), "test_token"); |
144 | - Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); | |
140 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString()); | |
145 | 141 | } |
146 | 142 | |
147 | 143 | protected void processTestProvisioningCreateNewDeviceWithCert() throws Exception { |
148 | - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
144 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
149 | 145 | CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateDeviceX509CertRequestMsg(ValidateDeviceX509CertRequestMsg.newBuilder().setHash("testHash").build()).build(); |
150 | 146 | |
151 | 147 | ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish(createTestsProvisionMessage(CredentialsType.X509_CERTIFICATE, requestCredentials)).getPayloadBytes()); |
... | ... | @@ -153,12 +149,10 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI |
153 | 149 | Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
154 | 150 | |
155 | 151 | Assert.assertNotNull(createdDevice); |
156 | - Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); | |
157 | 152 | |
158 | 153 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
159 | 154 | |
160 | - Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); | |
161 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | |
155 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString()); | |
162 | 156 | Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.X509_CERTIFICATE); |
163 | 157 | |
164 | 158 | String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue()); |
... | ... | @@ -167,11 +161,11 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI |
167 | 161 | Assert.assertEquals(deviceCredentials.getCredentialsId(), sha3Hash); |
168 | 162 | |
169 | 163 | Assert.assertEquals(deviceCredentials.getCredentialsValue(), "testHash"); |
170 | - Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); | |
164 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString()); | |
171 | 165 | } |
172 | 166 | |
173 | 167 | protected void processTestProvisioningCreateNewDeviceWithMqttBasic() throws Exception { |
174 | - super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
168 | + super.processBeforeTest("Test Provision device3", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.ALLOW_CREATE_NEW_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
175 | 169 | CredentialsDataProto requestCredentials = CredentialsDataProto.newBuilder().setValidateBasicMqttCredRequestMsg( |
176 | 170 | ValidateBasicMqttCredRequestMsg.newBuilder() |
177 | 171 | .setClientId("test_clientId") |
... | ... | @@ -185,12 +179,10 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI |
185 | 179 | Device createdDevice = deviceService.findDeviceByTenantIdAndName(savedTenant.getTenantId(), "Test Provision device"); |
186 | 180 | |
187 | 181 | Assert.assertNotNull(createdDevice); |
188 | - Assert.assertEquals(createdDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); | |
189 | 182 | |
190 | 183 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), createdDevice.getId()); |
191 | 184 | |
192 | - Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); | |
193 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | |
185 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString()); | |
194 | 186 | Assert.assertEquals(deviceCredentials.getCredentialsType(), DeviceCredentialsType.MQTT_BASIC); |
195 | 187 | Assert.assertEquals(deviceCredentials.getCredentialsId(), EncryptionUtil.getSha3Hash("|", "test_clientId", "test_username")); |
196 | 188 | |
... | ... | @@ -200,26 +192,23 @@ public abstract class AbstractMqttProvisionProtoDeviceTest extends AbstractMqttI |
200 | 192 | mqttCredentials.setPassword("test_password"); |
201 | 193 | |
202 | 194 | Assert.assertEquals(deviceCredentials.getCredentialsValue(), JacksonUtil.toString(mqttCredentials)); |
203 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | |
204 | - Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); | |
195 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString()); | |
205 | 196 | } |
206 | 197 | |
207 | 198 | protected void processTestProvisioningCheckPreProvisionedDevice() throws Exception { |
208 | - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
199 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKey", "testProvisionSecret"); | |
209 | 200 | ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); |
210 | - Assert.assertEquals(savedDevice.getId().getId(), new UUID(response.getDeviceCredentials().getDeviceIdMSB(), response.getDeviceCredentials().getDeviceIdLSB())); | |
211 | 201 | |
212 | 202 | DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedTenant.getTenantId(), savedDevice.getId()); |
213 | 203 | |
214 | - Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getDeviceCredentials().getCredentialsType().toString()); | |
215 | - Assert.assertEquals(deviceCredentials.getCredentialsId(), response.getDeviceCredentials().getCredentialsId()); | |
216 | - Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getProvisionResponseStatus().toString()); | |
204 | + Assert.assertEquals(deviceCredentials.getCredentialsType().name(), response.getCredentialsType().toString()); | |
205 | + Assert.assertEquals(ProvisionResponseStatus.SUCCESS.name(), response.getStatus().toString()); | |
217 | 206 | } |
218 | 207 | |
219 | 208 | protected void processTestProvisioningWithBadKeyDevice() throws Exception { |
220 | - super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); | |
209 | + super.processBeforeTest("Test Provision device", "Test Provision gateway", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.CHECK_PRE_PROVISIONED_DEVICES, "testProvisionKeyOrig", "testProvisionSecret"); | |
221 | 210 | ProvisionDeviceResponseMsg response = ProvisionDeviceResponseMsg.parseFrom(createMqttClientAndPublish().getPayloadBytes()); |
222 | - Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getProvisionResponseStatus().toString()); | |
211 | + Assert.assertEquals(ProvisionResponseStatus.NOT_FOUND.name(), response.getStatus().toString()); | |
223 | 212 | } |
224 | 213 | |
225 | 214 | protected TestMqttCallback createMqttClientAndPublish() throws Exception { | ... | ... |
... | ... | @@ -15,49 +15,14 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.mqtt.rpc; |
17 | 17 | |
18 | -import com.fasterxml.jackson.databind.JsonNode; | |
19 | -import com.fasterxml.jackson.databind.node.ObjectNode; | |
20 | -import com.google.protobuf.InvalidProtocolBufferException; | |
21 | -import com.nimbusds.jose.util.StandardCharset; | |
22 | 18 | import com.datastax.oss.driver.api.core.uuid.Uuids; |
23 | -import io.netty.handler.codec.mqtt.MqttQoS; | |
24 | 19 | import lombok.extern.slf4j.Slf4j; |
25 | -import org.apache.commons.lang3.StringUtils; | |
26 | -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; | |
27 | -import org.eclipse.paho.client.mqttv3.MqttAsyncClient; | |
28 | -import org.eclipse.paho.client.mqttv3.MqttCallback; | |
29 | -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | |
30 | -import org.eclipse.paho.client.mqttv3.MqttException; | |
31 | -import org.eclipse.paho.client.mqttv3.MqttMessage; | |
32 | 20 | import org.junit.After; |
33 | 21 | import org.junit.Assert; |
34 | 22 | import org.junit.Before; |
35 | -import org.junit.Ignore; | |
36 | 23 | import org.junit.Test; |
37 | -import org.thingsboard.server.common.data.Device; | |
38 | -import org.thingsboard.server.common.data.DeviceProfile; | |
39 | -import org.thingsboard.server.common.data.DeviceProfileType; | |
40 | -import org.thingsboard.server.common.data.DeviceTransportType; | |
41 | -import org.thingsboard.server.common.data.Tenant; | |
42 | -import org.thingsboard.server.common.data.TransportPayloadType; | |
43 | -import org.thingsboard.server.common.data.User; | |
44 | -import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | |
45 | -import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | |
46 | -import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | |
47 | -import org.thingsboard.server.common.data.device.profile.MqttTopics; | |
48 | -import org.thingsboard.server.common.data.security.Authority; | |
49 | -import org.thingsboard.server.common.data.security.DeviceCredentials; | |
50 | -import org.thingsboard.server.controller.AbstractControllerTest; | |
51 | -import org.thingsboard.server.dao.util.mapping.JacksonUtil; | |
52 | 24 | import org.thingsboard.server.service.security.AccessValidator; |
53 | 25 | |
54 | -import java.util.Arrays; | |
55 | -import java.util.concurrent.CountDownLatch; | |
56 | -import java.util.concurrent.TimeUnit; | |
57 | -import java.util.concurrent.atomic.AtomicInteger; | |
58 | - | |
59 | -import static org.junit.Assert.assertEquals; | |
60 | -import static org.junit.Assert.assertNotNull; | |
61 | 26 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
62 | 27 | |
63 | 28 | /** | ... | ... |
... | ... | @@ -15,9 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.mqtt.rpc; |
17 | 17 | |
18 | -import com.datastax.oss.driver.api.core.uuid.Uuids; | |
19 | 18 | import com.fasterxml.jackson.databind.JsonNode; |
20 | -import com.fasterxml.jackson.databind.node.ObjectNode; | |
21 | 19 | import com.google.protobuf.InvalidProtocolBufferException; |
22 | 20 | import com.nimbusds.jose.util.StandardCharset; |
23 | 21 | import io.netty.handler.codec.mqtt.MqttQoS; |
... | ... | @@ -26,35 +24,18 @@ import org.apache.commons.lang3.StringUtils; |
26 | 24 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
27 | 25 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
28 | 26 | import org.eclipse.paho.client.mqttv3.MqttCallback; |
29 | -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; | |
30 | 27 | import org.eclipse.paho.client.mqttv3.MqttException; |
31 | 28 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
32 | -import org.junit.After; | |
33 | 29 | import org.junit.Assert; |
34 | -import org.junit.Before; | |
35 | -import org.junit.Test; | |
36 | 30 | import org.thingsboard.server.common.data.Device; |
37 | -import org.thingsboard.server.common.data.DeviceProfile; | |
38 | -import org.thingsboard.server.common.data.DeviceProfileType; | |
39 | -import org.thingsboard.server.common.data.DeviceTransportType; | |
40 | -import org.thingsboard.server.common.data.Tenant; | |
41 | 31 | import org.thingsboard.server.common.data.TransportPayloadType; |
42 | -import org.thingsboard.server.common.data.User; | |
43 | -import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | |
44 | -import org.thingsboard.server.common.data.device.profile.DeviceProfileData; | |
45 | -import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | |
46 | 32 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
47 | -import org.thingsboard.server.common.data.security.Authority; | |
48 | -import org.thingsboard.server.common.data.security.DeviceCredentials; | |
49 | -import org.thingsboard.server.controller.AbstractControllerTest; | |
50 | 33 | import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
51 | 34 | import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; |
52 | -import org.thingsboard.server.service.security.AccessValidator; | |
53 | 35 | |
54 | 36 | import java.util.Arrays; |
55 | 37 | import java.util.concurrent.CountDownLatch; |
56 | 38 | import java.util.concurrent.TimeUnit; |
57 | -import java.util.concurrent.atomic.AtomicInteger; | |
58 | 39 | |
59 | 40 | import static org.junit.Assert.assertEquals; |
60 | 41 | import static org.junit.Assert.assertNotNull; | ... | ... |
... | ... | @@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
20 | 20 | import org.junit.After; |
21 | 21 | import org.junit.Before; |
22 | -import org.junit.Ignore; | |
23 | 22 | import org.junit.Test; |
24 | 23 | import org.thingsboard.server.common.data.TransportPayloadType; |
25 | 24 | ... | ... |
... | ... | @@ -22,16 +22,12 @@ import org.eclipse.paho.client.mqttv3.MqttException; |
22 | 22 | import org.eclipse.paho.client.mqttv3.MqttMessage; |
23 | 23 | import org.junit.After; |
24 | 24 | import org.junit.Before; |
25 | -import org.junit.Ignore; | |
26 | 25 | import org.junit.Test; |
27 | 26 | import org.thingsboard.server.common.data.TransportPayloadType; |
28 | 27 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
29 | 28 | import org.thingsboard.server.gen.transport.TransportApiProtos; |
30 | 29 | import org.thingsboard.server.gen.transport.TransportProtos; |
31 | 30 | |
32 | -import static org.junit.Assert.assertEquals; | |
33 | -import static org.junit.Assert.assertNotNull; | |
34 | - | |
35 | 31 | @Slf4j |
36 | 32 | public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { |
37 | 33 | ... | ... |
... | ... | @@ -15,6 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.mqtt.telemetry.attributes; |
17 | 17 | |
18 | +import com.fasterxml.jackson.core.JsonProcessingException; | |
18 | 19 | import lombok.extern.slf4j.Slf4j; |
19 | 20 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
20 | 21 | import org.junit.After; |
... | ... | @@ -142,7 +143,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt |
142 | 143 | |
143 | 144 | } |
144 | 145 | |
145 | - protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) { | |
146 | + protected void assertAttributesValues(List<Map<String, Object>> deviceValues, Set<String> expectedKeySet) throws JsonProcessingException { | |
146 | 147 | for (Map<String, Object> map : deviceValues) { |
147 | 148 | String key = (String) map.get("key"); |
148 | 149 | Object value = map.get("value"); | ... | ... |
... | ... | @@ -15,18 +15,24 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.mqtt.telemetry.attributes; |
17 | 17 | |
18 | +import com.github.os72.protobuf.dynamic.DynamicSchema; | |
19 | +import com.google.protobuf.Descriptors; | |
20 | +import com.google.protobuf.DynamicMessage; | |
21 | +import com.squareup.wire.schema.internal.parser.ProtoFileElement; | |
18 | 22 | import lombok.extern.slf4j.Slf4j; |
19 | 23 | import org.junit.After; |
20 | -import org.junit.Before; | |
21 | 24 | import org.junit.Test; |
22 | 25 | import org.thingsboard.server.common.data.TransportPayloadType; |
26 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | |
27 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | |
28 | +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; | |
29 | +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; | |
23 | 30 | import org.thingsboard.server.gen.transport.TransportApiProtos; |
24 | 31 | import org.thingsboard.server.gen.transport.TransportProtos; |
25 | 32 | |
26 | 33 | import java.util.Arrays; |
27 | 34 | import java.util.List; |
28 | 35 | |
29 | -import static org.junit.Assert.assertEquals; | |
30 | 36 | import static org.junit.Assert.assertNotNull; |
31 | 37 | import static org.junit.Assert.assertTrue; |
32 | 38 | |
... | ... | @@ -35,11 +41,6 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac |
35 | 41 | |
36 | 42 | private static final String POST_DATA_ATTRIBUTES_TOPIC = "proto/attributes"; |
37 | 43 | |
38 | - @Before | |
39 | - public void beforeTest() throws Exception { | |
40 | - processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC); | |
41 | - } | |
42 | - | |
43 | 44 | @After |
44 | 45 | public void afterTest() throws Exception { |
45 | 46 | processAfterTest(); |
... | ... | @@ -47,13 +48,49 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac |
47 | 48 | |
48 | 49 | @Test |
49 | 50 | public void testPushMqttAttributes() throws Exception { |
51 | + super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC); | |
50 | 52 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
51 | - TransportProtos.PostAttributeMsg msg = getPostAttributeMsg(expectedKeys); | |
52 | - processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, msg.toByteArray()); | |
53 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
54 | + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); | |
55 | + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; | |
56 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); | |
57 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
58 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
59 | + ProtoFileElement transportProtoSchemaFile = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_ATTRIBUTES_PROTO_SCHEMA); | |
60 | + DynamicSchema attributesSchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchemaFile, ProtoTransportPayloadConfiguration.ATTRIBUTES_PROTO_SCHEMA); | |
61 | + | |
62 | + DynamicMessage.Builder nestedJsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject.NestedJsonObject"); | |
63 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | |
64 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | |
65 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | |
66 | + | |
67 | + DynamicMessage.Builder jsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject"); | |
68 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | |
69 | + assertNotNull(jsonObjectBuilderDescriptor); | |
70 | + DynamicMessage jsonObject = jsonObjectBuilder | |
71 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) | |
72 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | |
73 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | |
74 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | |
75 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | |
76 | + .build(); | |
77 | + | |
78 | + DynamicMessage.Builder postAttributesBuilder = attributesSchema.newMessageBuilder("PostAttributes"); | |
79 | + Descriptors.Descriptor postAttributesMsgDescriptor = postAttributesBuilder.getDescriptorForType(); | |
80 | + assertNotNull(postAttributesMsgDescriptor); | |
81 | + DynamicMessage postAttributesMsg = postAttributesBuilder | |
82 | + .setField(postAttributesMsgDescriptor.findFieldByName("key1"), "value1") | |
83 | + .setField(postAttributesMsgDescriptor.findFieldByName("key2"), true) | |
84 | + .setField(postAttributesMsgDescriptor.findFieldByName("key3"), 3.0) | |
85 | + .setField(postAttributesMsgDescriptor.findFieldByName("key4"), 4) | |
86 | + .setField(postAttributesMsgDescriptor.findFieldByName("key5"), jsonObject) | |
87 | + .build(); | |
88 | + processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, expectedKeys, postAttributesMsg.toByteArray()); | |
53 | 89 | } |
54 | 90 | |
55 | 91 | @Test |
56 | 92 | public void testPushMqttAttributesGateway() throws Exception { |
93 | + super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, null); | |
57 | 94 | TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributesMsgProtoBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder(); |
58 | 95 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
59 | 96 | String deviceName1 = "Device A"; | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.telemetry.attributes.nosql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoNoSqlTest; |
19 | -import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; | |
20 | 19 | import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; |
21 | 20 | |
22 | 21 | @DaoNoSqlTest | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.telemetry.attributes.nosql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoNoSqlTest; |
19 | -import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesIntegrationTest; | |
20 | 19 | import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest; |
21 | 20 | |
22 | 21 | @DaoNoSqlTest | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.telemetry.attributes.sql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesJsonIntegrationTest; | |
20 | 19 | import org.thingsboard.server.mqtt.telemetry.attributes.AbstractMqttAttributesProtoIntegrationTest; |
21 | 20 | |
22 | 21 | @DaoSqlTest | ... | ... |
... | ... | @@ -27,7 +27,6 @@ import org.junit.After; |
27 | 27 | import org.junit.Before; |
28 | 28 | import org.junit.Test; |
29 | 29 | import org.thingsboard.server.common.data.Device; |
30 | -import org.thingsboard.server.common.data.TransportPayloadType; | |
31 | 30 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
32 | 31 | import org.thingsboard.server.common.data.id.DeviceId; |
33 | 32 | import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; |
... | ... | @@ -128,7 +127,34 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt |
128 | 127 | } else { |
129 | 128 | getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet); |
130 | 129 | } |
131 | - Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class); | |
130 | + start = System.currentTimeMillis(); | |
131 | + end = System.currentTimeMillis() + 5000; | |
132 | + Map<String, List<Map<String, String>>> values = null; | |
133 | + while (start <= end) { | |
134 | + values = doGetAsync(getTelemetryValuesUrl, Map.class); | |
135 | + boolean valid = values.size() == expectedKeys.size(); | |
136 | + if (valid) { | |
137 | + for (String key : expectedKeys) { | |
138 | + List<Map<String, String>> tsValues = values.get(key); | |
139 | + if (tsValues != null && tsValues.size() > 0) { | |
140 | + Object ts = tsValues.get(0).get("ts"); | |
141 | + if (ts == null) { | |
142 | + valid = false; | |
143 | + break; | |
144 | + } | |
145 | + } else { | |
146 | + valid = false; | |
147 | + break; | |
148 | + } | |
149 | + } | |
150 | + } | |
151 | + if (valid) { | |
152 | + break; | |
153 | + } | |
154 | + Thread.sleep(100); | |
155 | + start += 100; | |
156 | + } | |
157 | + assertNotNull(values); | |
132 | 158 | |
133 | 159 | if (withTs) { |
134 | 160 | assertTs(values, expectedKeys, 10000, 0); | ... | ... |
... | ... | @@ -15,33 +15,36 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.mqtt.telemetry.timeseries; |
17 | 17 | |
18 | +import com.github.os72.protobuf.dynamic.DynamicSchema; | |
19 | +import com.google.protobuf.Descriptors; | |
20 | +import com.google.protobuf.DynamicMessage; | |
21 | +import com.squareup.wire.schema.internal.parser.ProtoFileElement; | |
18 | 22 | import lombok.extern.slf4j.Slf4j; |
19 | 23 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
20 | 24 | import org.junit.After; |
21 | -import org.junit.Before; | |
22 | 25 | import org.junit.Test; |
23 | 26 | import org.thingsboard.server.common.data.Device; |
27 | +import org.thingsboard.server.common.data.DeviceProfileProvisionType; | |
24 | 28 | import org.thingsboard.server.common.data.TransportPayloadType; |
29 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | |
30 | +import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; | |
31 | +import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; | |
25 | 32 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
33 | +import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; | |
26 | 34 | import org.thingsboard.server.gen.transport.TransportApiProtos; |
27 | 35 | import org.thingsboard.server.gen.transport.TransportProtos; |
28 | 36 | |
29 | 37 | import java.util.Arrays; |
30 | 38 | import java.util.List; |
31 | 39 | |
32 | -import static org.junit.Assert.assertEquals; | |
33 | 40 | import static org.junit.Assert.assertNotNull; |
41 | +import static org.junit.Assert.assertTrue; | |
34 | 42 | |
35 | 43 | @Slf4j |
36 | 44 | public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends AbstractMqttTimeseriesIntegrationTest { |
37 | 45 | |
38 | 46 | private static final String POST_DATA_TELEMETRY_TOPIC = "proto/telemetry"; |
39 | 47 | |
40 | - @Before | |
41 | - public void beforeTest() throws Exception { | |
42 | - processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null); | |
43 | - } | |
44 | - | |
45 | 48 | @After |
46 | 49 | public void afterTest() throws Exception { |
47 | 50 | processAfterTest(); |
... | ... | @@ -49,20 +52,127 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac |
49 | 52 | |
50 | 53 | @Test |
51 | 54 | public void testPushMqttTelemetry() throws Exception { |
55 | + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null); | |
52 | 56 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
53 | - TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 0); | |
54 | - processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), false); | |
57 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
58 | + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); | |
59 | + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; | |
60 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); | |
61 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
62 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
63 | + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(DEVICE_TELEMETRY_PROTO_SCHEMA); | |
64 | + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); | |
65 | + | |
66 | + DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject"); | |
67 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | |
68 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | |
69 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | |
70 | + | |
71 | + DynamicMessage.Builder jsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject"); | |
72 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | |
73 | + assertNotNull(jsonObjectBuilderDescriptor); | |
74 | + DynamicMessage jsonObject = jsonObjectBuilder | |
75 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) | |
76 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | |
77 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | |
78 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | |
79 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | |
80 | + .build(); | |
81 | + | |
82 | + DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); | |
83 | + Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType(); | |
84 | + assertNotNull(postTelemetryMsgDescriptor); | |
85 | + DynamicMessage postTelemetryMsg = postTelemetryBuilder | |
86 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key1"), "value1") | |
87 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key2"), true) | |
88 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key3"), 3.0) | |
89 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key4"), 4) | |
90 | + .setField(postTelemetryMsgDescriptor.findFieldByName("key5"), jsonObject) | |
91 | + .build(); | |
92 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, postTelemetryMsg.toByteArray(), false); | |
55 | 93 | } |
56 | 94 | |
57 | 95 | @Test |
58 | 96 | public void testPushMqttTelemetryWithTs() throws Exception { |
97 | + String schemaStr = "syntax =\"proto3\";\n" + | |
98 | + "\n" + | |
99 | + "package test;\n" + | |
100 | + "\n" + | |
101 | + "message PostTelemetry {\n" + | |
102 | + " int64 ts = 1;\n" + | |
103 | + " Values values = 2;\n" + | |
104 | + " \n" + | |
105 | + " message Values {\n" + | |
106 | + " string key1 = 3;\n" + | |
107 | + " bool key2 = 4;\n" + | |
108 | + " double key3 = 5;\n" + | |
109 | + " int32 key4 = 6;\n" + | |
110 | + " JsonObject key5 = 7;\n" + | |
111 | + " }\n" + | |
112 | + " \n" + | |
113 | + " message JsonObject {\n" + | |
114 | + " int32 someNumber = 8;\n" + | |
115 | + " repeated int32 someArray = 9;\n" + | |
116 | + " NestedJsonObject someNestedObject = 10;\n" + | |
117 | + " message NestedJsonObject {\n" + | |
118 | + " string key = 11;\n" + | |
119 | + " }\n" + | |
120 | + " }\n" + | |
121 | + "}"; | |
122 | + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, DeviceProfileProvisionType.DISABLED, null, null); | |
59 | 123 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
60 | - TransportProtos.TsKvListProto tsKvListProto = getTsKvListProto(expectedKeys, 10000); | |
61 | - processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, tsKvListProto.toByteArray(), true); | |
124 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
125 | + assertTrue(transportConfiguration instanceof MqttDeviceProfileTransportConfiguration); | |
126 | + MqttDeviceProfileTransportConfiguration mqttTransportConfiguration = (MqttDeviceProfileTransportConfiguration) transportConfiguration; | |
127 | + TransportPayloadTypeConfiguration transportPayloadTypeConfiguration = mqttTransportConfiguration.getTransportPayloadTypeConfiguration(); | |
128 | + assertTrue(transportPayloadTypeConfiguration instanceof ProtoTransportPayloadConfiguration); | |
129 | + ProtoTransportPayloadConfiguration protoTransportPayloadConfiguration = (ProtoTransportPayloadConfiguration) transportPayloadTypeConfiguration; | |
130 | + ProtoFileElement transportProtoSchema = protoTransportPayloadConfiguration.getTransportProtoSchema(schemaStr); | |
131 | + DynamicSchema telemetrySchema = protoTransportPayloadConfiguration.getDynamicSchema(transportProtoSchema, "telemetrySchema"); | |
132 | + | |
133 | + DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject"); | |
134 | + Descriptors.Descriptor nestedJsonObjectBuilderDescriptor = nestedJsonObjectBuilder.getDescriptorForType(); | |
135 | + assertNotNull(nestedJsonObjectBuilderDescriptor); | |
136 | + DynamicMessage nestedJsonObject = nestedJsonObjectBuilder.setField(nestedJsonObjectBuilderDescriptor.findFieldByName("key"), "value").build(); | |
137 | + | |
138 | + DynamicMessage.Builder jsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject"); | |
139 | + Descriptors.Descriptor jsonObjectBuilderDescriptor = jsonObjectBuilder.getDescriptorForType(); | |
140 | + assertNotNull(jsonObjectBuilderDescriptor); | |
141 | + DynamicMessage jsonObject = jsonObjectBuilder | |
142 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNumber"), 42) | |
143 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 1) | |
144 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 2) | |
145 | + .addRepeatedField(jsonObjectBuilderDescriptor.findFieldByName("someArray"), 3) | |
146 | + .setField(jsonObjectBuilderDescriptor.findFieldByName("someNestedObject"), nestedJsonObject) | |
147 | + .build(); | |
148 | + | |
149 | + | |
150 | + DynamicMessage.Builder valuesBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.Values"); | |
151 | + Descriptors.Descriptor valuesDescriptor = valuesBuilder.getDescriptorForType(); | |
152 | + assertNotNull(valuesDescriptor); | |
153 | + | |
154 | + DynamicMessage valuesMsg = valuesBuilder | |
155 | + .setField(valuesDescriptor.findFieldByName("key1"), "value1") | |
156 | + .setField(valuesDescriptor.findFieldByName("key2"), true) | |
157 | + .setField(valuesDescriptor.findFieldByName("key3"), 3.0) | |
158 | + .setField(valuesDescriptor.findFieldByName("key4"), 4) | |
159 | + .setField(valuesDescriptor.findFieldByName("key5"), jsonObject) | |
160 | + .build(); | |
161 | + | |
162 | + DynamicMessage.Builder postTelemetryBuilder = telemetrySchema.newMessageBuilder("PostTelemetry"); | |
163 | + Descriptors.Descriptor postTelemetryMsgDescriptor = postTelemetryBuilder.getDescriptorForType(); | |
164 | + assertNotNull(postTelemetryMsgDescriptor); | |
165 | + DynamicMessage postTelemetryMsg = postTelemetryBuilder | |
166 | + .setField(postTelemetryMsgDescriptor.findFieldByName("ts"), 10000L) | |
167 | + .setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg) | |
168 | + .build(); | |
169 | + | |
170 | + processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, expectedKeys, postTelemetryMsg.toByteArray(), true); | |
62 | 171 | } |
63 | 172 | |
64 | 173 | @Test |
65 | 174 | public void testPushMqttTelemetryGateway() throws Exception { |
175 | + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, null, null, null, null, DeviceProfileProvisionType.DISABLED, null, null); | |
66 | 176 | TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder(); |
67 | 177 | List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5"); |
68 | 178 | String deviceName1 = "Device A"; |
... | ... | @@ -76,6 +186,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac |
76 | 186 | |
77 | 187 | @Test |
78 | 188 | public void testGatewayConnect() throws Exception { |
189 | + super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, null, null, DeviceProfileProvisionType.DISABLED, null, null); | |
79 | 190 | String deviceName = "Device A"; |
80 | 191 | TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); |
81 | 192 | MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.telemetry.timeseries.sql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesIntegrationTest; | |
20 | 19 | import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; |
21 | 20 | |
22 | 21 | /** | ... | ... |
... | ... | @@ -16,7 +16,6 @@ |
16 | 16 | package org.thingsboard.server.mqtt.telemetry.timeseries.sql; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.dao.service.DaoSqlTest; |
19 | -import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesJsonIntegrationTest; | |
20 | 19 | import org.thingsboard.server.mqtt.telemetry.timeseries.AbstractMqttTimeseriesProtoIntegrationTest; |
21 | 20 | |
22 | 21 | /** | ... | ... |
... | ... | @@ -71,6 +71,14 @@ |
71 | 71 | <artifactId>java-driver-core</artifactId> |
72 | 72 | <scope>test</scope> |
73 | 73 | </dependency> |
74 | + <dependency> | |
75 | + <groupId>com.squareup.wire</groupId> | |
76 | + <artifactId>wire-schema</artifactId> | |
77 | + </dependency> | |
78 | + <dependency> | |
79 | + <groupId>org.thingsboard</groupId> | |
80 | + <artifactId>protobuf-dynamic</artifactId> | |
81 | + </dependency> | |
74 | 82 | </dependencies> |
75 | 83 | |
76 | 84 | <build> | ... | ... |
... | ... | @@ -18,16 +18,21 @@ package org.thingsboard.server.common.data; |
18 | 18 | import lombok.Getter; |
19 | 19 | |
20 | 20 | public enum ApiFeature { |
21 | - TRANSPORT("transportApiState"), | |
22 | - DB("dbApiState"), | |
23 | - RE("ruleEngineApiState"), | |
24 | - JS("jsExecutionApiState"); | |
21 | + TRANSPORT("transportApiState", "Device API"), | |
22 | + DB("dbApiState", "Telemetry persistence"), | |
23 | + RE("ruleEngineApiState", "Rule Engine execution"), | |
24 | + JS("jsExecutionApiState", "JavaScript functions execution"), | |
25 | + EMAIL("emailApiState", "Email messages"), | |
26 | + SMS("smsApiState", "SMS messages"); | |
25 | 27 | |
26 | 28 | @Getter |
27 | 29 | private final String apiStateKey; |
30 | + @Getter | |
31 | + private final String label; | |
28 | 32 | |
29 | - ApiFeature(String apiStateKey) { | |
33 | + ApiFeature(String apiStateKey, String label) { | |
30 | 34 | this.apiStateKey = apiStateKey; |
35 | + this.label = label; | |
31 | 36 | } |
32 | 37 | |
33 | 38 | } | ... | ... |
... | ... | @@ -23,11 +23,15 @@ public enum ApiUsageRecordKey { |
23 | 23 | TRANSPORT_DP_COUNT(ApiFeature.TRANSPORT, "transportDataPointsCount", "transportDataPointsLimit"), |
24 | 24 | STORAGE_DP_COUNT(ApiFeature.DB, "storageDataPointsCount", "storageDataPointsLimit"), |
25 | 25 | RE_EXEC_COUNT(ApiFeature.RE, "ruleEngineExecutionCount", "ruleEngineExecutionLimit"), |
26 | - JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit"); | |
26 | + JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit"), | |
27 | + EMAIL_EXEC_COUNT(ApiFeature.EMAIL, "emailCount", "emailLimit"), | |
28 | + SMS_EXEC_COUNT(ApiFeature.SMS, "smsCount", "smsLimit"); | |
27 | 29 | private static final ApiUsageRecordKey[] JS_RECORD_KEYS = {JS_EXEC_COUNT}; |
28 | 30 | private static final ApiUsageRecordKey[] RE_RECORD_KEYS = {RE_EXEC_COUNT}; |
29 | 31 | private static final ApiUsageRecordKey[] DB_RECORD_KEYS = {STORAGE_DP_COUNT}; |
30 | 32 | private static final ApiUsageRecordKey[] TRANSPORT_RECORD_KEYS = {TRANSPORT_MSG_COUNT, TRANSPORT_DP_COUNT}; |
33 | + private static final ApiUsageRecordKey[] EMAIL_RECORD_KEYS = {EMAIL_EXEC_COUNT}; | |
34 | + private static final ApiUsageRecordKey[] SMS_RECORD_KEYS = {SMS_EXEC_COUNT}; | |
31 | 35 | |
32 | 36 | @Getter |
33 | 37 | private final ApiFeature apiFeature; |
... | ... | @@ -52,6 +56,10 @@ public enum ApiUsageRecordKey { |
52 | 56 | return RE_RECORD_KEYS; |
53 | 57 | case JS: |
54 | 58 | return JS_RECORD_KEYS; |
59 | + case EMAIL: | |
60 | + return EMAIL_RECORD_KEYS; | |
61 | + case SMS: | |
62 | + return SMS_RECORD_KEYS; | |
55 | 63 | default: |
56 | 64 | return new ApiUsageRecordKey[]{}; |
57 | 65 | } | ... | ... |
... | ... | @@ -47,6 +47,12 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan |
47 | 47 | @Getter |
48 | 48 | @Setter |
49 | 49 | private ApiUsageStateValue jsExecState; |
50 | + @Getter | |
51 | + @Setter | |
52 | + private ApiUsageStateValue emailExecState; | |
53 | + @Getter | |
54 | + @Setter | |
55 | + private ApiUsageStateValue smsExecState; | |
50 | 56 | |
51 | 57 | public ApiUsageState() { |
52 | 58 | super(); |
... | ... | @@ -64,6 +70,8 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan |
64 | 70 | this.dbStorageState = ur.getDbStorageState(); |
65 | 71 | this.reExecState = ur.getReExecState(); |
66 | 72 | this.jsExecState = ur.getJsExecState(); |
73 | + this.emailExecState = ur.getEmailExecState(); | |
74 | + this.smsExecState = ur.getSmsExecState(); | |
67 | 75 | } |
68 | 76 | |
69 | 77 | public boolean isTransportEnabled() { |
... | ... | @@ -81,4 +89,12 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan |
81 | 89 | public boolean isJsExecEnabled() { |
82 | 90 | return !ApiUsageStateValue.DISABLED.equals(jsExecState); |
83 | 91 | } |
92 | + | |
93 | + public boolean isEmailSendEnabled(){ | |
94 | + return !ApiUsageStateValue.DISABLED.equals(emailExecState); | |
95 | + } | |
96 | + | |
97 | + public boolean isSmsSendEnabled(){ | |
98 | + return !ApiUsageStateValue.DISABLED.equals(smsExecState); | |
99 | + } | |
84 | 100 | } | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/ApiUsageStateMailMessage.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.common.data; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +@Data | |
21 | +public class ApiUsageStateMailMessage { | |
22 | + private final ApiUsageRecordKey key; | |
23 | + private final long threshold; | |
24 | + private final long value; | |
25 | +} | ... | ... |
... | ... | @@ -29,7 +29,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; |
29 | 29 | property = "type") |
30 | 30 | @JsonSubTypes({ |
31 | 31 | @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"), |
32 | - @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), | |
32 | + @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), | |
33 | 33 | @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M")}) |
34 | 34 | public interface DeviceProfileTransportConfiguration { |
35 | 35 | ... | ... |
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.common.data.device.profile; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.common.data.TransportPayloadType; | |
20 | + | |
21 | +@Data | |
22 | +public class JsonTransportPayloadConfiguration implements TransportPayloadTypeConfiguration { | |
23 | + | |
24 | + @Override | |
25 | + public TransportPayloadType getTransportPayloadType() { | |
26 | + return TransportPayloadType.JSON; | |
27 | + } | |
28 | +} | ... | ... |
... | ... | @@ -16,20 +16,27 @@ |
16 | 16 | package org.thingsboard.server.common.data.device.profile; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | -import org.thingsboard.server.common.data.TransportPayloadType; | |
20 | 19 | import org.thingsboard.server.common.data.DeviceTransportType; |
21 | 20 | |
22 | 21 | @Data |
23 | 22 | public class MqttDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { |
24 | 23 | |
25 | - private TransportPayloadType transportPayloadType = TransportPayloadType.JSON; | |
26 | - | |
27 | 24 | private String deviceTelemetryTopic = MqttTopics.DEVICE_TELEMETRY_TOPIC; |
28 | 25 | private String deviceAttributesTopic = MqttTopics.DEVICE_ATTRIBUTES_TOPIC; |
26 | + private TransportPayloadTypeConfiguration transportPayloadTypeConfiguration; | |
29 | 27 | |
30 | 28 | @Override |
31 | 29 | public DeviceTransportType getType() { |
32 | 30 | return DeviceTransportType.MQTT; |
33 | 31 | } |
34 | 32 | |
33 | + public TransportPayloadTypeConfiguration getTransportPayloadTypeConfiguration() { | |
34 | + if (transportPayloadTypeConfiguration != null) { | |
35 | + return transportPayloadTypeConfiguration; | |
36 | + } else { | |
37 | + return new JsonTransportPayloadConfiguration(); | |
38 | + } | |
39 | + } | |
40 | + | |
41 | + | |
35 | 42 | } | ... | ... |
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.common.data.device.profile; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
19 | +import com.github.os72.protobuf.dynamic.DynamicSchema; | |
20 | +import com.github.os72.protobuf.dynamic.EnumDefinition; | |
21 | +import com.github.os72.protobuf.dynamic.MessageDefinition; | |
22 | +import com.google.protobuf.Descriptors; | |
23 | +import com.google.protobuf.DynamicMessage; | |
24 | +import com.squareup.wire.schema.Location; | |
25 | +import com.squareup.wire.schema.internal.parser.EnumConstantElement; | |
26 | +import com.squareup.wire.schema.internal.parser.EnumElement; | |
27 | +import com.squareup.wire.schema.internal.parser.FieldElement; | |
28 | +import com.squareup.wire.schema.internal.parser.MessageElement; | |
29 | +import com.squareup.wire.schema.internal.parser.OneOfElement; | |
30 | +import com.squareup.wire.schema.internal.parser.ProtoFileElement; | |
31 | +import com.squareup.wire.schema.internal.parser.ProtoParser; | |
32 | +import com.squareup.wire.schema.internal.parser.TypeElement; | |
33 | +import lombok.Data; | |
34 | +import lombok.extern.slf4j.Slf4j; | |
35 | +import org.thingsboard.server.common.data.TransportPayloadType; | |
36 | + | |
37 | +import java.util.ArrayList; | |
38 | +import java.util.Collections; | |
39 | +import java.util.List; | |
40 | +import java.util.stream.Collectors; | |
41 | + | |
42 | +@Slf4j | |
43 | +@Data | |
44 | +public class ProtoTransportPayloadConfiguration implements TransportPayloadTypeConfiguration { | |
45 | + | |
46 | + public static final Location LOCATION = new Location("", "", -1, -1); | |
47 | + public static final String ATTRIBUTES_PROTO_SCHEMA = "attributes proto schema"; | |
48 | + public static final String TELEMETRY_PROTO_SCHEMA = "telemetry proto schema"; | |
49 | + | |
50 | + private String deviceTelemetryProtoSchema; | |
51 | + private String deviceAttributesProtoSchema; | |
52 | + | |
53 | + @Override | |
54 | + public TransportPayloadType getTransportPayloadType() { | |
55 | + return TransportPayloadType.PROTOBUF; | |
56 | + } | |
57 | + | |
58 | + public Descriptors.Descriptor getTelemetryDynamicMessageDescriptor(String deviceTelemetryProtoSchema) { | |
59 | + return getDescriptor(deviceTelemetryProtoSchema, TELEMETRY_PROTO_SCHEMA); | |
60 | + } | |
61 | + | |
62 | + public Descriptors.Descriptor getAttributesDynamicMessageDescriptor(String deviceAttributesProtoSchema) { | |
63 | + return getDescriptor(deviceAttributesProtoSchema, ATTRIBUTES_PROTO_SCHEMA); | |
64 | + } | |
65 | + | |
66 | + private Descriptors.Descriptor getDescriptor(String protoSchema, String schemaName) { | |
67 | + try { | |
68 | + ProtoFileElement protoFileElement = getTransportProtoSchema(protoSchema); | |
69 | + DynamicSchema dynamicSchema = getDynamicSchema(protoFileElement, schemaName); | |
70 | + String lastMsgName = getMessageTypes(protoFileElement.getTypes()).stream() | |
71 | + .map(MessageElement::getName).reduce((previous, last) -> last).get(); | |
72 | + DynamicMessage.Builder builder = dynamicSchema.newMessageBuilder(lastMsgName); | |
73 | + return builder.getDescriptorForType(); | |
74 | + } catch (Exception e) { | |
75 | + log.warn("Failed to get Message Descriptor due to {}", e.getMessage()); | |
76 | + return null; | |
77 | + } | |
78 | + } | |
79 | + | |
80 | + public DynamicSchema getDynamicSchema(ProtoFileElement protoFileElement, String schemaName) { | |
81 | + DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder(); | |
82 | + schemaBuilder.setName(schemaName); | |
83 | + schemaBuilder.setPackage(!isEmptyStr(protoFileElement.getPackageName()) ? | |
84 | + protoFileElement.getPackageName() : schemaName.toLowerCase()); | |
85 | + List<TypeElement> types = protoFileElement.getTypes(); | |
86 | + List<MessageElement> messageTypes = getMessageTypes(types); | |
87 | + | |
88 | + if (!messageTypes.isEmpty()) { | |
89 | + List<EnumElement> enumTypes = getEnumElements(types); | |
90 | + if (!enumTypes.isEmpty()) { | |
91 | + enumTypes.forEach(enumElement -> { | |
92 | + EnumDefinition enumDefinition = getEnumDefinition(enumElement); | |
93 | + schemaBuilder.addEnumDefinition(enumDefinition); | |
94 | + }); | |
95 | + } | |
96 | + List<MessageDefinition> messageDefinitions = getMessageDefinitions(messageTypes); | |
97 | + messageDefinitions.forEach(schemaBuilder::addMessageDefinition); | |
98 | + try { | |
99 | + return schemaBuilder.build(); | |
100 | + } catch (Descriptors.DescriptorValidationException e) { | |
101 | + throw new RuntimeException("Failed to create dynamic schema due to: " + e.getMessage()); | |
102 | + } | |
103 | + } else { | |
104 | + throw new RuntimeException("Failed to get Dynamic Schema! Message types is empty for schema:" + schemaName); | |
105 | + } | |
106 | + } | |
107 | + | |
108 | + public ProtoFileElement getTransportProtoSchema(String protoSchema) { | |
109 | + return new ProtoParser(LOCATION, protoSchema.toCharArray()).readProtoFile(); | |
110 | + } | |
111 | + | |
112 | + private List<MessageElement> getMessageTypes(List<TypeElement> types) { | |
113 | + return types.stream() | |
114 | + .filter(typeElement -> typeElement instanceof MessageElement) | |
115 | + .map(typeElement -> (MessageElement) typeElement) | |
116 | + .collect(Collectors.toList()); | |
117 | + } | |
118 | + | |
119 | + private List<EnumElement> getEnumElements(List<TypeElement> types) { | |
120 | + return types.stream() | |
121 | + .filter(typeElement -> typeElement instanceof EnumElement) | |
122 | + .map(typeElement -> (EnumElement) typeElement) | |
123 | + .collect(Collectors.toList()); | |
124 | + } | |
125 | + | |
126 | + private List<MessageDefinition> getMessageDefinitions(List<MessageElement> messageElementsList) { | |
127 | + if (!messageElementsList.isEmpty()) { | |
128 | + List<MessageDefinition> messageDefinitions = new ArrayList<>(); | |
129 | + messageElementsList.forEach(messageElement -> { | |
130 | + MessageDefinition.Builder messageDefinitionBuilder = MessageDefinition.newBuilder(messageElement.getName()); | |
131 | + | |
132 | + List<TypeElement> nestedTypes = messageElement.getNestedTypes(); | |
133 | + if (!nestedTypes.isEmpty()) { | |
134 | + List<EnumElement> nestedEnumTypes = getEnumElements(nestedTypes); | |
135 | + if (!nestedEnumTypes.isEmpty()) { | |
136 | + nestedEnumTypes.forEach(enumElement -> { | |
137 | + EnumDefinition nestedEnumDefinition = getEnumDefinition(enumElement); | |
138 | + messageDefinitionBuilder.addEnumDefinition(nestedEnumDefinition); | |
139 | + }); | |
140 | + } | |
141 | + List<MessageElement> nestedMessageTypes = getMessageTypes(nestedTypes); | |
142 | + List<MessageDefinition> nestedMessageDefinitions = getMessageDefinitions(nestedMessageTypes); | |
143 | + nestedMessageDefinitions.forEach(messageDefinitionBuilder::addMessageDefinition); | |
144 | + } | |
145 | + List<FieldElement> messageElementFields = messageElement.getFields(); | |
146 | + List<OneOfElement> oneOfs = messageElement.getOneOfs(); | |
147 | + if (!oneOfs.isEmpty()) { | |
148 | + for (OneOfElement oneOfelement : oneOfs) { | |
149 | + MessageDefinition.OneofBuilder oneofBuilder = messageDefinitionBuilder.addOneof(oneOfelement.getName()); | |
150 | + addMessageFieldsToTheOneOfDefinition(oneOfelement.getFields(), oneofBuilder); | |
151 | + } | |
152 | + } | |
153 | + if (!messageElementFields.isEmpty()) { | |
154 | + addMessageFieldsToTheMessageDefinition(messageElementFields, messageDefinitionBuilder); | |
155 | + } | |
156 | + messageDefinitions.add(messageDefinitionBuilder.build()); | |
157 | + }); | |
158 | + return messageDefinitions; | |
159 | + } else { | |
160 | + return Collections.emptyList(); | |
161 | + } | |
162 | + } | |
163 | + | |
164 | + private EnumDefinition getEnumDefinition(EnumElement enumElement) { | |
165 | + List<EnumConstantElement> enumElementTypeConstants = enumElement.getConstants(); | |
166 | + EnumDefinition.Builder enumDefinitionBuilder = EnumDefinition.newBuilder(enumElement.getName()); | |
167 | + if (!enumElementTypeConstants.isEmpty()) { | |
168 | + enumElementTypeConstants.forEach(constantElement -> enumDefinitionBuilder.addValue(constantElement.getName(), constantElement.getTag())); | |
169 | + } | |
170 | + return enumDefinitionBuilder.build(); | |
171 | + } | |
172 | + | |
173 | + | |
174 | + private void addMessageFieldsToTheMessageDefinition(List<FieldElement> messageElementFields, MessageDefinition.Builder messageDefinitionBuilder) { | |
175 | + messageElementFields.forEach(fieldElement -> { | |
176 | + String labelStr = null; | |
177 | + if (fieldElement.getLabel() != null) { | |
178 | + labelStr = fieldElement.getLabel().name().toLowerCase(); | |
179 | + } | |
180 | + messageDefinitionBuilder.addField( | |
181 | + labelStr, | |
182 | + fieldElement.getType(), | |
183 | + fieldElement.getName(), | |
184 | + fieldElement.getTag()); | |
185 | + }); | |
186 | + } | |
187 | + | |
188 | + private void addMessageFieldsToTheOneOfDefinition(List<FieldElement> oneOfsElementFields, MessageDefinition.OneofBuilder oneofBuilder) { | |
189 | + oneOfsElementFields.forEach(fieldElement -> oneofBuilder.addField( | |
190 | + fieldElement.getType(), | |
191 | + fieldElement.getName(), | |
192 | + fieldElement.getTag())); | |
193 | + oneofBuilder.msgDefBuilder(); | |
194 | + } | |
195 | + | |
196 | + private boolean isEmptyStr(String str) { | |
197 | + return str == null || "".equals(str); | |
198 | + } | |
199 | + | |
200 | +} | ... | ... |
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.common.data.device.profile; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
19 | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | |
20 | +import com.fasterxml.jackson.annotation.JsonSubTypes; | |
21 | +import com.fasterxml.jackson.annotation.JsonTypeInfo; | |
22 | +import org.thingsboard.server.common.data.TransportPayloadType; | |
23 | + | |
24 | +@JsonIgnoreProperties(ignoreUnknown = true) | |
25 | +@JsonTypeInfo( | |
26 | + use = JsonTypeInfo.Id.NAME, | |
27 | + include = JsonTypeInfo.As.PROPERTY, | |
28 | + property = "transportPayloadType") | |
29 | +@JsonSubTypes({ | |
30 | + @JsonSubTypes.Type(value = JsonTransportPayloadConfiguration.class, name = "JSON"), | |
31 | + @JsonSubTypes.Type(value = ProtoTransportPayloadConfiguration.class, name = "PROTOBUF")}) | |
32 | +public interface TransportPayloadTypeConfiguration { | |
33 | + | |
34 | + @JsonIgnore | |
35 | + TransportPayloadType getTransportPayloadType(); | |
36 | + | |
37 | +} | ... | ... |
... | ... | @@ -42,6 +42,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura |
42 | 42 | private long maxJSExecutions; |
43 | 43 | private long maxDPStorageDays; |
44 | 44 | private int maxRuleNodeExecutionsPerMessage; |
45 | + private long maxEmails; | |
46 | + private long maxSms; | |
45 | 47 | |
46 | 48 | private double warnThreshold; |
47 | 49 | |
... | ... | @@ -58,6 +60,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura |
58 | 60 | return maxREExecutions; |
59 | 61 | case STORAGE_DP_COUNT: |
60 | 62 | return maxDPStorageDays; |
63 | + case EMAIL_EXEC_COUNT: | |
64 | + return maxEmails; | |
65 | + case SMS_EXEC_COUNT: | |
66 | + return maxSms; | |
61 | 67 | } |
62 | 68 | return 0L; |
63 | 69 | } | ... | ... |
... | ... | @@ -46,7 +46,6 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQue |
46 | 46 | private TbKafkaConsumerTemplate(TbKafkaSettings settings, TbKafkaDecoder<T> decoder, |
47 | 47 | String clientId, String groupId, String topic, |
48 | 48 | boolean autoCommit, int autoCommitIntervalMs, |
49 | - int maxPollRecords, | |
50 | 49 | TbQueueAdmin admin) { |
51 | 50 | super(topic); |
52 | 51 | Properties props = settings.toProps(); |
... | ... | @@ -54,6 +53,9 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQue |
54 | 53 | if (groupId != null) { |
55 | 54 | props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); |
56 | 55 | } |
56 | + if (settings.getMaxPollIntervalMs() > 0) { | |
57 | + props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, settings.getMaxPollIntervalMs()); | |
58 | + } | |
57 | 59 | props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, settings.getMaxPollRecords()); |
58 | 60 | props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, settings.getMaxPartitionFetchBytes()); |
59 | 61 | props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, settings.getFetchMaxBytes()); |
... | ... | @@ -61,9 +63,6 @@ public class TbKafkaConsumerTemplate<T extends TbQueueMsg> extends AbstractTbQue |
61 | 63 | props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, autoCommitIntervalMs); |
62 | 64 | props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer"); |
63 | 65 | props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.ByteArrayDeserializer"); |
64 | - if (maxPollRecords > 0) { | |
65 | - props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords); | |
66 | - } | |
67 | 66 | this.admin = admin; |
68 | 67 | this.consumer = new KafkaConsumer<>(props); |
69 | 68 | this.decoder = decoder; | ... | ... |
... | ... | @@ -63,6 +63,10 @@ public class TbKafkaSettings { |
63 | 63 | @Getter |
64 | 64 | private int maxPollRecords; |
65 | 65 | |
66 | + @Value("${queue.kafka.max_poll_interval_ms:0}") | |
67 | + @Getter | |
68 | + private int maxPollIntervalMs; | |
69 | + | |
66 | 70 | @Value("${queue.kafka.max_partition_fetch_bytes:16777216}") |
67 | 71 | @Getter |
68 | 72 | private int maxPartitionFetchBytes; |
... | ... | @@ -111,4 +115,5 @@ public class TbKafkaSettings { |
111 | 115 | } |
112 | 116 | return props; |
113 | 117 | } |
118 | + | |
114 | 119 | } | ... | ... |
... | ... | @@ -269,8 +269,9 @@ message ProvisionDeviceCredentialsMsg { |
269 | 269 | } |
270 | 270 | |
271 | 271 | message ProvisionDeviceResponseMsg { |
272 | - DeviceCredentialsProto deviceCredentials = 1; | |
273 | - ProvisionResponseStatus provisionResponseStatus = 2; | |
272 | + ProvisionResponseStatus status = 1; | |
273 | + CredentialsType credentialsType = 2; | |
274 | + string credentialsValue = 3; | |
274 | 275 | } |
275 | 276 | |
276 | 277 | enum ProvisionResponseStatus { | ... | ... |