Commit 6b3448d10fc07f95591eec94041f81d5214d9d23

Authored by ShvaykaD
2 parents 22c91039 08de78a8

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.

... ... @@ -33,3 +33,5 @@ pom.xml.versionsBackup
33 33 **/.env
34 34 .instance_id
35 35 rebuild-docker.sh
  36 +.run/**
  37 +.run
\ No newline at end of file
... ...
... ... @@ -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);
... ...
... ... @@ -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);
... ...
... ... @@ -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 +}
... ...
  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">&mdash; 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&nbsp;<a
  154 + style="box-sizing: border-box; color: #999999; margin: 0px;"
  155 + href="mailto:${targetEmail}">${targetEmail}</a>&nbsp;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">&mdash; 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&nbsp;<a
  148 + style="box-sizing: border-box; color: #999999; margin: 0px;"
  149 + href="mailto:${targetEmail}">${targetEmail}</a>&nbsp;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&nbsp;${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">&mdash; 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&nbsp;<a
  155 + style="box-sizing: border-box; color: #999999; margin: 0px;"
  156 + href="mailto:${targetEmail}">${targetEmail}</a>&nbsp;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);
... ...
... ... @@ -27,7 +27,6 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics;
27 27 import java.util.Arrays;
28 28 import java.util.List;
29 29
30   -import static org.junit.Assert.assertEquals;
31 30 import static org.junit.Assert.assertNotNull;
32 31
33 32 @Slf4j
... ...
... ... @@ -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 }
... ...
  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 {
... ...