Commit a3cb9724e3a99b3db6b86c5aeffeafc2aecdc822
Committed by
GitHub
Merge pull request #4191 from thingsboard/master
Merge master to develop 3.3
Showing
100 changed files
with
1596 additions
and
488 deletions
Too many changes to show.
To preserve performance only 100 of 198 files are displayed.
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>application</artifactId> | 26 | <artifactId>application</artifactId> |
@@ -47,7 +47,7 @@ | @@ -47,7 +47,7 @@ | ||
47 | "resources": [], | 47 | "resources": [], |
48 | "templateHtml": "<tb-timeseries-table-widget \n [ctx]=\"ctx\">\n</tb-timeseries-table-widget>", | 48 | "templateHtml": "<tb-timeseries-table-widget \n [ctx]=\"ctx\">\n</tb-timeseries-table-widget>", |
49 | "templateCss": "", | 49 | "templateCss": "", |
50 | - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}", | 50 | + "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n ignoreDataUpdateOnIntervalTick: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}", |
51 | "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showMilliseconds\": {\n \"title\": \"Display timestamp milliseconds\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n }, \n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"hideEmptyLines\": {\n \"title\": \"Hide empty lines\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTimestamp\",\n \"showMilliseconds\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"hideEmptyLines\"\n ]\n}", | 51 | "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showMilliseconds\": {\n \"title\": \"Display timestamp milliseconds\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n }, \n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"hideEmptyLines\": {\n \"title\": \"Hide empty lines\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTimestamp\",\n \"showMilliseconds\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"hideEmptyLines\"\n ]\n}", |
52 | "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", | 52 | "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", |
53 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\"}" | 53 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\"}" |
@@ -134,4 +134,4 @@ | @@ -134,4 +134,4 @@ | ||
134 | } | 134 | } |
135 | } | 135 | } |
136 | ] | 136 | ] |
137 | -} | ||
137 | +} |
@@ -84,11 +84,12 @@ BEGIN | @@ -84,11 +84,12 @@ BEGIN | ||
84 | END IF; | 84 | END IF; |
85 | END IF; | 85 | END IF; |
86 | END IF; | 86 | END IF; |
87 | - END IF; | ||
88 | - IF partition_to_delete IS NOT NULL THEN | ||
89 | - RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete; | ||
90 | - EXECUTE format('DROP TABLE %I', partition_to_delete); | ||
91 | - deleted := deleted + 1; | 87 | + IF partition_to_delete IS NOT NULL THEN |
88 | + RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete; | ||
89 | + EXECUTE format('DROP TABLE IF EXISTS %I', partition_to_delete); | ||
90 | + partition_to_delete := NULL; | ||
91 | + deleted := deleted + 1; | ||
92 | + END IF; | ||
92 | END IF; | 93 | END IF; |
93 | END LOOP; | 94 | END LOOP; |
94 | END IF; | 95 | END IF; |
@@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.Customer; | @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.Customer; | ||
37 | import org.thingsboard.server.common.data.DataConstants; | 37 | import org.thingsboard.server.common.data.DataConstants; |
38 | import org.thingsboard.server.common.data.Device; | 38 | import org.thingsboard.server.common.data.Device; |
39 | import org.thingsboard.server.common.data.DeviceProfile; | 39 | import org.thingsboard.server.common.data.DeviceProfile; |
40 | +import org.thingsboard.server.common.data.EntityType; | ||
40 | import org.thingsboard.server.common.data.TenantProfile; | 41 | import org.thingsboard.server.common.data.TenantProfile; |
41 | import org.thingsboard.server.common.data.alarm.Alarm; | 42 | import org.thingsboard.server.common.data.alarm.Alarm; |
42 | import org.thingsboard.server.common.data.asset.Asset; | 43 | import org.thingsboard.server.common.data.asset.Asset; |
@@ -278,7 +279,21 @@ class DefaultTbContext implements TbContext { | @@ -278,7 +279,21 @@ class DefaultTbContext implements TbContext { | ||
278 | } | 279 | } |
279 | 280 | ||
280 | public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) { | 281 | public TbMsg deviceCreatedMsg(Device device, RuleNodeId ruleNodeId) { |
281 | - return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED); | 282 | + RuleChainId ruleChainId = null; |
283 | + String queueName = ServiceQueue.MAIN; | ||
284 | + if (device.getDeviceProfileId() != null) { | ||
285 | + DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().find(device.getDeviceProfileId()); | ||
286 | + if (deviceProfile == null) { | ||
287 | + log.warn("[{}] Device profile is null!", device.getDeviceProfileId()); | ||
288 | + ruleChainId = null; | ||
289 | + queueName = ServiceQueue.MAIN; | ||
290 | + } else { | ||
291 | + ruleChainId = deviceProfile.getDefaultRuleChainId(); | ||
292 | + String defaultQueueName = deviceProfile.getDefaultQueueName(); | ||
293 | + queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN; | ||
294 | + } | ||
295 | + } | ||
296 | + return entityActionMsg(device, device.getId(), ruleNodeId, DataConstants.ENTITY_CREATED, queueName, ruleChainId); | ||
282 | } | 297 | } |
283 | 298 | ||
284 | public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) { | 299 | public TbMsg assetCreatedMsg(Asset asset, RuleNodeId ruleNodeId) { |
@@ -286,12 +301,31 @@ class DefaultTbContext implements TbContext { | @@ -286,12 +301,31 @@ class DefaultTbContext implements TbContext { | ||
286 | } | 301 | } |
287 | 302 | ||
288 | public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) { | 303 | public TbMsg alarmActionMsg(Alarm alarm, RuleNodeId ruleNodeId, String action) { |
289 | - return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action); | 304 | + RuleChainId ruleChainId = null; |
305 | + String queueName = ServiceQueue.MAIN; | ||
306 | + if (EntityType.DEVICE.equals(alarm.getOriginator().getEntityType())) { | ||
307 | + DeviceId deviceId = new DeviceId(alarm.getOriginator().getId()); | ||
308 | + DeviceProfile deviceProfile = mainCtx.getDeviceProfileCache().get(getTenantId(), deviceId); | ||
309 | + if (deviceProfile == null) { | ||
310 | + log.warn("[{}] Device profile is null!", deviceId); | ||
311 | + ruleChainId = null; | ||
312 | + queueName = ServiceQueue.MAIN; | ||
313 | + } else { | ||
314 | + ruleChainId = deviceProfile.getDefaultRuleChainId(); | ||
315 | + String defaultQueueName = deviceProfile.getDefaultQueueName(); | ||
316 | + queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN; | ||
317 | + } | ||
318 | + } | ||
319 | + return entityActionMsg(alarm, alarm.getId(), ruleNodeId, action, queueName, ruleChainId); | ||
290 | } | 320 | } |
291 | 321 | ||
292 | public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) { | 322 | public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action) { |
323 | + return entityActionMsg(entity, id, ruleNodeId, action, ServiceQueue.MAIN, null); | ||
324 | + } | ||
325 | + | ||
326 | + public <E, I extends EntityId> TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, String action, String queueName, RuleChainId ruleChainId) { | ||
293 | try { | 327 | try { |
294 | - return TbMsg.newMsg(action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity))); | 328 | + return TbMsg.newMsg(queueName, action, id, getActionMetaData(ruleNodeId), mapper.writeValueAsString(mapper.valueToTree(entity)), ruleChainId, null); |
295 | } catch (JsonProcessingException | IllegalArgumentException e) { | 329 | } catch (JsonProcessingException | IllegalArgumentException e) { |
296 | throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + action + " msg: " + e); | 330 | throw new RuntimeException("Failed to process " + id.getEntityType().name().toLowerCase() + " " + action + " msg: " + e); |
297 | } | 331 | } |
@@ -215,6 +215,7 @@ public class AuthController extends BaseController { | @@ -215,6 +215,7 @@ public class AuthController extends BaseController { | ||
215 | User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId()); | 215 | User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId()); |
216 | UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); | 216 | UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail()); |
217 | SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); | 217 | SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal); |
218 | + userService.setUserCredentialsEnabled(user.getTenantId(), user.getId(), true); | ||
218 | String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); | 219 | String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request); |
219 | String loginUrl = String.format("%s/login", baseUrl); | 220 | String loginUrl = String.format("%s/login", baseUrl); |
220 | String email = user.getEmail(); | 221 | String email = user.getEmail(); |
@@ -94,12 +94,24 @@ public class UserController extends BaseController { | @@ -94,12 +94,24 @@ public class UserController extends BaseController { | ||
94 | processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD); | 94 | processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD); |
95 | processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD); | 95 | processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD); |
96 | } | 96 | } |
97 | + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); | ||
98 | + if(userCredentials.isEnabled()) { | ||
99 | + addUserCredentialsEnabled((ObjectNode) user.getAdditionalInfo()); | ||
100 | + } | ||
97 | return user; | 101 | return user; |
98 | } catch (Exception e) { | 102 | } catch (Exception e) { |
99 | throw handleException(e); | 103 | throw handleException(e); |
100 | } | 104 | } |
101 | } | 105 | } |
102 | 106 | ||
107 | + private void addUserCredentialsEnabled(ObjectNode additionalInfo) { | ||
108 | + if(!additionalInfo.isNull()) { | ||
109 | + if(!additionalInfo.has("userCredentialsEnabled")) { | ||
110 | + additionalInfo.put("userCredentialsEnabled", true); | ||
111 | + } | ||
112 | + } | ||
113 | + } | ||
114 | + | ||
103 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | 115 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
104 | @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET) | 116 | @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET) |
105 | @ResponseBody | 117 | @ResponseBody |
@@ -193,13 +205,13 @@ public class UserController extends BaseController { | @@ -193,13 +205,13 @@ public class UserController extends BaseController { | ||
193 | user.getId(), user); | 205 | user.getId(), user); |
194 | 206 | ||
195 | UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId()); | 207 | UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId()); |
196 | - if (!userCredentials.isEnabled()) { | 208 | + if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) { |
197 | String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); | 209 | String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); |
198 | String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, | 210 | String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, |
199 | userCredentials.getActivateToken()); | 211 | userCredentials.getActivateToken()); |
200 | mailService.sendActivationEmail(activateUrl, email); | 212 | mailService.sendActivationEmail(activateUrl, email); |
201 | } else { | 213 | } else { |
202 | - throw new ThingsboardException("User is already active!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); | 214 | + throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); |
203 | } | 215 | } |
204 | } catch (Exception e) { | 216 | } catch (Exception e) { |
205 | throw handleException(e); | 217 | throw handleException(e); |
@@ -218,13 +230,13 @@ public class UserController extends BaseController { | @@ -218,13 +230,13 @@ public class UserController extends BaseController { | ||
218 | User user = checkUserId(userId, Operation.READ); | 230 | User user = checkUserId(userId, Operation.READ); |
219 | SecurityUser authUser = getCurrentUser(); | 231 | SecurityUser authUser = getCurrentUser(); |
220 | UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId()); | 232 | UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId()); |
221 | - if (!userCredentials.isEnabled()) { | 233 | + if (!userCredentials.isEnabled() && userCredentials.getActivateToken() != null) { |
222 | String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); | 234 | String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request); |
223 | String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, | 235 | String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl, |
224 | userCredentials.getActivateToken()); | 236 | userCredentials.getActivateToken()); |
225 | return activateUrl; | 237 | return activateUrl; |
226 | } else { | 238 | } else { |
227 | - throw new ThingsboardException("User is already active!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); | 239 | + throw new ThingsboardException("User is already activated!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); |
228 | } | 240 | } |
229 | } catch (Exception e) { | 241 | } catch (Exception e) { |
230 | throw handleException(e); | 242 | throw handleException(e); |
@@ -186,7 +186,11 @@ public class ThingsboardInstallService { | @@ -186,7 +186,11 @@ public class ThingsboardInstallService { | ||
186 | log.info("Upgrading ThingsBoard from version 3.2.0 to 3.2.1 ..."); | 186 | log.info("Upgrading ThingsBoard from version 3.2.0 to 3.2.1 ..."); |
187 | databaseEntitiesUpgradeService.upgradeDatabase("3.2.0"); | 187 | databaseEntitiesUpgradeService.upgradeDatabase("3.2.0"); |
188 | case "3.2.1": | 188 | case "3.2.1": |
189 | - log.info("Upgrading ThingsBoard from version 3.2.1 to 3.3.0 ..."); | 189 | + log.info("Upgrading ThingsBoard from version 3.2.1 to 3.2.2 ..."); |
190 | + if (databaseTsUpgradeService != null) { | ||
191 | + databaseTsUpgradeService.upgradeDatabase("3.2.1"); | ||
192 | + } | ||
193 | + | ||
190 | log.info("Updating system data..."); | 194 | log.info("Updating system data..."); |
191 | systemDataLoaderService.updateSystemWidgets(); | 195 | systemDataLoaderService.updateSystemWidgets(); |
192 | break; | 196 | break; |
@@ -50,6 +50,7 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase | @@ -50,6 +50,7 @@ public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabase | ||
50 | break; | 50 | break; |
51 | case "2.5.0": | 51 | case "2.5.0": |
52 | case "3.1.1": | 52 | case "3.1.1": |
53 | + case "3.2.1": | ||
53 | break; | 54 | break; |
54 | default: | 55 | default: |
55 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); | 56 | throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
@@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.TenantProfile; | @@ -36,6 +36,9 @@ import org.thingsboard.server.common.data.TenantProfile; | ||
36 | import org.thingsboard.server.common.data.User; | 36 | import org.thingsboard.server.common.data.User; |
37 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; | 37 | import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
38 | import org.thingsboard.server.common.data.device.profile.AlarmCondition; | 38 | import org.thingsboard.server.common.data.device.profile.AlarmCondition; |
39 | +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter; | ||
40 | +import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey; | ||
41 | +import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; | ||
39 | import org.thingsboard.server.common.data.device.profile.AlarmRule; | 42 | import org.thingsboard.server.common.data.device.profile.AlarmRule; |
40 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; | 43 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
41 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; | 44 | import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; |
@@ -290,16 +293,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -290,16 +293,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
290 | AlarmCondition temperatureCondition = new AlarmCondition(); | 293 | AlarmCondition temperatureCondition = new AlarmCondition(); |
291 | temperatureCondition.setSpec(new SimpleAlarmConditionSpec()); | 294 | temperatureCondition.setSpec(new SimpleAlarmConditionSpec()); |
292 | 295 | ||
293 | - KeyFilter temperatureAlarmFlagAttributeFilter = new KeyFilter(); | ||
294 | - temperatureAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperatureAlarmFlag")); | 296 | + AlarmConditionFilter temperatureAlarmFlagAttributeFilter = new AlarmConditionFilter(); |
297 | + temperatureAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "temperatureAlarmFlag")); | ||
295 | temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); | 298 | temperatureAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); |
296 | BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate(); | 299 | BooleanFilterPredicate temperatureAlarmFlagAttributePredicate = new BooleanFilterPredicate(); |
297 | temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); | 300 | temperatureAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); |
298 | temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); | 301 | temperatureAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); |
299 | temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate); | 302 | temperatureAlarmFlagAttributeFilter.setPredicate(temperatureAlarmFlagAttributePredicate); |
300 | 303 | ||
301 | - KeyFilter temperatureTimeseriesFilter = new KeyFilter(); | ||
302 | - temperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); | 304 | + AlarmConditionFilter temperatureTimeseriesFilter = new AlarmConditionFilter(); |
305 | + temperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); | ||
303 | temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | 306 | temperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); |
304 | NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); | 307 | NumericFilterPredicate temperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); |
305 | temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); | 308 | temperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); |
@@ -317,8 +320,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -317,8 +320,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
317 | AlarmCondition clearTemperatureCondition = new AlarmCondition(); | 320 | AlarmCondition clearTemperatureCondition = new AlarmCondition(); |
318 | clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec()); | 321 | clearTemperatureCondition.setSpec(new SimpleAlarmConditionSpec()); |
319 | 322 | ||
320 | - KeyFilter clearTemperatureTimeseriesFilter = new KeyFilter(); | ||
321 | - clearTemperatureTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")); | 323 | + AlarmConditionFilter clearTemperatureTimeseriesFilter = new AlarmConditionFilter(); |
324 | + clearTemperatureTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "temperature")); | ||
322 | clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | 325 | clearTemperatureTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); |
323 | NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); | 326 | NumericFilterPredicate clearTemperatureTimeseriesFilterPredicate = new NumericFilterPredicate(); |
324 | clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL); | 327 | clearTemperatureTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL); |
@@ -340,16 +343,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -340,16 +343,16 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
340 | AlarmCondition humidityCondition = new AlarmCondition(); | 343 | AlarmCondition humidityCondition = new AlarmCondition(); |
341 | humidityCondition.setSpec(new SimpleAlarmConditionSpec()); | 344 | humidityCondition.setSpec(new SimpleAlarmConditionSpec()); |
342 | 345 | ||
343 | - KeyFilter humidityAlarmFlagAttributeFilter = new KeyFilter(); | ||
344 | - humidityAlarmFlagAttributeFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "humidityAlarmFlag")); | 346 | + AlarmConditionFilter humidityAlarmFlagAttributeFilter = new AlarmConditionFilter(); |
347 | + humidityAlarmFlagAttributeFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, "humidityAlarmFlag")); | ||
345 | humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); | 348 | humidityAlarmFlagAttributeFilter.setValueType(EntityKeyValueType.BOOLEAN); |
346 | BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate(); | 349 | BooleanFilterPredicate humidityAlarmFlagAttributePredicate = new BooleanFilterPredicate(); |
347 | humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); | 350 | humidityAlarmFlagAttributePredicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL); |
348 | humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); | 351 | humidityAlarmFlagAttributePredicate.setValue(new FilterPredicateValue<>(Boolean.TRUE)); |
349 | humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate); | 352 | humidityAlarmFlagAttributeFilter.setPredicate(humidityAlarmFlagAttributePredicate); |
350 | 353 | ||
351 | - KeyFilter humidityTimeseriesFilter = new KeyFilter(); | ||
352 | - humidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); | 354 | + AlarmConditionFilter humidityTimeseriesFilter = new AlarmConditionFilter(); |
355 | + humidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity")); | ||
353 | humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | 356 | humidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); |
354 | NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate(); | 357 | NumericFilterPredicate humidityTimeseriesFilterPredicate = new NumericFilterPredicate(); |
355 | humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); | 358 | humidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.LESS); |
@@ -368,8 +371,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | @@ -368,8 +371,8 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { | ||
368 | AlarmCondition clearHumidityCondition = new AlarmCondition(); | 371 | AlarmCondition clearHumidityCondition = new AlarmCondition(); |
369 | clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec()); | 372 | clearHumidityCondition.setSpec(new SimpleAlarmConditionSpec()); |
370 | 373 | ||
371 | - KeyFilter clearHumidityTimeseriesFilter = new KeyFilter(); | ||
372 | - clearHumidityTimeseriesFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "humidity")); | 374 | + AlarmConditionFilter clearHumidityTimeseriesFilter = new AlarmConditionFilter(); |
375 | + clearHumidityTimeseriesFilter.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.TIME_SERIES, "humidity")); | ||
373 | clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); | 376 | clearHumidityTimeseriesFilter.setValueType(EntityKeyValueType.NUMERIC); |
374 | NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate(); | 377 | NumericFilterPredicate clearHumidityTimeseriesFilterPredicate = new NumericFilterPredicate(); |
375 | clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL); | 378 | clearHumidityTimeseriesFilterPredicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL); |
@@ -196,11 +196,17 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe | @@ -196,11 +196,17 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe | ||
196 | } | 196 | } |
197 | break; | 197 | break; |
198 | case "3.1.1": | 198 | case "3.1.1": |
199 | + case "3.2.1": | ||
199 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { | 200 | try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { |
200 | log.info("Load TTL functions ..."); | 201 | log.info("Load TTL functions ..."); |
201 | loadSql(conn, LOAD_TTL_FUNCTIONS_SQL); | 202 | loadSql(conn, LOAD_TTL_FUNCTIONS_SQL); |
202 | log.info("Load Drop Partitions functions ..."); | 203 | log.info("Load Drop Partitions functions ..."); |
203 | loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL); | 204 | loadSql(conn, LOAD_DROP_PARTITIONS_FUNCTIONS_SQL); |
205 | + | ||
206 | + executeQuery(conn, "DROP PROCEDURE IF EXISTS cleanup_timeseries_by_ttl(character varying, bigint, bigint);"); | ||
207 | + executeQuery(conn, "DROP FUNCTION IF EXISTS delete_asset_records_from_ts_kv(character varying, character varying, bigint);"); | ||
208 | + executeQuery(conn, "DROP FUNCTION IF EXISTS delete_device_records_from_ts_kv(character varying, character varying, bigint);"); | ||
209 | + executeQuery(conn, "DROP FUNCTION IF EXISTS delete_customer_records_from_ts_kv(character varying, character varying, bigint);"); | ||
204 | } | 210 | } |
205 | break; | 211 | break; |
206 | default: | 212 | default: |
@@ -178,6 +178,7 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr | @@ -178,6 +178,7 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr | ||
178 | } | 178 | } |
179 | break; | 179 | break; |
180 | case "3.1.1": | 180 | case "3.1.1": |
181 | + case "3.2.1": | ||
181 | break; | 182 | break; |
182 | default: | 183 | default: |
183 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); | 184 | throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); |
@@ -145,7 +145,7 @@ public class DefaultTbClusterService implements TbClusterService { | @@ -145,7 +145,7 @@ public class DefaultTbClusterService implements TbClusterService { | ||
145 | tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId()))); | 145 | tbMsg = transformMsg(tbMsg, deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId()))); |
146 | } | 146 | } |
147 | } | 147 | } |
148 | - TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tenantId, entityId); | 148 | + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_RULE_ENGINE, tbMsg.getQueueName(), tenantId, entityId); |
149 | log.trace("PUSHING msg: {} to:{}", tbMsg, tpi); | 149 | log.trace("PUSHING msg: {} to:{}", tbMsg, tpi); |
150 | ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder() | 150 | ToRuleEngineMsg msg = ToRuleEngineMsg.newBuilder() |
151 | .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) | 151 | .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) |
@@ -55,31 +55,22 @@ public class TbCoreConsumerStats { | @@ -55,31 +55,22 @@ public class TbCoreConsumerStats { | ||
55 | public TbCoreConsumerStats(StatsFactory statsFactory) { | 55 | public TbCoreConsumerStats(StatsFactory statsFactory) { |
56 | String statsKey = StatsType.CORE.getName(); | 56 | String statsKey = StatsType.CORE.getName(); |
57 | 57 | ||
58 | - this.totalCounter = statsFactory.createStatsCounter(statsKey, TOTAL_MSGS); | ||
59 | - this.sessionEventCounter = statsFactory.createStatsCounter(statsKey, SESSION_EVENTS); | ||
60 | - this.getAttributesCounter = statsFactory.createStatsCounter(statsKey, GET_ATTRIBUTE); | ||
61 | - this.subscribeToAttributesCounter = statsFactory.createStatsCounter(statsKey, ATTRIBUTE_SUBSCRIBES); | ||
62 | - this.subscribeToRPCCounter = statsFactory.createStatsCounter(statsKey, RPC_SUBSCRIBES); | ||
63 | - this.toDeviceRPCCallResponseCounter = statsFactory.createStatsCounter(statsKey, TO_DEVICE_RPC_CALL_RESPONSES); | ||
64 | - this.subscriptionInfoCounter = statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_INFO); | ||
65 | - this.claimDeviceCounter = statsFactory.createStatsCounter(statsKey, DEVICE_CLAIMS); | ||
66 | - this.deviceStateCounter = statsFactory.createStatsCounter(statsKey, DEVICE_STATES); | ||
67 | - this.subscriptionMsgCounter = statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_MSGS); | ||
68 | - this.toCoreNotificationsCounter = statsFactory.createStatsCounter(statsKey, TO_CORE_NOTIFICATIONS); | ||
69 | - | ||
70 | - | ||
71 | - counters.add(totalCounter); | ||
72 | - counters.add(sessionEventCounter); | ||
73 | - counters.add(getAttributesCounter); | ||
74 | - counters.add(subscribeToAttributesCounter); | ||
75 | - counters.add(subscribeToRPCCounter); | ||
76 | - counters.add(toDeviceRPCCallResponseCounter); | ||
77 | - counters.add(subscriptionInfoCounter); | ||
78 | - counters.add(claimDeviceCounter); | 58 | + this.totalCounter = register(statsFactory.createStatsCounter(statsKey, TOTAL_MSGS)); |
59 | + this.sessionEventCounter = register(statsFactory.createStatsCounter(statsKey, SESSION_EVENTS)); | ||
60 | + this.getAttributesCounter = register(statsFactory.createStatsCounter(statsKey, GET_ATTRIBUTE)); | ||
61 | + this.subscribeToAttributesCounter = register(statsFactory.createStatsCounter(statsKey, ATTRIBUTE_SUBSCRIBES)); | ||
62 | + this.subscribeToRPCCounter = register(statsFactory.createStatsCounter(statsKey, RPC_SUBSCRIBES)); | ||
63 | + this.toDeviceRPCCallResponseCounter = register(statsFactory.createStatsCounter(statsKey, TO_DEVICE_RPC_CALL_RESPONSES)); | ||
64 | + this.subscriptionInfoCounter = register(statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_INFO)); | ||
65 | + this.claimDeviceCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_CLAIMS)); | ||
66 | + this.deviceStateCounter = register(statsFactory.createStatsCounter(statsKey, DEVICE_STATES)); | ||
67 | + this.subscriptionMsgCounter = register(statsFactory.createStatsCounter(statsKey, SUBSCRIPTION_MSGS)); | ||
68 | + this.toCoreNotificationsCounter = register(statsFactory.createStatsCounter(statsKey, TO_CORE_NOTIFICATIONS)); | ||
69 | + } | ||
79 | 70 | ||
80 | - counters.add(deviceStateCounter); | ||
81 | - counters.add(subscriptionMsgCounter); | ||
82 | - counters.add(toCoreNotificationsCounter); | 71 | + private StatsCounter register(StatsCounter counter){ |
72 | + counters.add(counter); | ||
73 | + return counter; | ||
83 | } | 74 | } |
84 | 75 | ||
85 | public void log(TransportProtos.TransportToDeviceActorMsg msg) { | 76 | public void log(TransportProtos.TransportToDeviceActorMsg msg) { |
@@ -24,7 +24,7 @@ import java.util.regex.Pattern; | @@ -24,7 +24,7 @@ import java.util.regex.Pattern; | ||
24 | @Slf4j | 24 | @Slf4j |
25 | public abstract class AbstractSmsSender implements SmsSender { | 25 | public abstract class AbstractSmsSender implements SmsSender { |
26 | 26 | ||
27 | - private static final Pattern E_164_PHONE_NUMBER_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$"); | 27 | + protected static final Pattern E_164_PHONE_NUMBER_PATTERN = Pattern.compile("^\\+[1-9]\\d{1,14}$"); |
28 | 28 | ||
29 | private static final int MAX_SMS_MESSAGE_LENGTH = 1600; | 29 | private static final int MAX_SMS_MESSAGE_LENGTH = 1600; |
30 | private static final int MAX_SMS_SEGMENT_LENGTH = 70; | 30 | private static final int MAX_SMS_SEGMENT_LENGTH = 70; |
@@ -19,21 +19,34 @@ import com.twilio.http.TwilioRestClient; | @@ -19,21 +19,34 @@ import com.twilio.http.TwilioRestClient; | ||
19 | import com.twilio.rest.api.v2010.account.Message; | 19 | import com.twilio.rest.api.v2010.account.Message; |
20 | import com.twilio.type.PhoneNumber; | 20 | import com.twilio.type.PhoneNumber; |
21 | import org.apache.commons.lang3.StringUtils; | 21 | import org.apache.commons.lang3.StringUtils; |
22 | +import org.thingsboard.rule.engine.api.sms.exception.SmsParseException; | ||
22 | import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; | 23 | import org.thingsboard.server.common.data.sms.config.TwilioSmsProviderConfiguration; |
23 | import org.thingsboard.rule.engine.api.sms.exception.SmsException; | 24 | import org.thingsboard.rule.engine.api.sms.exception.SmsException; |
24 | import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; | 25 | import org.thingsboard.rule.engine.api.sms.exception.SmsSendException; |
25 | import org.thingsboard.server.service.sms.AbstractSmsSender; | 26 | import org.thingsboard.server.service.sms.AbstractSmsSender; |
26 | 27 | ||
28 | +import java.util.regex.Pattern; | ||
29 | + | ||
27 | public class TwilioSmsSender extends AbstractSmsSender { | 30 | public class TwilioSmsSender extends AbstractSmsSender { |
28 | 31 | ||
32 | + private static final Pattern PHONE_NUMBERS_SID_MESSAGE_SERVICE_SID = Pattern.compile("^(PN|MG).*$"); | ||
33 | + | ||
29 | private TwilioRestClient twilioRestClient; | 34 | private TwilioRestClient twilioRestClient; |
30 | private String numberFrom; | 35 | private String numberFrom; |
31 | 36 | ||
37 | + private String validatePhoneTwilioNumber(String phoneNumber) throws SmsParseException { | ||
38 | + phoneNumber = phoneNumber.trim(); | ||
39 | + if (!E_164_PHONE_NUMBER_PATTERN.matcher(phoneNumber).matches() && !PHONE_NUMBERS_SID_MESSAGE_SERVICE_SID.matcher(phoneNumber).matches()) { | ||
40 | + throw new SmsParseException("Invalid phone number format. Phone number must be in E.164 format/Phone Number's SID/Messaging Service SID."); | ||
41 | + } | ||
42 | + return phoneNumber; | ||
43 | + } | ||
44 | + | ||
32 | public TwilioSmsSender(TwilioSmsProviderConfiguration config) { | 45 | public TwilioSmsSender(TwilioSmsProviderConfiguration config) { |
33 | if (StringUtils.isEmpty(config.getAccountSid()) || StringUtils.isEmpty(config.getAccountToken()) || StringUtils.isEmpty(config.getNumberFrom())) { | 46 | 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!"); | 47 | throw new IllegalArgumentException("Invalid twilio sms provider configuration: accountSid, accountToken and numberFrom should be specified!"); |
35 | } | 48 | } |
36 | - this.numberFrom = this.validatePhoneNumber(config.getNumberFrom()); | 49 | + this.numberFrom = this.validatePhoneTwilioNumber(config.getNumberFrom()); |
37 | this.twilioRestClient = new TwilioRestClient.Builder(config.getAccountSid(), config.getAccountToken()).build(); | 50 | this.twilioRestClient = new TwilioRestClient.Builder(config.getAccountSid(), config.getAccountToken()).build(); |
38 | } | 51 | } |
39 | 52 |
@@ -54,6 +54,7 @@ import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; | @@ -54,6 +54,7 @@ import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; | ||
54 | import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; | 54 | import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; |
55 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; | 55 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; |
56 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUpdate; | 56 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUpdate; |
57 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; | ||
57 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; | 58 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; |
58 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; | 59 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; |
59 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd; | 60 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd; |
@@ -92,7 +93,7 @@ import java.util.stream.Collectors; | @@ -92,7 +93,7 @@ import java.util.stream.Collectors; | ||
92 | public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubscriptionService { | 93 | public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubscriptionService { |
93 | 94 | ||
94 | private static final int DEFAULT_LIMIT = 100; | 95 | private static final int DEFAULT_LIMIT = 100; |
95 | - private final Map<String, Map<Integer, TbAbstractDataSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>(); | 96 | + private final Map<String, Map<Integer, TbAbstractSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>(); |
96 | 97 | ||
97 | @Autowired | 98 | @Autowired |
98 | private TelemetryWebSocketService wsService; | 99 | private TelemetryWebSocketService wsService; |
@@ -202,7 +203,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | @@ -202,7 +203,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | ||
202 | //TODO: validate number of dynamic page links against rate limits. Ignore dynamic flag if limit is reached. | 203 | //TODO: validate number of dynamic page links against rate limits. Ignore dynamic flag if limit is reached. |
203 | TbEntityDataSubCtx finalCtx = ctx; | 204 | TbEntityDataSubCtx finalCtx = ctx; |
204 | ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay( | 205 | ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay( |
205 | - () -> refreshDynamicQuery(tenantId, customerId, finalCtx), | 206 | + () -> refreshDynamicQuery(finalCtx), |
206 | dynamicPageLinkRefreshInterval, dynamicPageLinkRefreshInterval, TimeUnit.SECONDS); | 207 | dynamicPageLinkRefreshInterval, dynamicPageLinkRefreshInterval, TimeUnit.SECONDS); |
207 | finalCtx.setRefreshTask(task); | 208 | finalCtx.setRefreshTask(task); |
208 | } | 209 | } |
@@ -236,6 +237,26 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | @@ -236,6 +237,26 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | ||
236 | } | 237 | } |
237 | 238 | ||
238 | @Override | 239 | @Override |
240 | + public void handleCmd(TelemetryWebSocketSessionRef session, EntityCountCmd cmd) { | ||
241 | + TbEntityCountSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId()); | ||
242 | + if (ctx == null) { | ||
243 | + ctx = createSubCtx(session, cmd); | ||
244 | + long start = System.currentTimeMillis(); | ||
245 | + ctx.fetchData(); | ||
246 | + long end = System.currentTimeMillis(); | ||
247 | + stats.getRegularQueryInvocationCnt().incrementAndGet(); | ||
248 | + stats.getRegularQueryTimeSpent().addAndGet(end - start); | ||
249 | + TbEntityCountSubCtx finalCtx = ctx; | ||
250 | + ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay( | ||
251 | + () -> refreshDynamicQuery(finalCtx), | ||
252 | + dynamicPageLinkRefreshInterval, dynamicPageLinkRefreshInterval, TimeUnit.SECONDS); | ||
253 | + finalCtx.setRefreshTask(task); | ||
254 | + } else { | ||
255 | + log.debug("[{}][{}] Received duplicate command: {}", session.getSessionId(), cmd.getCmdId(), cmd); | ||
256 | + } | ||
257 | + } | ||
258 | + | ||
259 | + @Override | ||
239 | public void handleCmd(TelemetryWebSocketSessionRef session, AlarmDataCmd cmd) { | 260 | public void handleCmd(TelemetryWebSocketSessionRef session, AlarmDataCmd cmd) { |
240 | TbAlarmDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId()); | 261 | TbAlarmDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId()); |
241 | if (ctx == null) { | 262 | if (ctx == null) { |
@@ -267,7 +288,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | @@ -267,7 +288,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | ||
267 | } | 288 | } |
268 | } | 289 | } |
269 | 290 | ||
270 | - private void refreshDynamicQuery(TenantId tenantId, CustomerId customerId, TbEntityDataSubCtx finalCtx) { | 291 | + private void refreshDynamicQuery(TbAbstractSubCtx finalCtx) { |
271 | try { | 292 | try { |
272 | long start = System.currentTimeMillis(); | 293 | long start = System.currentTimeMillis(); |
273 | finalCtx.update(); | 294 | finalCtx.update(); |
@@ -299,7 +320,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | @@ -299,7 +320,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | ||
299 | } | 320 | } |
300 | 321 | ||
301 | private TbEntityDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) { | 322 | private TbEntityDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) { |
302 | - Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); | 323 | + Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); |
303 | TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, | 324 | TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, |
304 | attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription); | 325 | attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription); |
305 | if (cmd.getQuery() != null) { | 326 | if (cmd.getQuery() != null) { |
@@ -309,8 +330,20 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | @@ -309,8 +330,20 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | ||
309 | return ctx; | 330 | return ctx; |
310 | } | 331 | } |
311 | 332 | ||
333 | + private TbEntityCountSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) { | ||
334 | + Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); | ||
335 | + TbEntityCountSubCtx ctx = new TbEntityCountSubCtx(serviceId, wsService, entityService, localSubscriptionService, | ||
336 | + attributesService, stats, sessionRef, cmd.getCmdId()); | ||
337 | + if (cmd.getQuery() != null) { | ||
338 | + ctx.setAndResolveQuery(cmd.getQuery()); | ||
339 | + } | ||
340 | + sessionSubs.put(cmd.getCmdId(), ctx); | ||
341 | + return ctx; | ||
342 | + } | ||
343 | + | ||
344 | + | ||
312 | private TbAlarmDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { | 345 | private TbAlarmDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { |
313 | - Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); | 346 | + Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); |
314 | TbAlarmDataSubCtx ctx = new TbAlarmDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, | 347 | TbAlarmDataSubCtx ctx = new TbAlarmDataSubCtx(serviceId, wsService, entityService, localSubscriptionService, |
315 | attributesService, stats, alarmService, sessionRef, cmd.getCmdId(), maxEntitiesPerAlarmSubscription); | 348 | attributesService, stats, alarmService, sessionRef, cmd.getCmdId(), maxEntitiesPerAlarmSubscription); |
316 | ctx.setAndResolveQuery(cmd.getQuery()); | 349 | ctx.setAndResolveQuery(cmd.getQuery()); |
@@ -319,8 +352,8 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | @@ -319,8 +352,8 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | ||
319 | } | 352 | } |
320 | 353 | ||
321 | @SuppressWarnings("unchecked") | 354 | @SuppressWarnings("unchecked") |
322 | - private <T extends TbAbstractDataSubCtx> T getSubCtx(String sessionId, int cmdId) { | ||
323 | - Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId); | 355 | + private <T extends TbAbstractSubCtx> T getSubCtx(String sessionId, int cmdId) { |
356 | + Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId); | ||
324 | if (sessionSubs != null) { | 357 | if (sessionSubs != null) { |
325 | return (T) sessionSubs.get(cmdId); | 358 | return (T) sessionSubs.get(cmdId); |
326 | } else { | 359 | } else { |
@@ -464,17 +497,16 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | @@ -464,17 +497,16 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc | ||
464 | cleanupAndCancel(getSubCtx(sessionId, cmd.getCmdId())); | 497 | cleanupAndCancel(getSubCtx(sessionId, cmd.getCmdId())); |
465 | } | 498 | } |
466 | 499 | ||
467 | - private void cleanupAndCancel(TbAbstractDataSubCtx ctx) { | 500 | + private void cleanupAndCancel(TbAbstractSubCtx ctx) { |
468 | if (ctx != null) { | 501 | if (ctx != null) { |
469 | ctx.cancelTasks(); | 502 | ctx.cancelTasks(); |
470 | - ctx.clearEntitySubscriptions(); | ||
471 | - ctx.clearDynamicValueSubscriptions(); | 503 | + ctx.clearSubscriptions(); |
472 | } | 504 | } |
473 | } | 505 | } |
474 | 506 | ||
475 | @Override | 507 | @Override |
476 | public void cancelAllSessionSubscriptions(String sessionId) { | 508 | public void cancelAllSessionSubscriptions(String sessionId) { |
477 | - Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId); | 509 | + Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId); |
478 | if (sessionSubs != null) { | 510 | if (sessionSubs != null) { |
479 | sessionSubs.values().forEach(this::cleanupAndCancel); | 511 | sessionSubs.values().forEach(this::cleanupAndCancel); |
480 | } | 512 | } |
@@ -15,32 +15,16 @@ | @@ -15,32 +15,16 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.subscription; | 16 | package org.thingsboard.server.service.subscription; |
17 | 17 | ||
18 | -import com.google.common.util.concurrent.Futures; | ||
19 | -import com.google.common.util.concurrent.ListenableFuture; | ||
20 | -import com.google.common.util.concurrent.MoreExecutors; | ||
21 | -import lombok.Data; | ||
22 | import lombok.Getter; | 18 | import lombok.Getter; |
23 | -import lombok.Setter; | ||
24 | import lombok.extern.slf4j.Slf4j; | 19 | import lombok.extern.slf4j.Slf4j; |
25 | -import org.thingsboard.server.common.data.id.CustomerId; | ||
26 | import org.thingsboard.server.common.data.id.EntityId; | 20 | import org.thingsboard.server.common.data.id.EntityId; |
27 | -import org.thingsboard.server.common.data.id.TenantId; | ||
28 | -import org.thingsboard.server.common.data.id.UserId; | ||
29 | -import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
30 | import org.thingsboard.server.common.data.page.PageData; | 21 | import org.thingsboard.server.common.data.page.PageData; |
31 | import org.thingsboard.server.common.data.query.AbstractDataQuery; | 22 | import org.thingsboard.server.common.data.query.AbstractDataQuery; |
32 | -import org.thingsboard.server.common.data.query.ComplexFilterPredicate; | ||
33 | -import org.thingsboard.server.common.data.query.DynamicValue; | ||
34 | -import org.thingsboard.server.common.data.query.DynamicValueSourceType; | ||
35 | import org.thingsboard.server.common.data.query.EntityData; | 23 | import org.thingsboard.server.common.data.query.EntityData; |
36 | import org.thingsboard.server.common.data.query.EntityDataPageLink; | 24 | import org.thingsboard.server.common.data.query.EntityDataPageLink; |
37 | import org.thingsboard.server.common.data.query.EntityDataQuery; | 25 | import org.thingsboard.server.common.data.query.EntityDataQuery; |
38 | import org.thingsboard.server.common.data.query.EntityKey; | 26 | import org.thingsboard.server.common.data.query.EntityKey; |
39 | import org.thingsboard.server.common.data.query.EntityKeyType; | 27 | import org.thingsboard.server.common.data.query.EntityKeyType; |
40 | -import org.thingsboard.server.common.data.query.FilterPredicateType; | ||
41 | -import org.thingsboard.server.common.data.query.KeyFilter; | ||
42 | -import org.thingsboard.server.common.data.query.KeyFilterPredicate; | ||
43 | -import org.thingsboard.server.common.data.query.SimpleKeyFilterPredicate; | ||
44 | import org.thingsboard.server.common.data.query.TsValue; | 28 | import org.thingsboard.server.common.data.query.TsValue; |
45 | import org.thingsboard.server.dao.attributes.AttributesService; | 29 | import org.thingsboard.server.dao.attributes.AttributesService; |
46 | import org.thingsboard.server.dao.entity.EntityService; | 30 | import org.thingsboard.server.dao.entity.EntityService; |
@@ -52,140 +36,25 @@ import java.util.ArrayList; | @@ -52,140 +36,25 @@ import java.util.ArrayList; | ||
52 | import java.util.Arrays; | 36 | import java.util.Arrays; |
53 | import java.util.Collections; | 37 | import java.util.Collections; |
54 | import java.util.HashMap; | 38 | import java.util.HashMap; |
55 | -import java.util.HashSet; | ||
56 | import java.util.List; | 39 | import java.util.List; |
57 | import java.util.Map; | 40 | import java.util.Map; |
58 | -import java.util.Optional; | ||
59 | -import java.util.Set; | ||
60 | import java.util.concurrent.ConcurrentHashMap; | 41 | import java.util.concurrent.ConcurrentHashMap; |
61 | -import java.util.concurrent.ExecutionException; | ||
62 | -import java.util.concurrent.ScheduledFuture; | ||
63 | import java.util.function.Function; | 42 | import java.util.function.Function; |
64 | import java.util.stream.Collectors; | 43 | import java.util.stream.Collectors; |
65 | 44 | ||
66 | @Slf4j | 45 | @Slf4j |
67 | -@Data | ||
68 | -public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends EntityDataPageLink>> { | 46 | +public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends EntityDataPageLink>> extends TbAbstractSubCtx<T> { |
69 | 47 | ||
70 | - protected final String serviceId; | ||
71 | - protected final SubscriptionServiceStatistics stats; | ||
72 | - protected final TelemetryWebSocketService wsService; | ||
73 | - protected final EntityService entityService; | ||
74 | - protected final TbLocalSubscriptionService localSubscriptionService; | ||
75 | - protected final AttributesService attributesService; | ||
76 | - protected final TelemetryWebSocketSessionRef sessionRef; | ||
77 | - protected final int cmdId; | ||
78 | protected final Map<Integer, EntityId> subToEntityIdMap; | 48 | protected final Map<Integer, EntityId> subToEntityIdMap; |
79 | - protected final Set<Integer> subToDynamicValueKeySet; | ||
80 | - @Getter | ||
81 | - protected final Map<DynamicValueKey, List<DynamicValue>> dynamicValues; | ||
82 | @Getter | 49 | @Getter |
83 | protected PageData<EntityData> data; | 50 | protected PageData<EntityData> data; |
84 | - @Getter | ||
85 | - @Setter | ||
86 | - protected T query; | ||
87 | - @Setter | ||
88 | - protected volatile ScheduledFuture<?> refreshTask; | ||
89 | 51 | ||
90 | public TbAbstractDataSubCtx(String serviceId, TelemetryWebSocketService wsService, | 52 | public TbAbstractDataSubCtx(String serviceId, TelemetryWebSocketService wsService, |
91 | EntityService entityService, TbLocalSubscriptionService localSubscriptionService, | 53 | EntityService entityService, TbLocalSubscriptionService localSubscriptionService, |
92 | AttributesService attributesService, SubscriptionServiceStatistics stats, | 54 | AttributesService attributesService, SubscriptionServiceStatistics stats, |
93 | TelemetryWebSocketSessionRef sessionRef, int cmdId) { | 55 | TelemetryWebSocketSessionRef sessionRef, int cmdId) { |
94 | - this.serviceId = serviceId; | ||
95 | - this.wsService = wsService; | ||
96 | - this.entityService = entityService; | ||
97 | - this.localSubscriptionService = localSubscriptionService; | ||
98 | - this.attributesService = attributesService; | ||
99 | - this.stats = stats; | ||
100 | - this.sessionRef = sessionRef; | ||
101 | - this.cmdId = cmdId; | 56 | + super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId); |
102 | this.subToEntityIdMap = new ConcurrentHashMap<>(); | 57 | this.subToEntityIdMap = new ConcurrentHashMap<>(); |
103 | - this.subToDynamicValueKeySet = ConcurrentHashMap.newKeySet(); | ||
104 | - this.dynamicValues = new ConcurrentHashMap<>(); | ||
105 | - } | ||
106 | - | ||
107 | - public void setAndResolveQuery(T query) { | ||
108 | - dynamicValues.clear(); | ||
109 | - this.query = query; | ||
110 | - if (query != null && query.getKeyFilters() != null) { | ||
111 | - for (KeyFilter filter : query.getKeyFilters()) { | ||
112 | - registerDynamicValues(filter.getPredicate()); | ||
113 | - } | ||
114 | - } | ||
115 | - resolve(getTenantId(), getCustomerId(), getUserId()); | ||
116 | - } | ||
117 | - | ||
118 | - public void resolve(TenantId tenantId, CustomerId customerId, UserId userId) { | ||
119 | - List<ListenableFuture<DynamicValueKeySub>> futures = new ArrayList<>(); | ||
120 | - for (DynamicValueKey key : dynamicValues.keySet()) { | ||
121 | - switch (key.getSourceType()) { | ||
122 | - case CURRENT_TENANT: | ||
123 | - futures.add(resolveEntityValue(tenantId, tenantId, key)); | ||
124 | - break; | ||
125 | - case CURRENT_CUSTOMER: | ||
126 | - if (customerId != null && !customerId.isNullUid()) { | ||
127 | - futures.add(resolveEntityValue(tenantId, customerId, key)); | ||
128 | - } | ||
129 | - break; | ||
130 | - case CURRENT_USER: | ||
131 | - if (userId != null && !userId.isNullUid()) { | ||
132 | - futures.add(resolveEntityValue(tenantId, userId, key)); | ||
133 | - } | ||
134 | - break; | ||
135 | - } | ||
136 | - } | ||
137 | - try { | ||
138 | - Map<EntityId, Map<String, DynamicValueKeySub>> tmpSubMap = new HashMap<>(); | ||
139 | - for (DynamicValueKeySub sub : Futures.successfulAsList(futures).get()) { | ||
140 | - tmpSubMap.computeIfAbsent(sub.getEntityId(), tmp -> new HashMap<>()).put(sub.getKey().getSourceAttribute(), sub); | ||
141 | - } | ||
142 | - for (EntityId entityId : tmpSubMap.keySet()) { | ||
143 | - Map<String, Long> keyStates = new HashMap<>(); | ||
144 | - Map<String, DynamicValueKeySub> dynamicValueKeySubMap = tmpSubMap.get(entityId); | ||
145 | - dynamicValueKeySubMap.forEach((k, v) -> keyStates.put(k, v.getLastUpdateTs())); | ||
146 | - int subIdx = sessionRef.getSessionSubIdSeq().incrementAndGet(); | ||
147 | - TbAttributeSubscription sub = TbAttributeSubscription.builder() | ||
148 | - .serviceId(serviceId) | ||
149 | - .sessionId(sessionRef.getSessionId()) | ||
150 | - .subscriptionId(subIdx) | ||
151 | - .tenantId(sessionRef.getSecurityCtx().getTenantId()) | ||
152 | - .entityId(entityId) | ||
153 | - .updateConsumer((s, subscriptionUpdate) -> dynamicValueSubUpdate(s, subscriptionUpdate, dynamicValueKeySubMap)) | ||
154 | - .allKeys(false) | ||
155 | - .keyStates(keyStates) | ||
156 | - .scope(TbAttributeSubscriptionScope.SERVER_SCOPE) | ||
157 | - .build(); | ||
158 | - subToDynamicValueKeySet.add(subIdx); | ||
159 | - localSubscriptionService.addSubscription(sub); | ||
160 | - } | ||
161 | - } catch (InterruptedException | ExecutionException e) { | ||
162 | - log.info("[{}][{}][{}] Failed to resolve dynamic values: {}", tenantId, customerId, userId, dynamicValues.keySet()); | ||
163 | - } | ||
164 | - | ||
165 | - } | ||
166 | - | ||
167 | - private void dynamicValueSubUpdate(String sessionId, TelemetrySubscriptionUpdate subscriptionUpdate, | ||
168 | - Map<String, DynamicValueKeySub> dynamicValueKeySubMap) { | ||
169 | - Map<String, TsValue> latestUpdate = new HashMap<>(); | ||
170 | - subscriptionUpdate.getData().forEach((k, v) -> { | ||
171 | - Object[] data = (Object[]) v.get(0); | ||
172 | - latestUpdate.put(k, new TsValue((Long) data[0], (String) data[1])); | ||
173 | - }); | ||
174 | - | ||
175 | - boolean invalidateFilter = false; | ||
176 | - for (Map.Entry<String, TsValue> entry : latestUpdate.entrySet()) { | ||
177 | - String k = entry.getKey(); | ||
178 | - TsValue tsValue = entry.getValue(); | ||
179 | - DynamicValueKeySub sub = dynamicValueKeySubMap.get(k); | ||
180 | - if (sub.updateValue(tsValue)) { | ||
181 | - invalidateFilter = true; | ||
182 | - updateDynamicValuesByKey(sub, tsValue); | ||
183 | - } | ||
184 | - } | ||
185 | - | ||
186 | - if (invalidateFilter) { | ||
187 | - update(); | ||
188 | - } | ||
189 | } | 58 | } |
190 | 59 | ||
191 | public void fetchData() { | 60 | public void fetchData() { |
@@ -231,104 +100,10 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends | @@ -231,104 +100,10 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends | ||
231 | return data.getData(); | 100 | return data.getData(); |
232 | } | 101 | } |
233 | 102 | ||
234 | - @Data | ||
235 | - private static class DynamicValueKeySub { | ||
236 | - private final DynamicValueKey key; | ||
237 | - private final EntityId entityId; | ||
238 | - private long lastUpdateTs; | ||
239 | - private String lastUpdateValue; | ||
240 | - | ||
241 | - boolean updateValue(TsValue value) { | ||
242 | - if (value.getTs() > lastUpdateTs && (lastUpdateValue == null || !lastUpdateValue.equals(value.getValue()))) { | ||
243 | - this.lastUpdateTs = value.getTs(); | ||
244 | - this.lastUpdateValue = value.getValue(); | ||
245 | - return true; | ||
246 | - } else { | ||
247 | - return false; | ||
248 | - } | ||
249 | - } | ||
250 | - } | ||
251 | - | ||
252 | - private ListenableFuture<DynamicValueKeySub> resolveEntityValue(TenantId tenantId, EntityId entityId, DynamicValueKey key) { | ||
253 | - ListenableFuture<Optional<AttributeKvEntry>> entry = attributesService.find(tenantId, entityId, | ||
254 | - TbAttributeSubscriptionScope.SERVER_SCOPE.name(), key.getSourceAttribute()); | ||
255 | - return Futures.transform(entry, attributeOpt -> { | ||
256 | - DynamicValueKeySub sub = new DynamicValueKeySub(key, entityId); | ||
257 | - if (attributeOpt.isPresent()) { | ||
258 | - AttributeKvEntry attribute = attributeOpt.get(); | ||
259 | - sub.setLastUpdateTs(attribute.getLastUpdateTs()); | ||
260 | - sub.setLastUpdateValue(attribute.getValueAsString()); | ||
261 | - updateDynamicValuesByKey(sub, new TsValue(attribute.getLastUpdateTs(), attribute.getValueAsString())); | ||
262 | - } | ||
263 | - return sub; | ||
264 | - }, MoreExecutors.directExecutor()); | ||
265 | - } | ||
266 | - | ||
267 | - @SuppressWarnings("unchecked") | ||
268 | - private void updateDynamicValuesByKey(DynamicValueKeySub sub, TsValue tsValue) { | ||
269 | - DynamicValueKey dvk = sub.getKey(); | ||
270 | - switch (dvk.getPredicateType()) { | ||
271 | - case STRING: | ||
272 | - dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(tsValue.getValue())); | ||
273 | - break; | ||
274 | - case NUMERIC: | ||
275 | - try { | ||
276 | - Double dValue = Double.parseDouble(tsValue.getValue()); | ||
277 | - dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(dValue)); | ||
278 | - } catch (NumberFormatException e) { | ||
279 | - dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(null)); | ||
280 | - } | ||
281 | - break; | ||
282 | - case BOOLEAN: | ||
283 | - Boolean bValue = Boolean.parseBoolean(tsValue.getValue()); | ||
284 | - dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(bValue)); | ||
285 | - break; | ||
286 | - } | ||
287 | - } | ||
288 | - | ||
289 | - @SuppressWarnings("unchecked") | ||
290 | - private void registerDynamicValues(KeyFilterPredicate predicate) { | ||
291 | - switch (predicate.getType()) { | ||
292 | - case STRING: | ||
293 | - case NUMERIC: | ||
294 | - case BOOLEAN: | ||
295 | - Optional<DynamicValue> value = getDynamicValueFromSimplePredicate((SimpleKeyFilterPredicate) predicate); | ||
296 | - if (value.isPresent()) { | ||
297 | - DynamicValue dynamicValue = value.get(); | ||
298 | - DynamicValueKey key = new DynamicValueKey( | ||
299 | - predicate.getType(), | ||
300 | - dynamicValue.getSourceType(), | ||
301 | - dynamicValue.getSourceAttribute()); | ||
302 | - dynamicValues.computeIfAbsent(key, tmp -> new ArrayList<>()).add(dynamicValue); | ||
303 | - } | ||
304 | - break; | ||
305 | - case COMPLEX: | ||
306 | - ((ComplexFilterPredicate) predicate).getPredicates().forEach(this::registerDynamicValues); | ||
307 | - } | ||
308 | - } | ||
309 | - | ||
310 | - private Optional<DynamicValue<T>> getDynamicValueFromSimplePredicate(SimpleKeyFilterPredicate<T> predicate) { | ||
311 | - if (predicate.getValue().getUserValue() == null) { | ||
312 | - return Optional.ofNullable(predicate.getValue().getDynamicValue()); | ||
313 | - } else { | ||
314 | - return Optional.empty(); | ||
315 | - } | ||
316 | - } | ||
317 | - | ||
318 | - public String getSessionId() { | ||
319 | - return sessionRef.getSessionId(); | ||
320 | - } | ||
321 | - | ||
322 | - public TenantId getTenantId() { | ||
323 | - return sessionRef.getSecurityCtx().getTenantId(); | ||
324 | - } | ||
325 | - | ||
326 | - public CustomerId getCustomerId() { | ||
327 | - return sessionRef.getSecurityCtx().getCustomerId(); | ||
328 | - } | ||
329 | - | ||
330 | - public UserId getUserId() { | ||
331 | - return sessionRef.getSecurityCtx().getId(); | 103 | + @Override |
104 | + public void clearSubscriptions() { | ||
105 | + clearEntitySubscriptions(); | ||
106 | + super.clearSubscriptions(); | ||
332 | } | 107 | } |
333 | 108 | ||
334 | public void clearEntitySubscriptions() { | 109 | public void clearEntitySubscriptions() { |
@@ -340,26 +115,6 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends | @@ -340,26 +115,6 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends | ||
340 | } | 115 | } |
341 | } | 116 | } |
342 | 117 | ||
343 | - public void clearDynamicValueSubscriptions() { | ||
344 | - if (subToDynamicValueKeySet != null) { | ||
345 | - for (Integer subId : subToDynamicValueKeySet) { | ||
346 | - localSubscriptionService.cancelSubscription(sessionRef.getSessionId(), subId); | ||
347 | - } | ||
348 | - subToDynamicValueKeySet.clear(); | ||
349 | - } | ||
350 | - } | ||
351 | - | ||
352 | - public void setRefreshTask(ScheduledFuture<?> task) { | ||
353 | - this.refreshTask = task; | ||
354 | - } | ||
355 | - | ||
356 | - public void cancelTasks() { | ||
357 | - if (this.refreshTask != null) { | ||
358 | - log.trace("[{}][{}] Canceling old refresh task", sessionRef.getSessionId(), cmdId); | ||
359 | - this.refreshTask.cancel(true); | ||
360 | - } | ||
361 | - } | ||
362 | - | ||
363 | public void createSubscriptions(List<EntityKey> keys, boolean resultToLatestValues) { | 118 | public void createSubscriptions(List<EntityKey> keys, boolean resultToLatestValues) { |
364 | Map<EntityKeyType, List<EntityKey>> keysByType = getEntityKeyByTypeMap(keys); | 119 | Map<EntityKeyType, List<EntityKey>> keysByType = getEntityKeyByTypeMap(keys); |
365 | for (EntityData entityData : data.getData()) { | 120 | for (EntityData entityData : data.getData()) { |
@@ -459,14 +214,4 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends | @@ -459,14 +214,4 @@ public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery<? extends | ||
459 | 214 | ||
460 | abstract void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate subscriptionUpdate, EntityKeyType keyType, boolean resultToLatestValues); | 215 | abstract void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate subscriptionUpdate, EntityKeyType keyType, boolean resultToLatestValues); |
461 | 216 | ||
462 | - @Data | ||
463 | - private static class DynamicValueKey { | ||
464 | - @Getter | ||
465 | - private final FilterPredicateType predicateType; | ||
466 | - @Getter | ||
467 | - private final DynamicValueSourceType sourceType; | ||
468 | - @Getter | ||
469 | - private final String sourceAttribute; | ||
470 | - } | ||
471 | - | ||
472 | } | 217 | } |
application/src/main/java/org/thingsboard/server/service/subscription/TbAbstractSubCtx.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.subscription; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.Futures; | ||
19 | +import com.google.common.util.concurrent.ListenableFuture; | ||
20 | +import com.google.common.util.concurrent.MoreExecutors; | ||
21 | +import lombok.Data; | ||
22 | +import lombok.Getter; | ||
23 | +import lombok.Setter; | ||
24 | +import lombok.extern.slf4j.Slf4j; | ||
25 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
26 | +import org.thingsboard.server.common.data.id.EntityId; | ||
27 | +import org.thingsboard.server.common.data.id.TenantId; | ||
28 | +import org.thingsboard.server.common.data.id.UserId; | ||
29 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
30 | +import org.thingsboard.server.common.data.query.ComplexFilterPredicate; | ||
31 | +import org.thingsboard.server.common.data.query.DynamicValue; | ||
32 | +import org.thingsboard.server.common.data.query.DynamicValueSourceType; | ||
33 | +import org.thingsboard.server.common.data.query.EntityCountQuery; | ||
34 | +import org.thingsboard.server.common.data.query.EntityKeyType; | ||
35 | +import org.thingsboard.server.common.data.query.FilterPredicateType; | ||
36 | +import org.thingsboard.server.common.data.query.KeyFilter; | ||
37 | +import org.thingsboard.server.common.data.query.KeyFilterPredicate; | ||
38 | +import org.thingsboard.server.common.data.query.SimpleKeyFilterPredicate; | ||
39 | +import org.thingsboard.server.common.data.query.TsValue; | ||
40 | +import org.thingsboard.server.dao.attributes.AttributesService; | ||
41 | +import org.thingsboard.server.dao.entity.EntityService; | ||
42 | +import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; | ||
43 | +import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; | ||
44 | +import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; | ||
45 | + | ||
46 | +import java.util.ArrayList; | ||
47 | +import java.util.HashMap; | ||
48 | +import java.util.List; | ||
49 | +import java.util.Map; | ||
50 | +import java.util.Optional; | ||
51 | +import java.util.Set; | ||
52 | +import java.util.concurrent.ConcurrentHashMap; | ||
53 | +import java.util.concurrent.ExecutionException; | ||
54 | +import java.util.concurrent.ScheduledFuture; | ||
55 | + | ||
56 | +@Slf4j | ||
57 | +@Data | ||
58 | +public abstract class TbAbstractSubCtx<T extends EntityCountQuery> { | ||
59 | + | ||
60 | + protected final String serviceId; | ||
61 | + protected final SubscriptionServiceStatistics stats; | ||
62 | + protected final TelemetryWebSocketService wsService; | ||
63 | + protected final EntityService entityService; | ||
64 | + protected final TbLocalSubscriptionService localSubscriptionService; | ||
65 | + protected final AttributesService attributesService; | ||
66 | + protected final TelemetryWebSocketSessionRef sessionRef; | ||
67 | + protected final int cmdId; | ||
68 | + protected final Set<Integer> subToDynamicValueKeySet; | ||
69 | + @Getter | ||
70 | + protected final Map<DynamicValueKey, List<DynamicValue>> dynamicValues; | ||
71 | + @Getter | ||
72 | + @Setter | ||
73 | + protected T query; | ||
74 | + @Setter | ||
75 | + protected volatile ScheduledFuture<?> refreshTask; | ||
76 | + | ||
77 | + public TbAbstractSubCtx(String serviceId, TelemetryWebSocketService wsService, | ||
78 | + EntityService entityService, TbLocalSubscriptionService localSubscriptionService, | ||
79 | + AttributesService attributesService, SubscriptionServiceStatistics stats, | ||
80 | + TelemetryWebSocketSessionRef sessionRef, int cmdId) { | ||
81 | + this.serviceId = serviceId; | ||
82 | + this.wsService = wsService; | ||
83 | + this.entityService = entityService; | ||
84 | + this.localSubscriptionService = localSubscriptionService; | ||
85 | + this.attributesService = attributesService; | ||
86 | + this.stats = stats; | ||
87 | + this.sessionRef = sessionRef; | ||
88 | + this.cmdId = cmdId; | ||
89 | + this.subToDynamicValueKeySet = ConcurrentHashMap.newKeySet(); | ||
90 | + this.dynamicValues = new ConcurrentHashMap<>(); | ||
91 | + } | ||
92 | + | ||
93 | + public void setAndResolveQuery(T query) { | ||
94 | + dynamicValues.clear(); | ||
95 | + this.query = query; | ||
96 | + if (query != null && query.getKeyFilters() != null) { | ||
97 | + for (KeyFilter filter : query.getKeyFilters()) { | ||
98 | + registerDynamicValues(filter.getPredicate()); | ||
99 | + } | ||
100 | + } | ||
101 | + resolve(getTenantId(), getCustomerId(), getUserId()); | ||
102 | + } | ||
103 | + | ||
104 | + public void resolve(TenantId tenantId, CustomerId customerId, UserId userId) { | ||
105 | + List<ListenableFuture<DynamicValueKeySub>> futures = new ArrayList<>(); | ||
106 | + for (DynamicValueKey key : dynamicValues.keySet()) { | ||
107 | + switch (key.getSourceType()) { | ||
108 | + case CURRENT_TENANT: | ||
109 | + futures.add(resolveEntityValue(tenantId, tenantId, key)); | ||
110 | + break; | ||
111 | + case CURRENT_CUSTOMER: | ||
112 | + if (customerId != null && !customerId.isNullUid()) { | ||
113 | + futures.add(resolveEntityValue(tenantId, customerId, key)); | ||
114 | + } | ||
115 | + break; | ||
116 | + case CURRENT_USER: | ||
117 | + if (userId != null && !userId.isNullUid()) { | ||
118 | + futures.add(resolveEntityValue(tenantId, userId, key)); | ||
119 | + } | ||
120 | + break; | ||
121 | + } | ||
122 | + } | ||
123 | + try { | ||
124 | + Map<EntityId, Map<String, DynamicValueKeySub>> tmpSubMap = new HashMap<>(); | ||
125 | + for (DynamicValueKeySub sub : Futures.successfulAsList(futures).get()) { | ||
126 | + tmpSubMap.computeIfAbsent(sub.getEntityId(), tmp -> new HashMap<>()).put(sub.getKey().getSourceAttribute(), sub); | ||
127 | + } | ||
128 | + for (EntityId entityId : tmpSubMap.keySet()) { | ||
129 | + Map<String, Long> keyStates = new HashMap<>(); | ||
130 | + Map<String, DynamicValueKeySub> dynamicValueKeySubMap = tmpSubMap.get(entityId); | ||
131 | + dynamicValueKeySubMap.forEach((k, v) -> keyStates.put(k, v.getLastUpdateTs())); | ||
132 | + int subIdx = sessionRef.getSessionSubIdSeq().incrementAndGet(); | ||
133 | + TbAttributeSubscription sub = TbAttributeSubscription.builder() | ||
134 | + .serviceId(serviceId) | ||
135 | + .sessionId(sessionRef.getSessionId()) | ||
136 | + .subscriptionId(subIdx) | ||
137 | + .tenantId(sessionRef.getSecurityCtx().getTenantId()) | ||
138 | + .entityId(entityId) | ||
139 | + .updateConsumer((s, subscriptionUpdate) -> dynamicValueSubUpdate(s, subscriptionUpdate, dynamicValueKeySubMap)) | ||
140 | + .allKeys(false) | ||
141 | + .keyStates(keyStates) | ||
142 | + .scope(TbAttributeSubscriptionScope.SERVER_SCOPE) | ||
143 | + .build(); | ||
144 | + subToDynamicValueKeySet.add(subIdx); | ||
145 | + localSubscriptionService.addSubscription(sub); | ||
146 | + } | ||
147 | + } catch (InterruptedException | ExecutionException e) { | ||
148 | + log.info("[{}][{}][{}] Failed to resolve dynamic values: {}", tenantId, customerId, userId, dynamicValues.keySet()); | ||
149 | + } | ||
150 | + | ||
151 | + } | ||
152 | + | ||
153 | + private void dynamicValueSubUpdate(String sessionId, TelemetrySubscriptionUpdate subscriptionUpdate, | ||
154 | + Map<String, DynamicValueKeySub> dynamicValueKeySubMap) { | ||
155 | + Map<String, TsValue> latestUpdate = new HashMap<>(); | ||
156 | + subscriptionUpdate.getData().forEach((k, v) -> { | ||
157 | + Object[] data = (Object[]) v.get(0); | ||
158 | + latestUpdate.put(k, new TsValue((Long) data[0], (String) data[1])); | ||
159 | + }); | ||
160 | + | ||
161 | + boolean invalidateFilter = false; | ||
162 | + for (Map.Entry<String, TsValue> entry : latestUpdate.entrySet()) { | ||
163 | + String k = entry.getKey(); | ||
164 | + TsValue tsValue = entry.getValue(); | ||
165 | + DynamicValueKeySub sub = dynamicValueKeySubMap.get(k); | ||
166 | + if (sub.updateValue(tsValue)) { | ||
167 | + invalidateFilter = true; | ||
168 | + updateDynamicValuesByKey(sub, tsValue); | ||
169 | + } | ||
170 | + } | ||
171 | + | ||
172 | + if (invalidateFilter) { | ||
173 | + update(); | ||
174 | + } | ||
175 | + } | ||
176 | + | ||
177 | + public abstract void fetchData(); | ||
178 | + | ||
179 | + protected abstract void update(); | ||
180 | + | ||
181 | + public void clearSubscriptions() { | ||
182 | + clearDynamicValueSubscriptions(); | ||
183 | + } | ||
184 | + | ||
185 | + @Data | ||
186 | + private static class DynamicValueKeySub { | ||
187 | + private final DynamicValueKey key; | ||
188 | + private final EntityId entityId; | ||
189 | + private long lastUpdateTs; | ||
190 | + private String lastUpdateValue; | ||
191 | + | ||
192 | + boolean updateValue(TsValue value) { | ||
193 | + if (value.getTs() > lastUpdateTs && (lastUpdateValue == null || !lastUpdateValue.equals(value.getValue()))) { | ||
194 | + this.lastUpdateTs = value.getTs(); | ||
195 | + this.lastUpdateValue = value.getValue(); | ||
196 | + return true; | ||
197 | + } else { | ||
198 | + return false; | ||
199 | + } | ||
200 | + } | ||
201 | + } | ||
202 | + | ||
203 | + private ListenableFuture<DynamicValueKeySub> resolveEntityValue(TenantId tenantId, EntityId entityId, DynamicValueKey key) { | ||
204 | + ListenableFuture<Optional<AttributeKvEntry>> entry = attributesService.find(tenantId, entityId, | ||
205 | + TbAttributeSubscriptionScope.SERVER_SCOPE.name(), key.getSourceAttribute()); | ||
206 | + return Futures.transform(entry, attributeOpt -> { | ||
207 | + DynamicValueKeySub sub = new DynamicValueKeySub(key, entityId); | ||
208 | + if (attributeOpt.isPresent()) { | ||
209 | + AttributeKvEntry attribute = attributeOpt.get(); | ||
210 | + sub.setLastUpdateTs(attribute.getLastUpdateTs()); | ||
211 | + sub.setLastUpdateValue(attribute.getValueAsString()); | ||
212 | + updateDynamicValuesByKey(sub, new TsValue(attribute.getLastUpdateTs(), attribute.getValueAsString())); | ||
213 | + } | ||
214 | + return sub; | ||
215 | + }, MoreExecutors.directExecutor()); | ||
216 | + } | ||
217 | + | ||
218 | + @SuppressWarnings("unchecked") | ||
219 | + protected void updateDynamicValuesByKey(DynamicValueKeySub sub, TsValue tsValue) { | ||
220 | + DynamicValueKey dvk = sub.getKey(); | ||
221 | + switch (dvk.getPredicateType()) { | ||
222 | + case STRING: | ||
223 | + dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(tsValue.getValue())); | ||
224 | + break; | ||
225 | + case NUMERIC: | ||
226 | + try { | ||
227 | + Double dValue = Double.parseDouble(tsValue.getValue()); | ||
228 | + dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(dValue)); | ||
229 | + } catch (NumberFormatException e) { | ||
230 | + dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(null)); | ||
231 | + } | ||
232 | + break; | ||
233 | + case BOOLEAN: | ||
234 | + Boolean bValue = Boolean.parseBoolean(tsValue.getValue()); | ||
235 | + dynamicValues.get(dvk).forEach(dynamicValue -> dynamicValue.setResolvedValue(bValue)); | ||
236 | + break; | ||
237 | + } | ||
238 | + } | ||
239 | + | ||
240 | + @SuppressWarnings("unchecked") | ||
241 | + private void registerDynamicValues(KeyFilterPredicate predicate) { | ||
242 | + switch (predicate.getType()) { | ||
243 | + case STRING: | ||
244 | + case NUMERIC: | ||
245 | + case BOOLEAN: | ||
246 | + Optional<DynamicValue> value = getDynamicValueFromSimplePredicate((SimpleKeyFilterPredicate) predicate); | ||
247 | + if (value.isPresent()) { | ||
248 | + DynamicValue dynamicValue = value.get(); | ||
249 | + DynamicValueKey key = new DynamicValueKey( | ||
250 | + predicate.getType(), | ||
251 | + dynamicValue.getSourceType(), | ||
252 | + dynamicValue.getSourceAttribute()); | ||
253 | + dynamicValues.computeIfAbsent(key, tmp -> new ArrayList<>()).add(dynamicValue); | ||
254 | + } | ||
255 | + break; | ||
256 | + case COMPLEX: | ||
257 | + ((ComplexFilterPredicate) predicate).getPredicates().forEach(this::registerDynamicValues); | ||
258 | + } | ||
259 | + } | ||
260 | + | ||
261 | + private Optional<DynamicValue<T>> getDynamicValueFromSimplePredicate(SimpleKeyFilterPredicate<T> predicate) { | ||
262 | + if (predicate.getValue().getUserValue() == null) { | ||
263 | + return Optional.ofNullable(predicate.getValue().getDynamicValue()); | ||
264 | + } else { | ||
265 | + return Optional.empty(); | ||
266 | + } | ||
267 | + } | ||
268 | + | ||
269 | + public String getSessionId() { | ||
270 | + return sessionRef.getSessionId(); | ||
271 | + } | ||
272 | + | ||
273 | + public TenantId getTenantId() { | ||
274 | + return sessionRef.getSecurityCtx().getTenantId(); | ||
275 | + } | ||
276 | + | ||
277 | + public CustomerId getCustomerId() { | ||
278 | + return sessionRef.getSecurityCtx().getCustomerId(); | ||
279 | + } | ||
280 | + | ||
281 | + public UserId getUserId() { | ||
282 | + return sessionRef.getSecurityCtx().getId(); | ||
283 | + } | ||
284 | + | ||
285 | + protected void clearDynamicValueSubscriptions() { | ||
286 | + if (subToDynamicValueKeySet != null) { | ||
287 | + for (Integer subId : subToDynamicValueKeySet) { | ||
288 | + localSubscriptionService.cancelSubscription(sessionRef.getSessionId(), subId); | ||
289 | + } | ||
290 | + subToDynamicValueKeySet.clear(); | ||
291 | + } | ||
292 | + } | ||
293 | + | ||
294 | + public void setRefreshTask(ScheduledFuture<?> task) { | ||
295 | + this.refreshTask = task; | ||
296 | + } | ||
297 | + | ||
298 | + public void cancelTasks() { | ||
299 | + if (this.refreshTask != null) { | ||
300 | + log.trace("[{}][{}] Canceling old refresh task", sessionRef.getSessionId(), cmdId); | ||
301 | + this.refreshTask.cancel(true); | ||
302 | + } | ||
303 | + } | ||
304 | + | ||
305 | + @Data | ||
306 | + public static class DynamicValueKey { | ||
307 | + @Getter | ||
308 | + private final FilterPredicateType predicateType; | ||
309 | + @Getter | ||
310 | + private final DynamicValueSourceType sourceType; | ||
311 | + @Getter | ||
312 | + private final String sourceAttribute; | ||
313 | + } | ||
314 | + | ||
315 | +} |
@@ -90,8 +90,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> { | @@ -90,8 +90,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> { | ||
90 | AlarmDataUpdate update; | 90 | AlarmDataUpdate update; |
91 | if (!entitiesMap.isEmpty()) { | 91 | if (!entitiesMap.isEmpty()) { |
92 | long start = System.currentTimeMillis(); | 92 | long start = System.currentTimeMillis(); |
93 | - PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(getTenantId(), getCustomerId(), | ||
94 | - query, getOrderedEntityIds()); | 93 | + PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(getTenantId(), getCustomerId(), query, getOrderedEntityIds()); |
95 | long end = System.currentTimeMillis(); | 94 | long end = System.currentTimeMillis(); |
96 | stats.getAlarmQueryInvocationCnt().incrementAndGet(); | 95 | stats.getAlarmQueryInvocationCnt().incrementAndGet(); |
97 | stats.getAlarmQueryTimeSpent().addAndGet(end - start); | 96 | stats.getAlarmQueryTimeSpent().addAndGet(end - start); |
application/src/main/java/org/thingsboard/server/service/subscription/TbEntityCountSubCtx.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.subscription; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.thingsboard.server.common.data.query.EntityCountQuery; | ||
20 | +import org.thingsboard.server.common.data.query.EntityKeyType; | ||
21 | +import org.thingsboard.server.dao.attributes.AttributesService; | ||
22 | +import org.thingsboard.server.dao.entity.EntityService; | ||
23 | +import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; | ||
24 | +import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; | ||
25 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate; | ||
26 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; | ||
27 | +import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; | ||
28 | + | ||
29 | +@Slf4j | ||
30 | +public class TbEntityCountSubCtx extends TbAbstractSubCtx<EntityCountQuery> { | ||
31 | + | ||
32 | + private volatile int result; | ||
33 | + | ||
34 | + public TbEntityCountSubCtx(String serviceId, TelemetryWebSocketService wsService, EntityService entityService, | ||
35 | + TbLocalSubscriptionService localSubscriptionService, AttributesService attributesService, | ||
36 | + SubscriptionServiceStatistics stats, TelemetryWebSocketSessionRef sessionRef, int cmdId) { | ||
37 | + super(serviceId, wsService, entityService, localSubscriptionService, attributesService, stats, sessionRef, cmdId); | ||
38 | + } | ||
39 | + | ||
40 | + @Override | ||
41 | + public void fetchData() { | ||
42 | + result = (int) entityService.countEntitiesByQuery(getTenantId(), getCustomerId(), query); | ||
43 | + wsService.sendWsMsg(sessionRef.getSessionId(), new EntityCountUpdate(cmdId, result)); | ||
44 | + } | ||
45 | + | ||
46 | + @Override | ||
47 | + protected void update() { | ||
48 | + int newCount = (int) entityService.countEntitiesByQuery(getTenantId(), getCustomerId(), query); | ||
49 | + if (newCount != result) { | ||
50 | + result = newCount; | ||
51 | + wsService.sendWsMsg(sessionRef.getSessionId(), new EntityCountUpdate(cmdId, result)); | ||
52 | + } | ||
53 | + } | ||
54 | + | ||
55 | +} |
@@ -17,6 +17,7 @@ package org.thingsboard.server.service.subscription; | @@ -17,6 +17,7 @@ package org.thingsboard.server.service.subscription; | ||
17 | 17 | ||
18 | import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; | 18 | import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; |
19 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; | 19 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; |
20 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; | ||
20 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; | 21 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; |
21 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; | 22 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; |
22 | import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; | 23 | import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; |
@@ -25,6 +26,8 @@ public interface TbEntityDataSubscriptionService { | @@ -25,6 +26,8 @@ public interface TbEntityDataSubscriptionService { | ||
25 | 26 | ||
26 | void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityDataCmd cmd); | 27 | void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityDataCmd cmd); |
27 | 28 | ||
29 | + void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityCountCmd cmd); | ||
30 | + | ||
28 | void handleCmd(TelemetryWebSocketSessionRef sessionId, AlarmDataCmd cmd); | 31 | void handleCmd(TelemetryWebSocketSessionRef sessionId, AlarmDataCmd cmd); |
29 | 32 | ||
30 | void cancelSubscription(String sessionId, UnsubscribeCmd subscriptionId); | 33 | void cancelSubscription(String sessionId, UnsubscribeCmd subscriptionId); |
@@ -51,22 +51,22 @@ import org.thingsboard.server.service.security.ValidationResult; | @@ -51,22 +51,22 @@ import org.thingsboard.server.service.security.ValidationResult; | ||
51 | import org.thingsboard.server.service.security.ValidationResultCode; | 51 | import org.thingsboard.server.service.security.ValidationResultCode; |
52 | import org.thingsboard.server.service.security.model.UserPrincipal; | 52 | import org.thingsboard.server.service.security.model.UserPrincipal; |
53 | import org.thingsboard.server.service.security.permission.Operation; | 53 | import org.thingsboard.server.service.security.permission.Operation; |
54 | +import org.thingsboard.server.service.subscription.TbAttributeSubscription; | ||
55 | +import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; | ||
54 | import org.thingsboard.server.service.subscription.TbEntityDataSubscriptionService; | 56 | import org.thingsboard.server.service.subscription.TbEntityDataSubscriptionService; |
55 | import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; | 57 | import org.thingsboard.server.service.subscription.TbLocalSubscriptionService; |
56 | -import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; | ||
57 | -import org.thingsboard.server.service.subscription.TbAttributeSubscription; | ||
58 | import org.thingsboard.server.service.subscription.TbTimeseriesSubscription; | 58 | import org.thingsboard.server.service.subscription.TbTimeseriesSubscription; |
59 | +import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; | ||
59 | import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd; | 60 | import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd; |
60 | import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; | 61 | import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; |
61 | import org.thingsboard.server.service.telemetry.cmd.v1.SubscriptionCmd; | 62 | import org.thingsboard.server.service.telemetry.cmd.v1.SubscriptionCmd; |
62 | import org.thingsboard.server.service.telemetry.cmd.v1.TelemetryPluginCmd; | 63 | import org.thingsboard.server.service.telemetry.cmd.v1.TelemetryPluginCmd; |
63 | -import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; | ||
64 | import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; | 64 | import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; |
65 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; | 65 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; |
66 | -import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUnsubscribeCmd; | 66 | +import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate; |
67 | import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate; | 67 | import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate; |
68 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; | ||
68 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; | 69 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; |
69 | -import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; | ||
70 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; | 70 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; |
71 | import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; | 71 | import org.thingsboard.server.service.telemetry.cmd.v2.UnsubscribeCmd; |
72 | import org.thingsboard.server.service.telemetry.exception.UnauthorizedException; | 72 | import org.thingsboard.server.service.telemetry.exception.UnauthorizedException; |
@@ -216,12 +216,18 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | @@ -216,12 +216,18 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | ||
216 | if (cmdsWrapper.getAlarmDataCmds() != null) { | 216 | if (cmdsWrapper.getAlarmDataCmds() != null) { |
217 | cmdsWrapper.getAlarmDataCmds().forEach(cmd -> handleWsAlarmDataCmd(sessionRef, cmd)); | 217 | cmdsWrapper.getAlarmDataCmds().forEach(cmd -> handleWsAlarmDataCmd(sessionRef, cmd)); |
218 | } | 218 | } |
219 | + if (cmdsWrapper.getEntityCountCmds() != null) { | ||
220 | + cmdsWrapper.getEntityCountCmds().forEach(cmd -> handleWsEntityCountCmd(sessionRef, cmd)); | ||
221 | + } | ||
219 | if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) { | 222 | if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) { |
220 | cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); | 223 | cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); |
221 | } | 224 | } |
222 | if (cmdsWrapper.getAlarmDataUnsubscribeCmds() != null) { | 225 | if (cmdsWrapper.getAlarmDataUnsubscribeCmds() != null) { |
223 | cmdsWrapper.getAlarmDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); | 226 | cmdsWrapper.getAlarmDataUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); |
224 | } | 227 | } |
228 | + if (cmdsWrapper.getEntityCountUnsubscribeCmds() != null) { | ||
229 | + cmdsWrapper.getEntityCountUnsubscribeCmds().forEach(cmd -> handleWsDataUnsubscribeCmd(sessionRef, cmd)); | ||
230 | + } | ||
225 | } | 231 | } |
226 | } catch (IOException e) { | 232 | } catch (IOException e) { |
227 | log.warn("Failed to decode subscription cmd: {}", e.getMessage(), e); | 233 | log.warn("Failed to decode subscription cmd: {}", e.getMessage(), e); |
@@ -239,6 +245,16 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | @@ -239,6 +245,16 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | ||
239 | } | 245 | } |
240 | } | 246 | } |
241 | 247 | ||
248 | + private void handleWsEntityCountCmd(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) { | ||
249 | + String sessionId = sessionRef.getSessionId(); | ||
250 | + log.debug("[{}] Processing: {}", sessionId, cmd); | ||
251 | + | ||
252 | + if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId) | ||
253 | + && validateSubscriptionCmd(sessionRef, cmd)) { | ||
254 | + entityDataSubService.handleCmd(sessionRef, cmd); | ||
255 | + } | ||
256 | + } | ||
257 | + | ||
242 | private void handleWsAlarmDataCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { | 258 | private void handleWsAlarmDataCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { |
243 | String sessionId = sessionRef.getSessionId(); | 259 | String sessionId = sessionRef.getSessionId(); |
244 | log.debug("[{}] Processing: {}", sessionId, cmd); | 260 | log.debug("[{}] Processing: {}", sessionId, cmd); |
@@ -264,7 +280,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | @@ -264,7 +280,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | ||
264 | } | 280 | } |
265 | 281 | ||
266 | @Override | 282 | @Override |
267 | - public void sendWsMsg(String sessionId, DataUpdate update) { | 283 | + public void sendWsMsg(String sessionId, CmdUpdate update) { |
268 | sendWsMsg(sessionId, update.getCmdId(), update); | 284 | sendWsMsg(sessionId, update.getCmdId(), update); |
269 | } | 285 | } |
270 | 286 | ||
@@ -679,6 +695,20 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | @@ -679,6 +695,20 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | ||
679 | return true; | 695 | return true; |
680 | } | 696 | } |
681 | 697 | ||
698 | + private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, EntityCountCmd cmd) { | ||
699 | + if (cmd.getCmdId() < 0) { | ||
700 | + TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, | ||
701 | + "Cmd id is negative value!"); | ||
702 | + sendWsMsg(sessionRef, update); | ||
703 | + return false; | ||
704 | + } else if (cmd.getQuery() == null) { | ||
705 | + TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, "Query is empty!"); | ||
706 | + sendWsMsg(sessionRef, update); | ||
707 | + return false; | ||
708 | + } | ||
709 | + return true; | ||
710 | + } | ||
711 | + | ||
682 | private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { | 712 | private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) { |
683 | if (cmd.getCmdId() < 0) { | 713 | if (cmd.getCmdId() < 0) { |
684 | TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, | 714 | TelemetrySubscriptionUpdate update = new TelemetrySubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, |
@@ -15,6 +15,7 @@ | @@ -15,6 +15,7 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.telemetry; | 16 | package org.thingsboard.server.service.telemetry; |
17 | 17 | ||
18 | +import org.thingsboard.server.service.telemetry.cmd.v2.CmdUpdate; | ||
18 | import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate; | 19 | import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate; |
19 | import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; | 20 | import org.thingsboard.server.service.telemetry.sub.TelemetrySubscriptionUpdate; |
20 | 21 | ||
@@ -29,6 +30,6 @@ public interface TelemetryWebSocketService { | @@ -29,6 +30,6 @@ public interface TelemetryWebSocketService { | ||
29 | 30 | ||
30 | void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate update); | 31 | void sendWsMsg(String sessionId, TelemetrySubscriptionUpdate update); |
31 | 32 | ||
32 | - void sendWsMsg(String sessionId, DataUpdate update); | 33 | + void sendWsMsg(String sessionId, CmdUpdate update); |
33 | 34 | ||
34 | } | 35 | } |
@@ -21,6 +21,8 @@ import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; | @@ -21,6 +21,8 @@ import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; | ||
21 | import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; | 21 | import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; |
22 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; | 22 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd; |
23 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUnsubscribeCmd; | 23 | import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUnsubscribeCmd; |
24 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; | ||
25 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUnsubscribeCmd; | ||
24 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; | 26 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; |
25 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; | 27 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; |
26 | 28 | ||
@@ -46,4 +48,8 @@ public class TelemetryPluginCmdsWrapper { | @@ -46,4 +48,8 @@ public class TelemetryPluginCmdsWrapper { | ||
46 | 48 | ||
47 | private List<AlarmDataUnsubscribeCmd> alarmDataUnsubscribeCmds; | 49 | private List<AlarmDataUnsubscribeCmd> alarmDataUnsubscribeCmds; |
48 | 50 | ||
51 | + private List<EntityCountCmd> entityCountCmds; | ||
52 | + | ||
53 | + private List<EntityCountUnsubscribeCmd> entityCountUnsubscribeCmds; | ||
54 | + | ||
49 | } | 55 | } |
@@ -18,14 +18,14 @@ package org.thingsboard.server.service.telemetry.cmd.v2; | @@ -18,14 +18,14 @@ package org.thingsboard.server.service.telemetry.cmd.v2; | ||
18 | import com.fasterxml.jackson.annotation.JsonCreator; | 18 | import com.fasterxml.jackson.annotation.JsonCreator; |
19 | import com.fasterxml.jackson.annotation.JsonProperty; | 19 | import com.fasterxml.jackson.annotation.JsonProperty; |
20 | import lombok.Getter; | 20 | import lombok.Getter; |
21 | -import lombok.NoArgsConstructor; | 21 | +import lombok.ToString; |
22 | import org.thingsboard.server.common.data.page.PageData; | 22 | import org.thingsboard.server.common.data.page.PageData; |
23 | import org.thingsboard.server.common.data.query.AlarmData; | 23 | import org.thingsboard.server.common.data.query.AlarmData; |
24 | -import org.thingsboard.server.common.data.query.EntityData; | ||
25 | import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; | 24 | import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; |
26 | 25 | ||
27 | import java.util.List; | 26 | import java.util.List; |
28 | 27 | ||
28 | +@ToString | ||
29 | public class AlarmDataUpdate extends DataUpdate<AlarmData> { | 29 | public class AlarmDataUpdate extends DataUpdate<AlarmData> { |
30 | 30 | ||
31 | @Getter | 31 | @Getter |
@@ -44,8 +44,8 @@ public class AlarmDataUpdate extends DataUpdate<AlarmData> { | @@ -44,8 +44,8 @@ public class AlarmDataUpdate extends DataUpdate<AlarmData> { | ||
44 | } | 44 | } |
45 | 45 | ||
46 | @Override | 46 | @Override |
47 | - public DataUpdateType getDataUpdateType() { | ||
48 | - return DataUpdateType.ALARM_DATA; | 47 | + public CmdUpdateType getCmdUpdateType() { |
48 | + return CmdUpdateType.ALARM_DATA; | ||
49 | } | 49 | } |
50 | 50 | ||
51 | @JsonCreator | 51 | @JsonCreator |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.telemetry.cmd.v2; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
19 | +import lombok.AllArgsConstructor; | ||
20 | +import lombok.Data; | ||
21 | + | ||
22 | +@Data | ||
23 | +@AllArgsConstructor | ||
24 | +@JsonIgnoreProperties(ignoreUnknown = true) | ||
25 | +public abstract class CmdUpdate { | ||
26 | + | ||
27 | + private final int cmdId; | ||
28 | + private final int errorCode; | ||
29 | + private final String errorMsg; | ||
30 | + | ||
31 | + public abstract CmdUpdateType getCmdUpdateType(); | ||
32 | + | ||
33 | +} |
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/CmdUpdateType.java
renamed from
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/DataUpdateType.java
@@ -15,7 +15,8 @@ | @@ -15,7 +15,8 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.telemetry.cmd.v2; | 16 | package org.thingsboard.server.service.telemetry.cmd.v2; |
17 | 17 | ||
18 | -public enum DataUpdateType { | 18 | +public enum CmdUpdateType { |
19 | ENTITY_DATA, | 19 | ENTITY_DATA, |
20 | - ALARM_DATA | 20 | + ALARM_DATA, |
21 | + COUNT_DATA | ||
21 | } | 22 | } |
@@ -15,24 +15,24 @@ | @@ -15,24 +15,24 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.telemetry.cmd.v2; | 16 | package org.thingsboard.server.service.telemetry.cmd.v2; |
17 | 17 | ||
18 | -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
19 | -import lombok.AllArgsConstructor; | ||
20 | -import lombok.Data; | 18 | +import lombok.Getter; |
21 | import org.thingsboard.server.common.data.page.PageData; | 19 | import org.thingsboard.server.common.data.page.PageData; |
22 | import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; | 20 | import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; |
23 | 21 | ||
24 | import java.util.List; | 22 | import java.util.List; |
25 | 23 | ||
26 | -@Data | ||
27 | -@AllArgsConstructor | ||
28 | -@JsonIgnoreProperties(ignoreUnknown = true) | ||
29 | -public abstract class DataUpdate<T> { | 24 | +public abstract class DataUpdate<T> extends CmdUpdate { |
30 | 25 | ||
31 | - private final int cmdId; | 26 | + @Getter |
32 | private final PageData<T> data; | 27 | private final PageData<T> data; |
28 | + @Getter | ||
33 | private final List<T> update; | 29 | private final List<T> update; |
34 | - private final int errorCode; | ||
35 | - private final String errorMsg; | 30 | + |
31 | + public DataUpdate(int cmdId, PageData<T> data, List<T> update, int errorCode, String errorMsg) { | ||
32 | + super(cmdId, errorCode, errorMsg); | ||
33 | + this.data = data; | ||
34 | + this.update = update; | ||
35 | + } | ||
36 | 36 | ||
37 | public DataUpdate(int cmdId, PageData<T> data, List<T> update) { | 37 | public DataUpdate(int cmdId, PageData<T> data, List<T> update) { |
38 | this(cmdId, data, update, SubscriptionErrorCode.NO_ERROR.getCode(), null); | 38 | this(cmdId, data, update, SubscriptionErrorCode.NO_ERROR.getCode(), null); |
@@ -42,5 +42,4 @@ public abstract class DataUpdate<T> { | @@ -42,5 +42,4 @@ public abstract class DataUpdate<T> { | ||
42 | this(cmdId, null, null, errorCode, errorMsg); | 42 | this(cmdId, null, null, errorCode, errorMsg); |
43 | } | 43 | } |
44 | 44 | ||
45 | - public abstract DataUpdateType getDataUpdateType(); | ||
46 | } | 45 | } |
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountCmd.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.telemetry.cmd.v2; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | ||
19 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
20 | +import lombok.Getter; | ||
21 | +import org.thingsboard.server.common.data.query.EntityCountQuery; | ||
22 | +import org.thingsboard.server.common.data.query.EntityDataQuery; | ||
23 | + | ||
24 | +public class EntityCountCmd extends DataCmd { | ||
25 | + | ||
26 | + @Getter | ||
27 | + private final EntityCountQuery query; | ||
28 | + | ||
29 | + @JsonCreator | ||
30 | + public EntityCountCmd(@JsonProperty("cmdId") int cmdId, | ||
31 | + @JsonProperty("query") EntityCountQuery query) { | ||
32 | + super(cmdId); | ||
33 | + this.query = query; | ||
34 | + } | ||
35 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.telemetry.cmd.v2; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | + | ||
20 | +@Data | ||
21 | +public class EntityCountUnsubscribeCmd implements UnsubscribeCmd { | ||
22 | + | ||
23 | + private final int cmdId; | ||
24 | + | ||
25 | +} |
application/src/main/java/org/thingsboard/server/service/telemetry/cmd/v2/EntityCountUpdate.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.telemetry.cmd.v2; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | ||
19 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
20 | +import lombok.Getter; | ||
21 | +import lombok.ToString; | ||
22 | +import org.thingsboard.server.common.data.page.PageData; | ||
23 | +import org.thingsboard.server.common.data.query.EntityData; | ||
24 | +import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; | ||
25 | + | ||
26 | +import java.util.List; | ||
27 | + | ||
28 | +@ToString | ||
29 | +public class EntityCountUpdate extends CmdUpdate { | ||
30 | + | ||
31 | + @Getter | ||
32 | + private int count; | ||
33 | + | ||
34 | + public EntityCountUpdate(int cmdId, int count) { | ||
35 | + super(cmdId, SubscriptionErrorCode.NO_ERROR.getCode(), null); | ||
36 | + this.count = count; | ||
37 | + } | ||
38 | + | ||
39 | + public EntityCountUpdate(int cmdId, int errorCode, String errorMsg) { | ||
40 | + super(cmdId, errorCode, errorMsg); | ||
41 | + } | ||
42 | + | ||
43 | + @Override | ||
44 | + public CmdUpdateType getCmdUpdateType() { | ||
45 | + return CmdUpdateType.COUNT_DATA; | ||
46 | + } | ||
47 | + | ||
48 | + @JsonCreator | ||
49 | + public EntityCountUpdate(@JsonProperty("cmdId") int cmdId, | ||
50 | + @JsonProperty("count") int count, | ||
51 | + @JsonProperty("errorCode") int errorCode, | ||
52 | + @JsonProperty("errorMsg") String errorMsg) { | ||
53 | + super(cmdId, errorCode, errorMsg); | ||
54 | + this.count = count; | ||
55 | + } | ||
56 | + | ||
57 | +} |
@@ -16,16 +16,16 @@ | @@ -16,16 +16,16 @@ | ||
16 | package org.thingsboard.server.service.telemetry.cmd.v2; | 16 | package org.thingsboard.server.service.telemetry.cmd.v2; |
17 | 17 | ||
18 | import com.fasterxml.jackson.annotation.JsonCreator; | 18 | import com.fasterxml.jackson.annotation.JsonCreator; |
19 | -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
20 | import com.fasterxml.jackson.annotation.JsonProperty; | 19 | import com.fasterxml.jackson.annotation.JsonProperty; |
21 | import lombok.Getter; | 20 | import lombok.Getter; |
21 | +import lombok.ToString; | ||
22 | import org.thingsboard.server.common.data.page.PageData; | 22 | import org.thingsboard.server.common.data.page.PageData; |
23 | import org.thingsboard.server.common.data.query.EntityData; | 23 | import org.thingsboard.server.common.data.query.EntityData; |
24 | import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; | 24 | import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; |
25 | 25 | ||
26 | import java.util.List; | 26 | import java.util.List; |
27 | 27 | ||
28 | - | 28 | +@ToString |
29 | public class EntityDataUpdate extends DataUpdate<EntityData> { | 29 | public class EntityDataUpdate extends DataUpdate<EntityData> { |
30 | 30 | ||
31 | @Getter | 31 | @Getter |
@@ -41,8 +41,8 @@ public class EntityDataUpdate extends DataUpdate<EntityData> { | @@ -41,8 +41,8 @@ public class EntityDataUpdate extends DataUpdate<EntityData> { | ||
41 | } | 41 | } |
42 | 42 | ||
43 | @Override | 43 | @Override |
44 | - public DataUpdateType getDataUpdateType() { | ||
45 | - return DataUpdateType.ENTITY_DATA; | 44 | + public CmdUpdateType getCmdUpdateType() { |
45 | + return CmdUpdateType.ENTITY_DATA; | ||
46 | } | 46 | } |
47 | 47 | ||
48 | @JsonCreator | 48 | @JsonCreator |
@@ -15,8 +15,6 @@ | @@ -15,8 +15,6 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.telemetry.cmd.v2; | 16 | package org.thingsboard.server.service.telemetry.cmd.v2; |
17 | 17 | ||
18 | -import lombok.Data; | ||
19 | - | ||
20 | public interface UnsubscribeCmd { | 18 | public interface UnsubscribeCmd { |
21 | 19 | ||
22 | int getCmdId(); | 20 | int getCmdId(); |
@@ -30,7 +30,7 @@ | @@ -30,7 +30,7 @@ | ||
30 | <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />--> | 30 | <!-- <logger name="org.thingsboard.server.service.queue" level="TRACE" />--> |
31 | <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />--> | 31 | <!-- <logger name="org.thingsboard.server.service.transport" level="TRACE" />--> |
32 | <!-- <logger name="org.thingsboard.server.queue.memory.InMemoryStorage" level="DEBUG" />--> | 32 | <!-- <logger name="org.thingsboard.server.queue.memory.InMemoryStorage" level="DEBUG" />--> |
33 | - | 33 | +<!-- <logger name="org.thingsboard.server.service.ttl.AbstractCleanUpService" level="DEBUG" />--> |
34 | 34 | ||
35 | <!-- <logger name="org.thingsboard.server.service.subscription" level="TRACE"/>--> | 35 | <!-- <logger name="org.thingsboard.server.service.subscription" level="TRACE"/>--> |
36 | <!-- <logger name="org.thingsboard.server.service.telemetry" level="TRACE"/>--> | 36 | <!-- <logger name="org.thingsboard.server.service.telemetry" level="TRACE"/>--> |
@@ -322,6 +322,9 @@ actors: | @@ -322,6 +322,9 @@ actors: | ||
322 | cache: | 322 | cache: |
323 | # caffeine or redis | 323 | # caffeine or redis |
324 | type: "${CACHE_TYPE:caffeine}" | 324 | type: "${CACHE_TYPE:caffeine}" |
325 | + attributes: | ||
326 | + # make sure that if cache.type is 'redis' and cache.attributes.enabled is 'true' that you change 'maxmemory-policy' Redis config property to 'allkeys-lru', 'allkeys-lfu' or 'allkeys-random' | ||
327 | + enabled: "${CACHE_ATTRIBUTES_ENABLED:true}" | ||
325 | 328 | ||
326 | caffeine: | 329 | caffeine: |
327 | specs: | 330 | specs: |
@@ -355,6 +358,9 @@ caffeine: | @@ -355,6 +358,9 @@ caffeine: | ||
355 | deviceProfiles: | 358 | deviceProfiles: |
356 | timeToLiveInMinutes: 1440 | 359 | timeToLiveInMinutes: 1440 |
357 | maxSize: 0 | 360 | maxSize: 0 |
361 | + attributes: | ||
362 | + timeToLiveInMinutes: 1440 | ||
363 | + maxSize: 100000 | ||
358 | 364 | ||
359 | redis: | 365 | redis: |
360 | # standalone or cluster | 366 | # standalone or cluster |
@@ -539,6 +545,7 @@ transport: | @@ -539,6 +545,7 @@ transport: | ||
539 | http: | 545 | http: |
540 | enabled: "${HTTP_ENABLED:true}" | 546 | enabled: "${HTTP_ENABLED:true}" |
541 | request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}" | 547 | request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}" |
548 | + max_request_timeout: "${HTTP_MAX_REQUEST_TIMEOUT:300000}" | ||
542 | # Local MQTT transport parameters | 549 | # Local MQTT transport parameters |
543 | mqtt: | 550 | mqtt: |
544 | # Enable/disable mqtt transport protocol. | 551 | # Enable/disable mqtt transport protocol. |
@@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder; | @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder; | ||
44 | import org.thingsboard.server.common.data.query.EntityKey; | 44 | import org.thingsboard.server.common.data.query.EntityKey; |
45 | import org.thingsboard.server.common.data.query.EntityKeyType; | 45 | import org.thingsboard.server.common.data.query.EntityKeyType; |
46 | import org.thingsboard.server.common.data.query.EntityListFilter; | 46 | import org.thingsboard.server.common.data.query.EntityListFilter; |
47 | +import org.thingsboard.server.common.data.query.EntityTypeFilter; | ||
47 | import org.thingsboard.server.common.data.query.FilterPredicateValue; | 48 | import org.thingsboard.server.common.data.query.FilterPredicateValue; |
48 | import org.thingsboard.server.common.data.query.KeyFilter; | 49 | import org.thingsboard.server.common.data.query.KeyFilter; |
49 | import org.thingsboard.server.common.data.query.NumericFilterPredicate; | 50 | import org.thingsboard.server.common.data.query.NumericFilterPredicate; |
@@ -132,6 +133,14 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe | @@ -132,6 +133,14 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe | ||
132 | 133 | ||
133 | count = doPostWithResponse("/api/entitiesQuery/count", countQuery, Long.class); | 134 | count = doPostWithResponse("/api/entitiesQuery/count", countQuery, Long.class); |
134 | Assert.assertEquals(97, count.longValue()); | 135 | Assert.assertEquals(97, count.longValue()); |
136 | + | ||
137 | + EntityTypeFilter filter2 = new EntityTypeFilter(); | ||
138 | + filter2.setEntityType(EntityType.DEVICE); | ||
139 | + | ||
140 | + EntityCountQuery countQuery2 = new EntityCountQuery(filter2); | ||
141 | + | ||
142 | + Long count2 = doPostWithResponse("/api/entitiesQuery/count", countQuery2, Long.class); | ||
143 | + Assert.assertEquals(97, count2.longValue()); | ||
135 | } | 144 | } |
136 | 145 | ||
137 | @Test | 146 | @Test |
@@ -198,11 +207,31 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe | @@ -198,11 +207,31 @@ public abstract class BaseEntityQueryControllerTest extends AbstractControllerTe | ||
198 | Assert.assertEquals(11, data.getTotalElements()); | 207 | Assert.assertEquals(11, data.getTotalElements()); |
199 | Assert.assertEquals("Device19", data.getData().get(0).getLatest().get(EntityKeyType.ENTITY_FIELD).get("name").getValue()); | 208 | Assert.assertEquals("Device19", data.getData().get(0).getLatest().get(EntityKeyType.ENTITY_FIELD).get("name").getValue()); |
200 | 209 | ||
210 | + | ||
211 | + EntityTypeFilter filter2 = new EntityTypeFilter(); | ||
212 | + filter2.setEntityType(EntityType.DEVICE); | ||
213 | + | ||
214 | + EntityDataSortOrder sortOrder2 = new EntityDataSortOrder( | ||
215 | + new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC | ||
216 | + ); | ||
217 | + EntityDataPageLink pageLink2 = new EntityDataPageLink(10, 0, null, sortOrder2); | ||
218 | + List<EntityKey> entityFields2 = Collections.singletonList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name")); | ||
219 | + | ||
220 | + EntityDataQuery query2 = new EntityDataQuery(filter2, pageLink2, entityFields2, null, null); | ||
221 | + | ||
222 | + PageData<EntityData> data2 = | ||
223 | + doPostWithTypedResponse("/api/entitiesQuery/find", query2, new TypeReference<PageData<EntityData>>() { | ||
224 | + }); | ||
225 | + | ||
226 | + Assert.assertEquals(97, data2.getTotalElements()); | ||
227 | + Assert.assertEquals(10, data2.getTotalPages()); | ||
228 | + Assert.assertTrue(data2.hasNext()); | ||
229 | + Assert.assertEquals(10, data2.getData().size()); | ||
230 | + | ||
201 | } | 231 | } |
202 | 232 | ||
203 | @Test | 233 | @Test |
204 | public void testFindEntityDataByQueryWithAttributes() throws Exception { | 234 | public void testFindEntityDataByQueryWithAttributes() throws Exception { |
205 | - | ||
206 | List<Device> devices = new ArrayList<>(); | 235 | List<Device> devices = new ArrayList<>(); |
207 | List<Long> temperatures = new ArrayList<>(); | 236 | List<Long> temperatures = new ArrayList<>(); |
208 | List<Long> highTemperatures = new ArrayList<>(); | 237 | List<Long> highTemperatures = new ArrayList<>(); |
@@ -35,16 +35,23 @@ import org.thingsboard.server.common.data.kv.LongDataEntry; | @@ -35,16 +35,23 @@ import org.thingsboard.server.common.data.kv.LongDataEntry; | ||
35 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 35 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
36 | import org.thingsboard.server.common.data.page.PageData; | 36 | import org.thingsboard.server.common.data.page.PageData; |
37 | import org.thingsboard.server.common.data.query.DeviceTypeFilter; | 37 | import org.thingsboard.server.common.data.query.DeviceTypeFilter; |
38 | +import org.thingsboard.server.common.data.query.EntityCountQuery; | ||
38 | import org.thingsboard.server.common.data.query.EntityData; | 39 | import org.thingsboard.server.common.data.query.EntityData; |
39 | import org.thingsboard.server.common.data.query.EntityDataPageLink; | 40 | import org.thingsboard.server.common.data.query.EntityDataPageLink; |
40 | import org.thingsboard.server.common.data.query.EntityDataQuery; | 41 | import org.thingsboard.server.common.data.query.EntityDataQuery; |
41 | import org.thingsboard.server.common.data.query.EntityKey; | 42 | import org.thingsboard.server.common.data.query.EntityKey; |
42 | import org.thingsboard.server.common.data.query.EntityKeyType; | 43 | import org.thingsboard.server.common.data.query.EntityKeyType; |
44 | +import org.thingsboard.server.common.data.query.EntityKeyValueType; | ||
45 | +import org.thingsboard.server.common.data.query.FilterPredicateValue; | ||
46 | +import org.thingsboard.server.common.data.query.KeyFilter; | ||
47 | +import org.thingsboard.server.common.data.query.NumericFilterPredicate; | ||
43 | import org.thingsboard.server.common.data.query.TsValue; | 48 | import org.thingsboard.server.common.data.query.TsValue; |
44 | import org.thingsboard.server.common.data.security.Authority; | 49 | import org.thingsboard.server.common.data.security.Authority; |
45 | import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; | 50 | import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; |
46 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | 51 | import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
47 | import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; | 52 | import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; |
53 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd; | ||
54 | +import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate; | ||
48 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; | 55 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; |
49 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; | 56 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; |
50 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd; | 57 | import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd; |
@@ -244,6 +251,98 @@ public class BaseWebsocketApiTest extends AbstractWebsocketTest { | @@ -244,6 +251,98 @@ public class BaseWebsocketApiTest extends AbstractWebsocketTest { | ||
244 | } | 251 | } |
245 | 252 | ||
246 | @Test | 253 | @Test |
254 | + public void testEntityCountWsCmd() throws Exception { | ||
255 | + Device device = new Device(); | ||
256 | + device.setName("Device"); | ||
257 | + device.setType("default"); | ||
258 | + device.setLabel("testLabel" + (int) (Math.random() * 1000)); | ||
259 | + device = doPost("/api/device", device, Device.class); | ||
260 | + | ||
261 | + AttributeKvEntry dataPoint1 = new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("temperature", 42L)); | ||
262 | + sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, Collections.singletonList(dataPoint1)); | ||
263 | + | ||
264 | + DeviceTypeFilter dtf1 = new DeviceTypeFilter(); | ||
265 | + dtf1.setDeviceNameFilter("D"); | ||
266 | + dtf1.setDeviceType("default"); | ||
267 | + EntityCountQuery edq1 = new EntityCountQuery(dtf1, Collections.emptyList()); | ||
268 | + | ||
269 | + EntityCountCmd cmd1 = new EntityCountCmd(1, edq1); | ||
270 | + | ||
271 | + TelemetryPluginCmdsWrapper wrapper1 = new TelemetryPluginCmdsWrapper(); | ||
272 | + wrapper1.setEntityCountCmds(Collections.singletonList(cmd1)); | ||
273 | + | ||
274 | + wsClient.send(mapper.writeValueAsString(wrapper1)); | ||
275 | + String msg1 = wsClient.waitForReply(); | ||
276 | + EntityCountUpdate update1 = mapper.readValue(msg1, EntityCountUpdate.class); | ||
277 | + Assert.assertEquals(1, update1.getCmdId()); | ||
278 | + Assert.assertEquals(1, update1.getCount()); | ||
279 | + | ||
280 | + DeviceTypeFilter dtf2 = new DeviceTypeFilter(); | ||
281 | + dtf2.setDeviceNameFilter("D"); | ||
282 | + dtf2.setDeviceType("non-existing-device-type"); | ||
283 | + EntityCountQuery edq2 = new EntityCountQuery(dtf2, Collections.emptyList()); | ||
284 | + | ||
285 | + EntityCountCmd cmd2 = new EntityCountCmd(2, edq2); | ||
286 | + | ||
287 | + TelemetryPluginCmdsWrapper wrapper2 = new TelemetryPluginCmdsWrapper(); | ||
288 | + wrapper2.setEntityCountCmds(Collections.singletonList(cmd2)); | ||
289 | + wsClient.send(mapper.writeValueAsString(wrapper2)); | ||
290 | + | ||
291 | + String msg2 = wsClient.waitForReply(); | ||
292 | + EntityCountUpdate update2 = mapper.readValue(msg2, EntityCountUpdate.class); | ||
293 | + Assert.assertEquals(2, update2.getCmdId()); | ||
294 | + Assert.assertEquals(0, update2.getCount()); | ||
295 | + | ||
296 | + KeyFilter highTemperatureFilter = new KeyFilter(); | ||
297 | + highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature")); | ||
298 | + NumericFilterPredicate predicate = new NumericFilterPredicate(); | ||
299 | + predicate.setValue(FilterPredicateValue.fromDouble(40)); | ||
300 | + predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER); | ||
301 | + highTemperatureFilter.setPredicate(predicate); | ||
302 | + highTemperatureFilter.setValueType(EntityKeyValueType.NUMERIC); | ||
303 | + | ||
304 | + DeviceTypeFilter dtf3 = new DeviceTypeFilter(); | ||
305 | + dtf3.setDeviceNameFilter("D"); | ||
306 | + dtf3.setDeviceType("default"); | ||
307 | + EntityCountQuery edq3 = new EntityCountQuery(dtf3, Collections.singletonList(highTemperatureFilter)); | ||
308 | + | ||
309 | + EntityCountCmd cmd3 = new EntityCountCmd(3, edq3); | ||
310 | + | ||
311 | + TelemetryPluginCmdsWrapper wrapper3 = new TelemetryPluginCmdsWrapper(); | ||
312 | + wrapper3.setEntityCountCmds(Collections.singletonList(cmd3)); | ||
313 | + wsClient.send(mapper.writeValueAsString(wrapper3)); | ||
314 | + | ||
315 | + String msg3 = wsClient.waitForReply(); | ||
316 | + EntityCountUpdate update3 = mapper.readValue(msg3, EntityCountUpdate.class); | ||
317 | + Assert.assertEquals(3, update3.getCmdId()); | ||
318 | + Assert.assertEquals(1, update3.getCount()); | ||
319 | + | ||
320 | + KeyFilter highTemperatureFilter2 = new KeyFilter(); | ||
321 | + highTemperatureFilter2.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature")); | ||
322 | + NumericFilterPredicate predicate2 = new NumericFilterPredicate(); | ||
323 | + predicate2.setValue(FilterPredicateValue.fromDouble(50)); | ||
324 | + predicate2.setOperation(NumericFilterPredicate.NumericOperation.GREATER); | ||
325 | + highTemperatureFilter2.setPredicate(predicate2); | ||
326 | + highTemperatureFilter2.setValueType(EntityKeyValueType.NUMERIC); | ||
327 | + | ||
328 | + DeviceTypeFilter dtf4 = new DeviceTypeFilter(); | ||
329 | + dtf4.setDeviceNameFilter("D"); | ||
330 | + dtf4.setDeviceType("default"); | ||
331 | + EntityCountQuery edq4 = new EntityCountQuery(dtf4, Collections.singletonList(highTemperatureFilter2)); | ||
332 | + | ||
333 | + EntityCountCmd cmd4 = new EntityCountCmd(4, edq4); | ||
334 | + | ||
335 | + TelemetryPluginCmdsWrapper wrapper4 = new TelemetryPluginCmdsWrapper(); | ||
336 | + wrapper4.setEntityCountCmds(Collections.singletonList(cmd4)); | ||
337 | + wsClient.send(mapper.writeValueAsString(wrapper4)); | ||
338 | + | ||
339 | + String msg4 = wsClient.waitForReply(); | ||
340 | + EntityCountUpdate update4 = mapper.readValue(msg4, EntityCountUpdate.class); | ||
341 | + Assert.assertEquals(4, update4.getCmdId()); | ||
342 | + Assert.assertEquals(0, update4.getCount()); | ||
343 | + } | ||
344 | + | ||
345 | + @Test | ||
247 | public void testEntityDataLatestWidgetFlow() throws Exception { | 346 | public void testEntityDataLatestWidgetFlow() throws Exception { |
248 | Device device = new Device(); | 347 | Device device = new Device(); |
249 | device.setName("Device"); | 348 | device.setName("Device"); |
@@ -27,7 +27,7 @@ import java.util.Arrays; | @@ -27,7 +27,7 @@ import java.util.Arrays; | ||
27 | @RunWith(ClasspathSuite.class) | 27 | @RunWith(ClasspathSuite.class) |
28 | @ClasspathSuite.ClassnameFilters({ | 28 | @ClasspathSuite.ClassnameFilters({ |
29 | // "org.thingsboard.server.controller.sql.WebsocketApiSqlTest", | 29 | // "org.thingsboard.server.controller.sql.WebsocketApiSqlTest", |
30 | -// "org.thingsboard.server.controller.sql.TenantProfileControllerSqlTest", | 30 | +// "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest", |
31 | "org.thingsboard.server.controller.sql.*Test", | 31 | "org.thingsboard.server.controller.sql.*Test", |
32 | }) | 32 | }) |
33 | public class ControllerSqlTestSuite { | 33 | public class ControllerSqlTestSuite { |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -26,4 +26,5 @@ public class CacheConstants { | @@ -26,4 +26,5 @@ public class CacheConstants { | ||
26 | public static final String SECURITY_SETTINGS_CACHE = "securitySettings"; | 26 | public static final String SECURITY_SETTINGS_CACHE = "securitySettings"; |
27 | public static final String TENANT_PROFILE_CACHE = "tenantProfiles"; | 27 | public static final String TENANT_PROFILE_CACHE = "tenantProfiles"; |
28 | public static final String DEVICE_PROFILE_CACHE = "deviceProfiles"; | 28 | public static final String DEVICE_PROFILE_CACHE = "deviceProfiles"; |
29 | + public static final String ATTRIBUTES_CACHE = "attributes"; | ||
29 | } | 30 | } |
@@ -19,7 +19,7 @@ import lombok.Data; | @@ -19,7 +19,7 @@ import lombok.Data; | ||
19 | import org.thingsboard.server.common.data.EntityType; | 19 | import org.thingsboard.server.common.data.EntityType; |
20 | import org.thingsboard.server.common.data.relation.EntityRelation; | 20 | import org.thingsboard.server.common.data.relation.EntityRelation; |
21 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; | 21 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; |
22 | -import org.thingsboard.server.common.data.relation.EntityTypeFilter; | 22 | +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; |
23 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; | 23 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; |
24 | 24 | ||
25 | import java.util.Collections; | 25 | import java.util.Collections; |
@@ -39,7 +39,7 @@ public class AssetSearchQuery { | @@ -39,7 +39,7 @@ public class AssetSearchQuery { | ||
39 | EntityRelationsQuery query = new EntityRelationsQuery(); | 39 | EntityRelationsQuery query = new EntityRelationsQuery(); |
40 | query.setParameters(parameters); | 40 | query.setParameters(parameters); |
41 | query.setFilters( | 41 | query.setFilters( |
42 | - Collections.singletonList(new EntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType, | 42 | + Collections.singletonList(new RelationEntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType, |
43 | Collections.singletonList(EntityType.ASSET)))); | 43 | Collections.singletonList(EntityType.ASSET)))); |
44 | return query; | 44 | return query; |
45 | } | 45 | } |
@@ -19,7 +19,7 @@ import lombok.Data; | @@ -19,7 +19,7 @@ import lombok.Data; | ||
19 | import org.thingsboard.server.common.data.EntityType; | 19 | import org.thingsboard.server.common.data.EntityType; |
20 | import org.thingsboard.server.common.data.relation.EntityRelation; | 20 | import org.thingsboard.server.common.data.relation.EntityRelation; |
21 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; | 21 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; |
22 | -import org.thingsboard.server.common.data.relation.EntityTypeFilter; | 22 | +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; |
23 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; | 23 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; |
24 | 24 | ||
25 | import java.util.Collections; | 25 | import java.util.Collections; |
@@ -36,7 +36,7 @@ public class DeviceSearchQuery { | @@ -36,7 +36,7 @@ public class DeviceSearchQuery { | ||
36 | EntityRelationsQuery query = new EntityRelationsQuery(); | 36 | EntityRelationsQuery query = new EntityRelationsQuery(); |
37 | query.setParameters(parameters); | 37 | query.setParameters(parameters); |
38 | query.setFilters( | 38 | query.setFilters( |
39 | - Collections.singletonList(new EntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType, | 39 | + Collections.singletonList(new RelationEntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType, |
40 | Collections.singletonList(EntityType.DEVICE)))); | 40 | Collections.singletonList(EntityType.DEVICE)))); |
41 | return query; | 41 | return query; |
42 | } | 42 | } |
@@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit; | @@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit; | ||
26 | @JsonIgnoreProperties(ignoreUnknown = true) | 26 | @JsonIgnoreProperties(ignoreUnknown = true) |
27 | public class AlarmCondition { | 27 | public class AlarmCondition { |
28 | 28 | ||
29 | - private List<KeyFilter> condition; | 29 | + private List<AlarmConditionFilter> condition; |
30 | private AlarmConditionSpec spec; | 30 | private AlarmConditionSpec spec; |
31 | 31 | ||
32 | } | 32 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.device.profile; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import org.thingsboard.server.common.data.query.EntityKeyValueType; | ||
20 | +import org.thingsboard.server.common.data.query.KeyFilterPredicate; | ||
21 | + | ||
22 | +@Data | ||
23 | +public class AlarmConditionFilter { | ||
24 | + | ||
25 | + private AlarmConditionFilterKey key; | ||
26 | + private EntityKeyValueType valueType; | ||
27 | + private Object value; | ||
28 | + private KeyFilterPredicate predicate; | ||
29 | + | ||
30 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.device.profile; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | + | ||
20 | +@Data | ||
21 | +public class AlarmConditionFilterKey { | ||
22 | + | ||
23 | + private final AlarmConditionKeyType type; | ||
24 | + private final String key; | ||
25 | + | ||
26 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.device.profile; | ||
17 | + | ||
18 | +public enum AlarmConditionKeyType { | ||
19 | + ATTRIBUTE, | ||
20 | + TIME_SERIES, | ||
21 | + ENTITY_FIELD, | ||
22 | + CONSTANT | ||
23 | +} |
@@ -19,7 +19,7 @@ import lombok.Data; | @@ -19,7 +19,7 @@ import lombok.Data; | ||
19 | import org.thingsboard.server.common.data.EntityType; | 19 | import org.thingsboard.server.common.data.EntityType; |
20 | import org.thingsboard.server.common.data.relation.EntityRelation; | 20 | import org.thingsboard.server.common.data.relation.EntityRelation; |
21 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; | 21 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; |
22 | -import org.thingsboard.server.common.data.relation.EntityTypeFilter; | 22 | +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; |
23 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; | 23 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; |
24 | 24 | ||
25 | import java.util.Collections; | 25 | import java.util.Collections; |
@@ -36,7 +36,7 @@ public class EntityViewSearchQuery { | @@ -36,7 +36,7 @@ public class EntityViewSearchQuery { | ||
36 | EntityRelationsQuery query = new EntityRelationsQuery(); | 36 | EntityRelationsQuery query = new EntityRelationsQuery(); |
37 | query.setParameters(parameters); | 37 | query.setParameters(parameters); |
38 | query.setFilters( | 38 | query.setFilters( |
39 | - Collections.singletonList(new EntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType, | 39 | + Collections.singletonList(new RelationEntityTypeFilter(relationType == null ? EntityRelation.CONTAINS_TYPE : relationType, |
40 | Collections.singletonList(EntityType.ENTITY_VIEW)))); | 40 | Collections.singletonList(EntityType.ENTITY_VIEW)))); |
41 | return query; | 41 | return query; |
42 | } | 42 | } |
@@ -29,15 +29,13 @@ public abstract class AbstractDataQuery<T extends EntityDataPageLink> extends En | @@ -29,15 +29,13 @@ public abstract class AbstractDataQuery<T extends EntityDataPageLink> extends En | ||
29 | protected List<EntityKey> entityFields; | 29 | protected List<EntityKey> entityFields; |
30 | @Getter | 30 | @Getter |
31 | protected List<EntityKey> latestValues; | 31 | protected List<EntityKey> latestValues; |
32 | - @Getter | ||
33 | - protected List<KeyFilter> keyFilters; | ||
34 | 32 | ||
35 | public AbstractDataQuery() { | 33 | public AbstractDataQuery() { |
36 | super(); | 34 | super(); |
37 | } | 35 | } |
38 | 36 | ||
39 | - public AbstractDataQuery(EntityFilter entityFilter) { | ||
40 | - super(entityFilter); | 37 | + public AbstractDataQuery(EntityFilter entityFilter, List<KeyFilter> keyFilters) { |
38 | + super(entityFilter, keyFilters); | ||
41 | } | 39 | } |
42 | 40 | ||
43 | public AbstractDataQuery(EntityFilter entityFilter, | 41 | public AbstractDataQuery(EntityFilter entityFilter, |
@@ -45,11 +43,10 @@ public abstract class AbstractDataQuery<T extends EntityDataPageLink> extends En | @@ -45,11 +43,10 @@ public abstract class AbstractDataQuery<T extends EntityDataPageLink> extends En | ||
45 | List<EntityKey> entityFields, | 43 | List<EntityKey> entityFields, |
46 | List<EntityKey> latestValues, | 44 | List<EntityKey> latestValues, |
47 | List<KeyFilter> keyFilters) { | 45 | List<KeyFilter> keyFilters) { |
48 | - super(entityFilter); | 46 | + super(entityFilter, keyFilters); |
49 | this.pageLink = pageLink; | 47 | this.pageLink = pageLink; |
50 | this.entityFields = entityFields; | 48 | this.entityFields = entityFields; |
51 | this.latestValues = latestValues; | 49 | this.latestValues = latestValues; |
52 | - this.keyFilters = keyFilters; | ||
53 | } | 50 | } |
54 | 51 | ||
55 | } | 52 | } |
@@ -30,8 +30,8 @@ public class AlarmDataQuery extends AbstractDataQuery<AlarmDataPageLink> { | @@ -30,8 +30,8 @@ public class AlarmDataQuery extends AbstractDataQuery<AlarmDataPageLink> { | ||
30 | public AlarmDataQuery() { | 30 | public AlarmDataQuery() { |
31 | } | 31 | } |
32 | 32 | ||
33 | - public AlarmDataQuery(EntityFilter entityFilter) { | ||
34 | - super(entityFilter); | 33 | + public AlarmDataQuery(EntityFilter entityFilter, List<KeyFilter> keyFilters) { |
34 | + super(entityFilter, keyFilters); | ||
35 | } | 35 | } |
36 | 36 | ||
37 | public AlarmDataQuery(EntityFilter entityFilter, AlarmDataPageLink pageLink, List<EntityKey> entityFields, List<EntityKey> latestValues, List<KeyFilter> keyFilters, List<EntityKey> alarmFields) { | 37 | public AlarmDataQuery(EntityFilter entityFilter, AlarmDataPageLink pageLink, List<EntityKey> entityFields, List<EntityKey> latestValues, List<KeyFilter> keyFilters, List<EntityKey> alarmFields) { |
@@ -17,14 +17,26 @@ package org.thingsboard.server.common.data.query; | @@ -17,14 +17,26 @@ package org.thingsboard.server.common.data.query; | ||
17 | 17 | ||
18 | import lombok.Getter; | 18 | import lombok.Getter; |
19 | 19 | ||
20 | +import java.util.Collections; | ||
21 | +import java.util.List; | ||
22 | + | ||
20 | public class EntityCountQuery { | 23 | public class EntityCountQuery { |
21 | 24 | ||
22 | @Getter | 25 | @Getter |
23 | private EntityFilter entityFilter; | 26 | private EntityFilter entityFilter; |
24 | 27 | ||
25 | - public EntityCountQuery() {} | 28 | + @Getter |
29 | + protected List<KeyFilter> keyFilters; | ||
30 | + | ||
31 | + public EntityCountQuery() { | ||
32 | + } | ||
26 | 33 | ||
27 | public EntityCountQuery(EntityFilter entityFilter) { | 34 | public EntityCountQuery(EntityFilter entityFilter) { |
35 | + this(entityFilter, Collections.emptyList()); | ||
36 | + } | ||
37 | + | ||
38 | + public EntityCountQuery(EntityFilter entityFilter, List<KeyFilter> keyFilters) { | ||
28 | this.entityFilter = entityFilter; | 39 | this.entityFilter = entityFilter; |
40 | + this.keyFilters = keyFilters; | ||
29 | } | 41 | } |
30 | } | 42 | } |
@@ -27,8 +27,8 @@ public class EntityDataQuery extends AbstractDataQuery<EntityDataPageLink> { | @@ -27,8 +27,8 @@ public class EntityDataQuery extends AbstractDataQuery<EntityDataPageLink> { | ||
27 | public EntityDataQuery() { | 27 | public EntityDataQuery() { |
28 | } | 28 | } |
29 | 29 | ||
30 | - public EntityDataQuery(EntityFilter entityFilter) { | ||
31 | - super(entityFilter); | 30 | + public EntityDataQuery(EntityFilter entityFilter, List<KeyFilter> keyFilters) { |
31 | + super(entityFilter, keyFilters); | ||
32 | } | 32 | } |
33 | 33 | ||
34 | public EntityDataQuery(EntityFilter entityFilter, EntityDataPageLink pageLink, List<EntityKey> entityFields, List<EntityKey> latestValues, List<KeyFilter> keyFilters) { | 34 | public EntityDataQuery(EntityFilter entityFilter, EntityDataPageLink pageLink, List<EntityKey> entityFields, List<EntityKey> latestValues, List<KeyFilter> keyFilters) { |
@@ -29,6 +29,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; | @@ -29,6 +29,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; | ||
29 | @JsonSubTypes.Type(value = SingleEntityFilter.class, name = "singleEntity"), | 29 | @JsonSubTypes.Type(value = SingleEntityFilter.class, name = "singleEntity"), |
30 | @JsonSubTypes.Type(value = EntityListFilter.class, name = "entityList"), | 30 | @JsonSubTypes.Type(value = EntityListFilter.class, name = "entityList"), |
31 | @JsonSubTypes.Type(value = EntityNameFilter.class, name = "entityName"), | 31 | @JsonSubTypes.Type(value = EntityNameFilter.class, name = "entityName"), |
32 | + @JsonSubTypes.Type(value = EntityTypeFilter.class, name = "entityType"), | ||
32 | @JsonSubTypes.Type(value = AssetTypeFilter.class, name = "assetType"), | 33 | @JsonSubTypes.Type(value = AssetTypeFilter.class, name = "assetType"), |
33 | @JsonSubTypes.Type(value = DeviceTypeFilter.class, name = "deviceType"), | 34 | @JsonSubTypes.Type(value = DeviceTypeFilter.class, name = "deviceType"), |
34 | @JsonSubTypes.Type(value = EntityViewTypeFilter.class, name = "entityViewType"), | 35 | @JsonSubTypes.Type(value = EntityViewTypeFilter.class, name = "entityViewType"), |
@@ -19,6 +19,7 @@ public enum EntityFilterType { | @@ -19,6 +19,7 @@ public enum EntityFilterType { | ||
19 | SINGLE_ENTITY("singleEntity"), | 19 | SINGLE_ENTITY("singleEntity"), |
20 | ENTITY_LIST("entityList"), | 20 | ENTITY_LIST("entityList"), |
21 | ENTITY_NAME("entityName"), | 21 | ENTITY_NAME("entityName"), |
22 | + ENTITY_TYPE("entityType"), | ||
22 | ASSET_TYPE("assetType"), | 23 | ASSET_TYPE("assetType"), |
23 | DEVICE_TYPE("deviceType"), | 24 | DEVICE_TYPE("deviceType"), |
24 | ENTITY_VIEW_TYPE("entityViewType"), | 25 | ENTITY_VIEW_TYPE("entityViewType"), |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.common.data.query; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import org.thingsboard.server.common.data.EntityType; | ||
20 | + | ||
21 | +@Data | ||
22 | +public class EntityTypeFilter implements EntityFilter { | ||
23 | + @Override | ||
24 | + public EntityFilterType getType() { | ||
25 | + return EntityFilterType.ENTITY_TYPE; | ||
26 | + } | ||
27 | + | ||
28 | + private EntityType entityType; | ||
29 | + | ||
30 | +} |
@@ -18,8 +18,7 @@ package org.thingsboard.server.common.data.query; | @@ -18,8 +18,7 @@ package org.thingsboard.server.common.data.query; | ||
18 | import lombok.Data; | 18 | import lombok.Data; |
19 | import org.thingsboard.server.common.data.id.EntityId; | 19 | import org.thingsboard.server.common.data.id.EntityId; |
20 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; | 20 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
21 | -import org.thingsboard.server.common.data.relation.EntityTypeFilter; | ||
22 | -import org.thingsboard.server.common.data.relation.RelationTypeGroup; | 21 | +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; |
23 | 22 | ||
24 | import java.util.List; | 23 | import java.util.List; |
25 | 24 | ||
@@ -33,7 +32,7 @@ public class RelationsQueryFilter implements EntityFilter { | @@ -33,7 +32,7 @@ public class RelationsQueryFilter implements EntityFilter { | ||
33 | 32 | ||
34 | private EntityId rootEntity; | 33 | private EntityId rootEntity; |
35 | private EntitySearchDirection direction; | 34 | private EntitySearchDirection direction; |
36 | - private List<EntityTypeFilter> filters; | 35 | + private List<RelationEntityTypeFilter> filters; |
37 | private int maxLevel; | 36 | private int maxLevel; |
38 | private boolean fetchLastLevelOnly; | 37 | private boolean fetchLastLevelOnly; |
39 | 38 |
@@ -26,6 +26,6 @@ import java.util.List; | @@ -26,6 +26,6 @@ import java.util.List; | ||
26 | public class EntityRelationsQuery { | 26 | public class EntityRelationsQuery { |
27 | 27 | ||
28 | private RelationsSearchParameters parameters; | 28 | private RelationsSearchParameters parameters; |
29 | - private List<EntityTypeFilter> filters; | 29 | + private List<RelationEntityTypeFilter> filters; |
30 | 30 | ||
31 | } | 31 | } |
common/data/src/main/java/org/thingsboard/server/common/data/relation/RelationEntityTypeFilter.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityTypeFilter.java
@@ -26,7 +26,7 @@ import java.util.List; | @@ -26,7 +26,7 @@ import java.util.List; | ||
26 | */ | 26 | */ |
27 | @Data | 27 | @Data |
28 | @AllArgsConstructor | 28 | @AllArgsConstructor |
29 | -public class EntityTypeFilter { | 29 | +public class RelationEntityTypeFilter { |
30 | 30 | ||
31 | private String relationType; | 31 | private String relationType; |
32 | 32 |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -120,7 +120,7 @@ public final class TbMsg implements Serializable { | @@ -120,7 +120,7 @@ public final class TbMsg implements Serializable { | ||
120 | private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, | 120 | private TbMsg(String queueName, UUID id, long ts, String type, EntityId originator, TbMsgMetaData metaData, TbMsgDataType dataType, String data, |
121 | RuleChainId ruleChainId, RuleNodeId ruleNodeId, int ruleNodeExecCounter, TbMsgCallback callback) { | 121 | RuleChainId ruleChainId, RuleNodeId ruleNodeId, int ruleNodeExecCounter, TbMsgCallback callback) { |
122 | this.id = id; | 122 | this.id = id; |
123 | - this.queueName = queueName; | 123 | + this.queueName = queueName != null ? queueName : ServiceQueue.MAIN; |
124 | if (ts > 0) { | 124 | if (ts > 0) { |
125 | this.ts = ts; | 125 | this.ts = ts; |
126 | } else { | 126 | } else { |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>common</artifactId> | 26 | <artifactId>common</artifactId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
22 | <modelVersion>4.0.0</modelVersion> | 22 | <modelVersion>4.0.0</modelVersion> |
23 | <parent> | 23 | <parent> |
24 | <groupId>org.thingsboard</groupId> | 24 | <groupId>org.thingsboard</groupId> |
25 | - <version>3.3.0-SNAPSHOT</version> | 25 | + <version>3.2.2-SNAPSHOT</version> |
26 | <artifactId>common</artifactId> | 26 | <artifactId>common</artifactId> |
27 | </parent> | 27 | </parent> |
28 | <groupId>org.thingsboard.common</groupId> | 28 | <groupId>org.thingsboard.common</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.common</groupId> | 22 | <groupId>org.thingsboard.common</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common.transport</groupId> | 26 | <groupId>org.thingsboard.common.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.common</groupId> | 22 | <groupId>org.thingsboard.common</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common.transport</groupId> | 26 | <groupId>org.thingsboard.common.transport</groupId> |
@@ -17,9 +17,13 @@ package org.thingsboard.server.transport.http; | @@ -17,9 +17,13 @@ package org.thingsboard.server.transport.http; | ||
17 | 17 | ||
18 | import lombok.Getter; | 18 | import lombok.Getter; |
19 | import lombok.extern.slf4j.Slf4j; | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.apache.coyote.ProtocolHandler; | ||
21 | +import org.apache.coyote.http11.Http11NioProtocol; | ||
20 | import org.springframework.beans.factory.annotation.Value; | 22 | import org.springframework.beans.factory.annotation.Value; |
21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | 23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
25 | +import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; | ||
26 | +import org.springframework.context.annotation.Bean; | ||
23 | import org.springframework.stereotype.Component; | 27 | import org.springframework.stereotype.Component; |
24 | import org.thingsboard.server.common.transport.TransportContext; | 28 | import org.thingsboard.server.common.transport.TransportContext; |
25 | 29 | ||
@@ -37,4 +41,18 @@ public class HttpTransportContext extends TransportContext { | @@ -37,4 +41,18 @@ public class HttpTransportContext extends TransportContext { | ||
37 | @Value("${transport.http.request_timeout}") | 41 | @Value("${transport.http.request_timeout}") |
38 | private long defaultTimeout; | 42 | private long defaultTimeout; |
39 | 43 | ||
44 | + @Getter | ||
45 | + @Value("${transport.http.max_request_timeout}") | ||
46 | + private long maxRequestTimeout; | ||
47 | + | ||
48 | + @Bean | ||
49 | + public TomcatConnectorCustomizer tomcatAsyncTimeoutConnectorCustomizer() { | ||
50 | + return connector -> { | ||
51 | + ProtocolHandler handler = connector.getProtocolHandler(); | ||
52 | + if (handler instanceof Http11NioProtocol) { | ||
53 | + log.trace("Setting async max request timeout {}", maxRequestTimeout); | ||
54 | + connector.setAsyncTimeout(maxRequestTimeout); | ||
55 | + } | ||
56 | + }; | ||
57 | + } | ||
40 | } | 58 | } |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.common</groupId> | 22 | <groupId>org.thingsboard.common</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common.transport</groupId> | 26 | <groupId>org.thingsboard.common.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.common</groupId> | 22 | <groupId>org.thingsboard.common</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common.transport</groupId> | 26 | <groupId>org.thingsboard.common.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>common</artifactId> | 24 | <artifactId>common</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.common</groupId> | 26 | <groupId>org.thingsboard.common</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>dao</artifactId> | 26 | <artifactId>dao</artifactId> |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.attributes; | ||
17 | + | ||
18 | +import lombok.AllArgsConstructor; | ||
19 | +import lombok.EqualsAndHashCode; | ||
20 | +import lombok.Getter; | ||
21 | +import org.thingsboard.server.common.data.id.EntityId; | ||
22 | + | ||
23 | +import java.io.Serializable; | ||
24 | + | ||
25 | +@EqualsAndHashCode | ||
26 | +@Getter | ||
27 | +@AllArgsConstructor | ||
28 | +public class AttributeCacheKey implements Serializable { | ||
29 | + private static final long serialVersionUID = 2013369077925351881L; | ||
30 | + | ||
31 | + private final String scope; | ||
32 | + private final EntityId entityId; | ||
33 | + private final String key; | ||
34 | + | ||
35 | + @Override | ||
36 | + public String toString() { | ||
37 | + return entityId + "_" + scope + "_" + key; | ||
38 | + } | ||
39 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.attributes; | ||
17 | + | ||
18 | +import org.thingsboard.server.common.data.id.EntityId; | ||
19 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
20 | +import org.thingsboard.server.dao.exception.IncorrectParameterException; | ||
21 | +import org.thingsboard.server.dao.service.Validator; | ||
22 | + | ||
23 | +public class AttributeUtils { | ||
24 | + public static void validate(EntityId id, String scope) { | ||
25 | + Validator.validateId(id.getId(), "Incorrect id " + id); | ||
26 | + Validator.validateString(scope, "Incorrect scope " + scope); | ||
27 | + } | ||
28 | + | ||
29 | + public static void validate(AttributeKvEntry kvEntry) { | ||
30 | + if (kvEntry == null) { | ||
31 | + throw new IncorrectParameterException("Key value entry can't be null"); | ||
32 | + } else if (kvEntry.getDataType() == null) { | ||
33 | + throw new IncorrectParameterException("Incorrect kvEntry. Data type can't be null"); | ||
34 | + } else { | ||
35 | + Validator.validateString(kvEntry.getKey(), "Incorrect kvEntry. Key can't be empty"); | ||
36 | + Validator.validatePositiveNumber(kvEntry.getLastUpdateTs(), "Incorrect last update ts. Ts should be positive"); | ||
37 | + } | ||
38 | + } | ||
39 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.attributes; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
20 | +import org.springframework.cache.Cache; | ||
21 | +import org.springframework.cache.CacheManager; | ||
22 | +import org.springframework.context.annotation.Primary; | ||
23 | +import org.springframework.stereotype.Service; | ||
24 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
25 | + | ||
26 | +import static org.thingsboard.server.common.data.CacheConstants.ATTRIBUTES_CACHE; | ||
27 | + | ||
28 | +@Service | ||
29 | +@ConditionalOnProperty(prefix = "cache.attributes", value = "enabled", havingValue = "true") | ||
30 | +@Primary | ||
31 | +@Slf4j | ||
32 | +public class AttributesCacheWrapper { | ||
33 | + private final Cache attributesCache; | ||
34 | + | ||
35 | + public AttributesCacheWrapper(CacheManager cacheManager) { | ||
36 | + this.attributesCache = cacheManager.getCache(ATTRIBUTES_CACHE); | ||
37 | + } | ||
38 | + | ||
39 | + public Cache.ValueWrapper get(AttributeCacheKey attributeCacheKey) { | ||
40 | + try { | ||
41 | + return attributesCache.get(attributeCacheKey); | ||
42 | + } catch (Exception e) { | ||
43 | + log.debug("Failed to retrieve element from cache for key {}. Reason - {}.", attributeCacheKey, e.getMessage()); | ||
44 | + return null; | ||
45 | + } | ||
46 | + } | ||
47 | + | ||
48 | + public void put(AttributeCacheKey attributeCacheKey, AttributeKvEntry attributeKvEntry) { | ||
49 | + try { | ||
50 | + attributesCache.put(attributeCacheKey, attributeKvEntry); | ||
51 | + } catch (Exception e) { | ||
52 | + log.debug("Failed to put element from cache for key {}. Reason - {}.", attributeCacheKey, e.getMessage()); | ||
53 | + } | ||
54 | + } | ||
55 | + | ||
56 | + public void evict(AttributeCacheKey attributeCacheKey) { | ||
57 | + try { | ||
58 | + attributesCache.evict(attributeCacheKey); | ||
59 | + } catch (Exception e) { | ||
60 | + log.debug("Failed to evict element from cache for key {}. Reason - {}.", attributeCacheKey, e.getMessage()); | ||
61 | + } | ||
62 | + } | ||
63 | +} |
@@ -15,31 +15,39 @@ | @@ -15,31 +15,39 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.attributes; | 16 | package org.thingsboard.server.dao.attributes; |
17 | 17 | ||
18 | -import com.google.common.collect.Lists; | ||
19 | import com.google.common.util.concurrent.Futures; | 18 | import com.google.common.util.concurrent.Futures; |
20 | import com.google.common.util.concurrent.ListenableFuture; | 19 | import com.google.common.util.concurrent.ListenableFuture; |
21 | -import org.springframework.beans.factory.annotation.Autowired; | 20 | +import lombok.extern.slf4j.Slf4j; |
21 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
22 | +import org.springframework.context.annotation.Primary; | ||
22 | import org.springframework.stereotype.Service; | 23 | import org.springframework.stereotype.Service; |
23 | import org.thingsboard.server.common.data.EntityType; | 24 | import org.thingsboard.server.common.data.EntityType; |
24 | import org.thingsboard.server.common.data.id.DeviceProfileId; | 25 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
25 | import org.thingsboard.server.common.data.id.EntityId; | 26 | import org.thingsboard.server.common.data.id.EntityId; |
26 | import org.thingsboard.server.common.data.id.TenantId; | 27 | import org.thingsboard.server.common.data.id.TenantId; |
27 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 28 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
28 | -import org.thingsboard.server.dao.exception.IncorrectParameterException; | ||
29 | import org.thingsboard.server.dao.service.Validator; | 29 | import org.thingsboard.server.dao.service.Validator; |
30 | 30 | ||
31 | import java.util.Collection; | 31 | import java.util.Collection; |
32 | import java.util.List; | 32 | import java.util.List; |
33 | import java.util.Optional; | 33 | import java.util.Optional; |
34 | +import java.util.stream.Collectors; | ||
35 | + | ||
36 | +import static org.thingsboard.server.dao.attributes.AttributeUtils.validate; | ||
34 | 37 | ||
35 | /** | 38 | /** |
36 | * @author Andrew Shvayka | 39 | * @author Andrew Shvayka |
37 | */ | 40 | */ |
38 | @Service | 41 | @Service |
42 | +@ConditionalOnProperty(prefix = "cache.attributes", value = "enabled", havingValue = "false", matchIfMissing = true) | ||
43 | +@Primary | ||
44 | +@Slf4j | ||
39 | public class BaseAttributesService implements AttributesService { | 45 | public class BaseAttributesService implements AttributesService { |
46 | + private final AttributesDao attributesDao; | ||
40 | 47 | ||
41 | - @Autowired | ||
42 | - private AttributesDao attributesDao; | 48 | + public BaseAttributesService(AttributesDao attributesDao) { |
49 | + this.attributesDao = attributesDao; | ||
50 | + } | ||
43 | 51 | ||
44 | @Override | 52 | @Override |
45 | public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey) { | 53 | public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey) { |
@@ -75,33 +83,14 @@ public class BaseAttributesService implements AttributesService { | @@ -75,33 +83,14 @@ public class BaseAttributesService implements AttributesService { | ||
75 | public ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes) { | 83 | public ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes) { |
76 | validate(entityId, scope); | 84 | validate(entityId, scope); |
77 | attributes.forEach(attribute -> validate(attribute)); | 85 | attributes.forEach(attribute -> validate(attribute)); |
78 | - List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(attributes.size()); | ||
79 | - for (AttributeKvEntry attribute : attributes) { | ||
80 | - futures.add(attributesDao.save(tenantId, entityId, scope, attribute)); | ||
81 | - } | ||
82 | - return Futures.allAsList(futures); | 86 | + |
87 | + List<ListenableFuture<Void>> saveFutures = attributes.stream().map(attribute -> attributesDao.save(tenantId, entityId, scope, attribute)).collect(Collectors.toList()); | ||
88 | + return Futures.allAsList(saveFutures); | ||
83 | } | 89 | } |
84 | 90 | ||
85 | @Override | 91 | @Override |
86 | - public ListenableFuture<List<Void>> removeAll(TenantId tenantId, EntityId entityId, String scope, List<String> keys) { | 92 | + public ListenableFuture<List<Void>> removeAll(TenantId tenantId, EntityId entityId, String scope, List<String> attributeKeys) { |
87 | validate(entityId, scope); | 93 | validate(entityId, scope); |
88 | - return attributesDao.removeAll(tenantId, entityId, scope, keys); | ||
89 | - } | ||
90 | - | ||
91 | - private static void validate(EntityId id, String scope) { | ||
92 | - Validator.validateId(id.getId(), "Incorrect id " + id); | ||
93 | - Validator.validateString(scope, "Incorrect scope " + scope); | ||
94 | - } | ||
95 | - | ||
96 | - private static void validate(AttributeKvEntry kvEntry) { | ||
97 | - if (kvEntry == null) { | ||
98 | - throw new IncorrectParameterException("Key value entry can't be null"); | ||
99 | - } else if (kvEntry.getDataType() == null) { | ||
100 | - throw new IncorrectParameterException("Incorrect kvEntry. Data type can't be null"); | ||
101 | - } else { | ||
102 | - Validator.validateString(kvEntry.getKey(), "Incorrect kvEntry. Key can't be empty"); | ||
103 | - Validator.validatePositiveNumber(kvEntry.getLastUpdateTs(), "Incorrect last update ts. Ts should be positive"); | ||
104 | - } | 94 | + return attributesDao.removeAll(tenantId, entityId, scope, attributeKeys); |
105 | } | 95 | } |
106 | - | ||
107 | } | 96 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2021 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.attributes; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.Futures; | ||
19 | +import com.google.common.util.concurrent.ListenableFuture; | ||
20 | +import com.google.common.util.concurrent.MoreExecutors; | ||
21 | +import lombok.extern.slf4j.Slf4j; | ||
22 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
23 | +import org.springframework.cache.Cache; | ||
24 | +import org.springframework.cache.CacheManager; | ||
25 | +import org.springframework.context.annotation.Primary; | ||
26 | +import org.springframework.stereotype.Service; | ||
27 | +import org.thingsboard.server.common.data.EntityType; | ||
28 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | ||
29 | +import org.thingsboard.server.common.data.id.EntityId; | ||
30 | +import org.thingsboard.server.common.data.id.TenantId; | ||
31 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
32 | +import org.thingsboard.server.common.data.kv.KvEntry; | ||
33 | +import org.thingsboard.server.common.stats.DefaultCounter; | ||
34 | +import org.thingsboard.server.common.stats.StatsFactory; | ||
35 | +import org.thingsboard.server.dao.service.Validator; | ||
36 | + | ||
37 | +import java.util.ArrayList; | ||
38 | +import java.util.Collection; | ||
39 | +import java.util.HashMap; | ||
40 | +import java.util.HashSet; | ||
41 | +import java.util.List; | ||
42 | +import java.util.Map; | ||
43 | +import java.util.Objects; | ||
44 | +import java.util.Optional; | ||
45 | +import java.util.Set; | ||
46 | +import java.util.stream.Collectors; | ||
47 | + | ||
48 | +import static org.thingsboard.server.common.data.CacheConstants.ATTRIBUTES_CACHE; | ||
49 | +import static org.thingsboard.server.dao.attributes.AttributeUtils.validate; | ||
50 | + | ||
51 | +@Service | ||
52 | +@ConditionalOnProperty(prefix = "cache.attributes", value = "enabled", havingValue = "true") | ||
53 | +@Primary | ||
54 | +@Slf4j | ||
55 | +public class CachedAttributesService implements AttributesService { | ||
56 | + private static final String STATS_NAME = "attributes.cache"; | ||
57 | + | ||
58 | + private final AttributesDao attributesDao; | ||
59 | + private final AttributesCacheWrapper cacheWrapper; | ||
60 | + private final DefaultCounter hitCounter; | ||
61 | + private final DefaultCounter missCounter; | ||
62 | + | ||
63 | + public CachedAttributesService(AttributesDao attributesDao, | ||
64 | + AttributesCacheWrapper cacheWrapper, | ||
65 | + StatsFactory statsFactory) { | ||
66 | + this.attributesDao = attributesDao; | ||
67 | + this.cacheWrapper = cacheWrapper; | ||
68 | + | ||
69 | + this.hitCounter = statsFactory.createDefaultCounter(STATS_NAME, "result", "hit"); | ||
70 | + this.missCounter = statsFactory.createDefaultCounter(STATS_NAME, "result", "miss"); | ||
71 | + } | ||
72 | + | ||
73 | + @Override | ||
74 | + public ListenableFuture<Optional<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, String attributeKey) { | ||
75 | + validate(entityId, scope); | ||
76 | + Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); | ||
77 | + | ||
78 | + AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, attributeKey); | ||
79 | + Cache.ValueWrapper cachedAttributeValue = cacheWrapper.get(attributeCacheKey); | ||
80 | + if (cachedAttributeValue != null) { | ||
81 | + hitCounter.increment(); | ||
82 | + AttributeKvEntry cachedAttributeKvEntry = (AttributeKvEntry) cachedAttributeValue.get(); | ||
83 | + return Futures.immediateFuture(Optional.ofNullable(cachedAttributeKvEntry)); | ||
84 | + } else { | ||
85 | + missCounter.increment(); | ||
86 | + ListenableFuture<Optional<AttributeKvEntry>> result = attributesDao.find(tenantId, entityId, scope, attributeKey); | ||
87 | + return Futures.transform(result, foundAttrKvEntry -> { | ||
88 | + // TODO: think if it's a good idea to store 'empty' attributes | ||
89 | + cacheWrapper.put(attributeCacheKey, foundAttrKvEntry.orElse(null)); | ||
90 | + return foundAttrKvEntry; | ||
91 | + }, MoreExecutors.directExecutor()); | ||
92 | + } | ||
93 | + } | ||
94 | + | ||
95 | + @Override | ||
96 | + public ListenableFuture<List<AttributeKvEntry>> find(TenantId tenantId, EntityId entityId, String scope, Collection<String> attributeKeys) { | ||
97 | + validate(entityId, scope); | ||
98 | + attributeKeys.forEach(attributeKey -> Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey)); | ||
99 | + | ||
100 | + Map<String, Cache.ValueWrapper> wrappedCachedAttributes = findCachedAttributes(entityId, scope, attributeKeys); | ||
101 | + | ||
102 | + List<AttributeKvEntry> cachedAttributes = wrappedCachedAttributes.values().stream() | ||
103 | + .map(wrappedCachedAttribute -> (AttributeKvEntry) wrappedCachedAttribute.get()) | ||
104 | + .filter(Objects::nonNull) | ||
105 | + .collect(Collectors.toList()); | ||
106 | + if (wrappedCachedAttributes.size() == attributeKeys.size()) { | ||
107 | + return Futures.immediateFuture(cachedAttributes); | ||
108 | + } | ||
109 | + | ||
110 | + Set<String> notFoundAttributeKeys = new HashSet<>(attributeKeys); | ||
111 | + notFoundAttributeKeys.removeAll(wrappedCachedAttributes.keySet()); | ||
112 | + | ||
113 | + ListenableFuture<List<AttributeKvEntry>> result = attributesDao.find(tenantId, entityId, scope, notFoundAttributeKeys); | ||
114 | + return Futures.transform(result, foundInDbAttributes -> mergeDbAndCacheAttributes(entityId, scope, cachedAttributes, notFoundAttributeKeys, foundInDbAttributes), MoreExecutors.directExecutor()); | ||
115 | + | ||
116 | + } | ||
117 | + | ||
118 | + private Map<String, Cache.ValueWrapper> findCachedAttributes(EntityId entityId, String scope, Collection<String> attributeKeys) { | ||
119 | + Map<String, Cache.ValueWrapper> cachedAttributes = new HashMap<>(); | ||
120 | + for (String attributeKey : attributeKeys) { | ||
121 | + Cache.ValueWrapper cachedAttributeValue = cacheWrapper.get(new AttributeCacheKey(scope, entityId, attributeKey)); | ||
122 | + if (cachedAttributeValue != null) { | ||
123 | + hitCounter.increment(); | ||
124 | + cachedAttributes.put(attributeKey, cachedAttributeValue); | ||
125 | + } else { | ||
126 | + missCounter.increment(); | ||
127 | + } | ||
128 | + } | ||
129 | + return cachedAttributes; | ||
130 | + } | ||
131 | + | ||
132 | + private List<AttributeKvEntry> mergeDbAndCacheAttributes(EntityId entityId, String scope, List<AttributeKvEntry> cachedAttributes, Set<String> notFoundAttributeKeys, List<AttributeKvEntry> foundInDbAttributes) { | ||
133 | + for (AttributeKvEntry foundInDbAttribute : foundInDbAttributes) { | ||
134 | + AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, foundInDbAttribute.getKey()); | ||
135 | + cacheWrapper.put(attributeCacheKey, foundInDbAttribute); | ||
136 | + notFoundAttributeKeys.remove(foundInDbAttribute.getKey()); | ||
137 | + } | ||
138 | + for (String key : notFoundAttributeKeys){ | ||
139 | + cacheWrapper.put(new AttributeCacheKey(scope, entityId, key), null); | ||
140 | + } | ||
141 | + List<AttributeKvEntry> mergedAttributes = new ArrayList<>(cachedAttributes); | ||
142 | + mergedAttributes.addAll(foundInDbAttributes); | ||
143 | + return mergedAttributes; | ||
144 | + } | ||
145 | + | ||
146 | + @Override | ||
147 | + public ListenableFuture<List<AttributeKvEntry>> findAll(TenantId tenantId, EntityId entityId, String scope) { | ||
148 | + validate(entityId, scope); | ||
149 | + return attributesDao.findAll(tenantId, entityId, scope); | ||
150 | + } | ||
151 | + | ||
152 | + @Override | ||
153 | + public List<String> findAllKeysByDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId) { | ||
154 | + return attributesDao.findAllKeysByDeviceProfileId(tenantId, deviceProfileId); | ||
155 | + } | ||
156 | + | ||
157 | + @Override | ||
158 | + public List<String> findAllKeysByEntityIds(TenantId tenantId, EntityType entityType, List<EntityId> entityIds) { | ||
159 | + return attributesDao.findAllKeysByEntityIds(tenantId, entityType, entityIds); | ||
160 | + } | ||
161 | + | ||
162 | + @Override | ||
163 | + public ListenableFuture<List<Void>> save(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes) { | ||
164 | + validate(entityId, scope); | ||
165 | + attributes.forEach(AttributeUtils::validate); | ||
166 | + | ||
167 | + List<ListenableFuture<Void>> saveFutures = attributes.stream().map(attribute -> attributesDao.save(tenantId, entityId, scope, attribute)).collect(Collectors.toList()); | ||
168 | + ListenableFuture<List<Void>> future = Futures.allAsList(saveFutures); | ||
169 | + | ||
170 | + // TODO: can do if (attributesCache.get() != null) attributesCache.put() instead, but will be more twice more requests to cache | ||
171 | + List<String> attributeKeys = attributes.stream().map(KvEntry::getKey).collect(Collectors.toList()); | ||
172 | + future.addListener(() -> evictAttributesFromCache(tenantId, entityId, scope, attributeKeys), MoreExecutors.directExecutor()); | ||
173 | + return future; | ||
174 | + } | ||
175 | + | ||
176 | + @Override | ||
177 | + public ListenableFuture<List<Void>> removeAll(TenantId tenantId, EntityId entityId, String scope, List<String> attributeKeys) { | ||
178 | + validate(entityId, scope); | ||
179 | + ListenableFuture<List<Void>> future = attributesDao.removeAll(tenantId, entityId, scope, attributeKeys); | ||
180 | + future.addListener(() -> evictAttributesFromCache(tenantId, entityId, scope, attributeKeys), MoreExecutors.directExecutor()); | ||
181 | + return future; | ||
182 | + } | ||
183 | + | ||
184 | + private void evictAttributesFromCache(TenantId tenantId, EntityId entityId, String scope, List<String> attributeKeys) { | ||
185 | + try { | ||
186 | + for (String attributeKey : attributeKeys) { | ||
187 | + cacheWrapper.evict(new AttributeCacheKey(scope, entityId, attributeKey)); | ||
188 | + } | ||
189 | + } catch (Exception e) { | ||
190 | + log.error("[{}][{}] Failed to remove values from cache.", tenantId, entityId, e); | ||
191 | + } | ||
192 | + } | ||
193 | +} |
@@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; | @@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; | ||
35 | import org.thingsboard.server.common.data.relation.EntityRelationInfo; | 35 | import org.thingsboard.server.common.data.relation.EntityRelationInfo; |
36 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; | 36 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; |
37 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; | 37 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
38 | -import org.thingsboard.server.common.data.relation.EntityTypeFilter; | 38 | +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; |
39 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; | 39 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
40 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; | 40 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; |
41 | import org.thingsboard.server.dao.entity.EntityService; | 41 | import org.thingsboard.server.dao.entity.EntityService; |
@@ -457,7 +457,7 @@ public class BaseRelationService implements RelationService { | @@ -457,7 +457,7 @@ public class BaseRelationService implements RelationService { | ||
457 | //boolean fetchLastLevelOnly = true; | 457 | //boolean fetchLastLevelOnly = true; |
458 | log.trace("Executing findByQuery [{}]", query); | 458 | log.trace("Executing findByQuery [{}]", query); |
459 | RelationsSearchParameters params = query.getParameters(); | 459 | RelationsSearchParameters params = query.getParameters(); |
460 | - final List<EntityTypeFilter> filters = query.getFilters(); | 460 | + final List<RelationEntityTypeFilter> filters = query.getFilters(); |
461 | if (filters == null || filters.isEmpty()) { | 461 | if (filters == null || filters.isEmpty()) { |
462 | log.debug("Filters are not set [{}]", query); | 462 | log.debug("Filters are not set [{}]", query); |
463 | } | 463 | } |
@@ -575,8 +575,8 @@ public class BaseRelationService implements RelationService { | @@ -575,8 +575,8 @@ public class BaseRelationService implements RelationService { | ||
575 | }; | 575 | }; |
576 | } | 576 | } |
577 | 577 | ||
578 | - private boolean matchFilters(List<EntityTypeFilter> filters, EntityRelation relation, EntitySearchDirection direction) { | ||
579 | - for (EntityTypeFilter filter : filters) { | 578 | + private boolean matchFilters(List<RelationEntityTypeFilter> filters, EntityRelation relation, EntitySearchDirection direction) { |
579 | + for (RelationEntityTypeFilter filter : filters) { | ||
580 | if (match(filter, relation, direction)) { | 580 | if (match(filter, relation, direction)) { |
581 | return true; | 581 | return true; |
582 | } | 582 | } |
@@ -584,7 +584,7 @@ public class BaseRelationService implements RelationService { | @@ -584,7 +584,7 @@ public class BaseRelationService implements RelationService { | ||
584 | return false; | 584 | return false; |
585 | } | 585 | } |
586 | 586 | ||
587 | - private boolean match(EntityTypeFilter filter, EntityRelation relation, EntitySearchDirection direction) { | 587 | + private boolean match(RelationEntityTypeFilter filter, EntityRelation relation, EntitySearchDirection direction) { |
588 | if (StringUtils.isEmpty(filter.getRelationType()) || filter.getRelationType().equals(relation.getType())) { | 588 | if (StringUtils.isEmpty(filter.getRelationType()) || filter.getRelationType().equals(relation.getType())) { |
589 | if (filter.getEntityTypes() == null || filter.getEntityTypes().isEmpty()) { | 589 | if (filter.getEntityTypes() == null || filter.getEntityTypes().isEmpty()) { |
590 | return true; | 590 | return true; |
@@ -40,12 +40,13 @@ import org.thingsboard.server.common.data.query.EntityKeyType; | @@ -40,12 +40,13 @@ import org.thingsboard.server.common.data.query.EntityKeyType; | ||
40 | import org.thingsboard.server.common.data.query.EntityListFilter; | 40 | import org.thingsboard.server.common.data.query.EntityListFilter; |
41 | import org.thingsboard.server.common.data.query.EntityNameFilter; | 41 | import org.thingsboard.server.common.data.query.EntityNameFilter; |
42 | import org.thingsboard.server.common.data.query.EntitySearchQueryFilter; | 42 | import org.thingsboard.server.common.data.query.EntitySearchQueryFilter; |
43 | +import org.thingsboard.server.common.data.query.EntityTypeFilter; | ||
43 | import org.thingsboard.server.common.data.query.EntityViewSearchQueryFilter; | 44 | import org.thingsboard.server.common.data.query.EntityViewSearchQueryFilter; |
44 | import org.thingsboard.server.common.data.query.EntityViewTypeFilter; | 45 | import org.thingsboard.server.common.data.query.EntityViewTypeFilter; |
45 | import org.thingsboard.server.common.data.query.RelationsQueryFilter; | 46 | import org.thingsboard.server.common.data.query.RelationsQueryFilter; |
46 | import org.thingsboard.server.common.data.query.SingleEntityFilter; | 47 | import org.thingsboard.server.common.data.query.SingleEntityFilter; |
47 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; | 48 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
48 | -import org.thingsboard.server.common.data.relation.EntityTypeFilter; | 49 | +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; |
49 | 50 | ||
50 | import java.util.Arrays; | 51 | import java.util.Arrays; |
51 | import java.util.Collections; | 52 | import java.util.Collections; |
@@ -249,18 +250,70 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -249,18 +250,70 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
249 | public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { | 250 | public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { |
250 | EntityType entityType = resolveEntityType(query.getEntityFilter()); | 251 | EntityType entityType = resolveEntityType(query.getEntityFilter()); |
251 | QueryContext ctx = new QueryContext(new QuerySecurityContext(tenantId, customerId, entityType)); | 252 | QueryContext ctx = new QueryContext(new QuerySecurityContext(tenantId, customerId, entityType)); |
252 | - ctx.append("select count(e.id) from "); | ||
253 | - ctx.append(addEntityTableQuery(ctx, query.getEntityFilter())); | ||
254 | - ctx.append(" e where "); | ||
255 | - ctx.append(buildEntityWhere(ctx, query.getEntityFilter(), Collections.emptyList())); | ||
256 | - return transactionTemplate.execute(status -> { | ||
257 | - long startTs = System.currentTimeMillis(); | ||
258 | - try { | ||
259 | - return jdbcTemplate.queryForObject(ctx.getQuery(), ctx, Long.class); | ||
260 | - } finally { | ||
261 | - queryLog.logQuery(ctx, ctx.getQuery(), System.currentTimeMillis() - startTs); | 253 | + if (query.getKeyFilters() == null || query.getKeyFilters().isEmpty()) { |
254 | + ctx.append("select count(e.id) from "); | ||
255 | + ctx.append(addEntityTableQuery(ctx, query.getEntityFilter())); | ||
256 | + ctx.append(" e where "); | ||
257 | + ctx.append(buildEntityWhere(ctx, query.getEntityFilter(), Collections.emptyList())); | ||
258 | + return transactionTemplate.execute(status -> { | ||
259 | + long startTs = System.currentTimeMillis(); | ||
260 | + try { | ||
261 | + return jdbcTemplate.queryForObject(ctx.getQuery(), ctx, Long.class); | ||
262 | + } finally { | ||
263 | + queryLog.logQuery(ctx, ctx.getQuery(), System.currentTimeMillis() - startTs); | ||
264 | + } | ||
265 | + }); | ||
266 | + } else { | ||
267 | + List<EntityKeyMapping> mappings = EntityKeyMapping.prepareEntityCountKeyMapping(query); | ||
268 | + | ||
269 | + List<EntityKeyMapping> selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection) | ||
270 | + .collect(Collectors.toList()); | ||
271 | + List<EntityKeyMapping> entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest()) | ||
272 | + .collect(Collectors.toList()); | ||
273 | + | ||
274 | + List<EntityKeyMapping> filterMapping = mappings.stream().filter(EntityKeyMapping::hasFilter) | ||
275 | + .collect(Collectors.toList()); | ||
276 | + List<EntityKeyMapping> entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest()) | ||
277 | + .collect(Collectors.toList()); | ||
278 | + | ||
279 | + List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest) | ||
280 | + .collect(Collectors.toList()); | ||
281 | + | ||
282 | + | ||
283 | + String entityWhereClause = DefaultEntityQueryRepository.this.buildEntityWhere(ctx, query.getEntityFilter(), entityFieldsFiltersMapping); | ||
284 | + String latestJoinsCnt = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings, true); | ||
285 | + String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType); | ||
286 | + String entityTypeStr; | ||
287 | + if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) { | ||
288 | + entityTypeStr = "e.entity_type"; | ||
289 | + } else { | ||
290 | + entityTypeStr = "'" + entityType.name() + "'"; | ||
262 | } | 291 | } |
263 | - }); | 292 | + if (!StringUtils.isEmpty(entityFieldsSelection)) { |
293 | + entityFieldsSelection = String.format("e.id id, %s entity_type, %s", entityTypeStr, entityFieldsSelection); | ||
294 | + } else { | ||
295 | + entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr); | ||
296 | + } | ||
297 | + | ||
298 | + String fromClauseCount = String.format("from (select %s from (select %s from %s e where %s) entities %s ) result %s", | ||
299 | + "entities.*", | ||
300 | + entityFieldsSelection, | ||
301 | + addEntityTableQuery(ctx, query.getEntityFilter()), | ||
302 | + entityWhereClause, | ||
303 | + latestJoinsCnt, | ||
304 | + ""); | ||
305 | + | ||
306 | + String countQuery = String.format("select count(id) %s", fromClauseCount); | ||
307 | + | ||
308 | + return transactionTemplate.execute(status -> { | ||
309 | + long startTs = System.currentTimeMillis(); | ||
310 | + try { | ||
311 | + return jdbcTemplate.queryForObject(countQuery, ctx, Long.class); | ||
312 | + } finally { | ||
313 | + queryLog.logQuery(ctx, ctx.getQuery(), System.currentTimeMillis() - startTs); | ||
314 | + } | ||
315 | + }); | ||
316 | + } | ||
264 | } | 317 | } |
265 | 318 | ||
266 | @Override | 319 | @Override |
@@ -436,6 +489,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -436,6 +489,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
436 | case ASSET_SEARCH_QUERY: | 489 | case ASSET_SEARCH_QUERY: |
437 | case ENTITY_VIEW_SEARCH_QUERY: | 490 | case ENTITY_VIEW_SEARCH_QUERY: |
438 | case API_USAGE_STATE: | 491 | case API_USAGE_STATE: |
492 | + case ENTITY_TYPE: | ||
439 | return ""; | 493 | return ""; |
440 | default: | 494 | default: |
441 | throw new RuntimeException("Not implemented!"); | 495 | throw new RuntimeException("Not implemented!"); |
@@ -521,7 +575,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -521,7 +575,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
521 | boolean single = entityFilter.getFilters() != null && entityFilter.getFilters().size() == 1; | 575 | boolean single = entityFilter.getFilters() != null && entityFilter.getFilters().size() == 1; |
522 | if (entityFilter.getFilters() != null && !entityFilter.getFilters().isEmpty()) { | 576 | if (entityFilter.getFilters() != null && !entityFilter.getFilters().isEmpty()) { |
523 | int entityTypeFilterIdx = 0; | 577 | int entityTypeFilterIdx = 0; |
524 | - for (EntityTypeFilter etf : entityFilter.getFilters()) { | 578 | + for (RelationEntityTypeFilter etf : entityFilter.getFilters()) { |
525 | String etfCondition = buildEtfCondition(ctx, etf, entityFilter.getDirection(), entityTypeFilterIdx++); | 579 | String etfCondition = buildEtfCondition(ctx, etf, entityFilter.getDirection(), entityTypeFilterIdx++); |
526 | if (!etfCondition.isEmpty()) { | 580 | if (!etfCondition.isEmpty()) { |
527 | if (noConditions) { | 581 | if (noConditions) { |
@@ -570,7 +624,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -570,7 +624,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
570 | return "( " + selectFields + from + ")"; | 624 | return "( " + selectFields + from + ")"; |
571 | } | 625 | } |
572 | 626 | ||
573 | - private String buildEtfCondition(QueryContext ctx, EntityTypeFilter etf, EntitySearchDirection direction, int entityTypeFilterIdx) { | 627 | + private String buildEtfCondition(QueryContext ctx, RelationEntityTypeFilter etf, EntitySearchDirection direction, int entityTypeFilterIdx) { |
574 | StringBuilder whereFilter = new StringBuilder(); | 628 | StringBuilder whereFilter = new StringBuilder(); |
575 | String relationType = etf.getRelationType(); | 629 | String relationType = etf.getRelationType(); |
576 | List<EntityType> entityTypes = etf.getEntityTypes(); | 630 | List<EntityType> entityTypes = etf.getEntityTypes(); |
@@ -676,6 +730,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | @@ -676,6 +730,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { | ||
676 | return ((EntityListFilter) entityFilter).getEntityType(); | 730 | return ((EntityListFilter) entityFilter).getEntityType(); |
677 | case ENTITY_NAME: | 731 | case ENTITY_NAME: |
678 | return ((EntityNameFilter) entityFilter).getEntityType(); | 732 | return ((EntityNameFilter) entityFilter).getEntityType(); |
733 | + case ENTITY_TYPE: | ||
734 | + return ((EntityTypeFilter) entityFilter).getEntityType(); | ||
679 | case ASSET_TYPE: | 735 | case ASSET_TYPE: |
680 | case ASSET_SEARCH_QUERY: | 736 | case ASSET_SEARCH_QUERY: |
681 | return EntityType.ASSET; | 737 | return EntityType.ASSET; |
@@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.DataConstants; | @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.DataConstants; | ||
21 | import org.thingsboard.server.common.data.EntityType; | 21 | import org.thingsboard.server.common.data.EntityType; |
22 | import org.thingsboard.server.common.data.query.BooleanFilterPredicate; | 22 | import org.thingsboard.server.common.data.query.BooleanFilterPredicate; |
23 | import org.thingsboard.server.common.data.query.ComplexFilterPredicate; | 23 | import org.thingsboard.server.common.data.query.ComplexFilterPredicate; |
24 | +import org.thingsboard.server.common.data.query.EntityCountQuery; | ||
24 | import org.thingsboard.server.common.data.query.EntityDataQuery; | 25 | import org.thingsboard.server.common.data.query.EntityDataQuery; |
25 | import org.thingsboard.server.common.data.query.EntityDataSortOrder; | 26 | import org.thingsboard.server.common.data.query.EntityDataSortOrder; |
26 | import org.thingsboard.server.common.data.query.EntityFilter; | 27 | import org.thingsboard.server.common.data.query.EntityFilter; |
@@ -380,6 +381,30 @@ public class EntityKeyMapping { | @@ -380,6 +381,30 @@ public class EntityKeyMapping { | ||
380 | return mappings; | 381 | return mappings; |
381 | } | 382 | } |
382 | 383 | ||
384 | + public static List<EntityKeyMapping> prepareEntityCountKeyMapping(EntityCountQuery query) { | ||
385 | + Map<EntityKey, List<KeyFilter>> filters = | ||
386 | + query.getKeyFilters() != null ? | ||
387 | + query.getKeyFilters().stream().collect(Collectors.groupingBy(KeyFilter::getKey)) : Collections.emptyMap(); | ||
388 | + int index = 2; | ||
389 | + List<EntityKeyMapping> mappings = new ArrayList<>(); | ||
390 | + if (!filters.isEmpty()) { | ||
391 | + for (EntityKey filterField : filters.keySet()) { | ||
392 | + EntityKeyMapping mapping = new EntityKeyMapping(); | ||
393 | + mapping.setIndex(index); | ||
394 | + mapping.setAlias(String.format("alias%s", index)); | ||
395 | + mapping.setKeyFilters(filters.get(filterField)); | ||
396 | + mapping.setLatest(!filterField.getType().equals(EntityKeyType.ENTITY_FIELD)); | ||
397 | + mapping.setSelection(false); | ||
398 | + mapping.setEntityKey(filterField); | ||
399 | + mappings.add(mapping); | ||
400 | + index += 1; | ||
401 | + } | ||
402 | + } | ||
403 | + | ||
404 | + return mappings; | ||
405 | + } | ||
406 | + | ||
407 | + | ||
383 | private String buildAttributeSelection() { | 408 | private String buildAttributeSelection() { |
384 | return buildTimeSeriesOrAttrSelection(true); | 409 | return buildTimeSeriesOrAttrSelection(true); |
385 | } | 410 | } |
@@ -19,8 +19,10 @@ import com.google.common.util.concurrent.Futures; | @@ -19,8 +19,10 @@ import com.google.common.util.concurrent.Futures; | ||
19 | import com.google.common.util.concurrent.ListenableFuture; | 19 | import com.google.common.util.concurrent.ListenableFuture; |
20 | import com.google.common.util.concurrent.MoreExecutors; | 20 | import com.google.common.util.concurrent.MoreExecutors; |
21 | import lombok.extern.slf4j.Slf4j; | 21 | import lombok.extern.slf4j.Slf4j; |
22 | +import org.hibernate.exception.ConstraintViolationException; | ||
22 | import org.springframework.beans.factory.annotation.Autowired; | 23 | import org.springframework.beans.factory.annotation.Autowired; |
23 | import org.springframework.beans.factory.annotation.Value; | 24 | import org.springframework.beans.factory.annotation.Value; |
25 | +import org.springframework.dao.DataIntegrityViolationException; | ||
24 | import org.springframework.stereotype.Component; | 26 | import org.springframework.stereotype.Component; |
25 | import org.thingsboard.server.common.data.id.EntityId; | 27 | import org.thingsboard.server.common.data.id.EntityId; |
26 | import org.thingsboard.server.common.data.id.TenantId; | 28 | import org.thingsboard.server.common.data.id.TenantId; |
@@ -114,6 +116,14 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa | @@ -114,6 +116,14 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa | ||
114 | partitioningRepository.save(psqlPartition); | 116 | partitioningRepository.save(psqlPartition); |
115 | log.trace("Adding partition to Set: {}", psqlPartition); | 117 | log.trace("Adding partition to Set: {}", psqlPartition); |
116 | partitions.put(psqlPartition.getStart(), psqlPartition); | 118 | partitions.put(psqlPartition.getStart(), psqlPartition); |
119 | + } catch (DataIntegrityViolationException ex) { | ||
120 | + log.trace("Error occurred during partition save:", ex); | ||
121 | + if (ex.getCause() instanceof ConstraintViolationException) { | ||
122 | + log.warn("Saving partition [{}] rejected. Timeseries data will save to the ts_kv_indefinite (DEFAULT) partition.", psqlPartition.getPartitionDate()); | ||
123 | + partitions.put(psqlPartition.getStart(), psqlPartition); | ||
124 | + } else { | ||
125 | + throw new RuntimeException(ex); | ||
126 | + } | ||
117 | } finally { | 127 | } finally { |
118 | partitionCreationLock.unlock(); | 128 | partitionCreationLock.unlock(); |
119 | } | 129 | } |
@@ -15,9 +15,6 @@ | @@ -15,9 +15,6 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.dao.service; | 16 | package org.thingsboard.server.dao.service; |
17 | 17 | ||
18 | -import com.fasterxml.jackson.core.JsonProcessingException; | ||
19 | -import com.fasterxml.jackson.databind.JsonMappingException; | ||
20 | -import com.fasterxml.jackson.databind.ObjectMapper; | ||
21 | import com.google.common.util.concurrent.Futures; | 18 | import com.google.common.util.concurrent.Futures; |
22 | import com.google.common.util.concurrent.ListenableFuture; | 19 | import com.google.common.util.concurrent.ListenableFuture; |
23 | import org.junit.After; | 20 | import org.junit.After; |
@@ -31,25 +28,48 @@ import org.thingsboard.server.common.data.Device; | @@ -31,25 +28,48 @@ import org.thingsboard.server.common.data.Device; | ||
31 | import org.thingsboard.server.common.data.EntityType; | 28 | import org.thingsboard.server.common.data.EntityType; |
32 | import org.thingsboard.server.common.data.Tenant; | 29 | import org.thingsboard.server.common.data.Tenant; |
33 | import org.thingsboard.server.common.data.asset.Asset; | 30 | import org.thingsboard.server.common.data.asset.Asset; |
34 | -import org.thingsboard.server.common.data.id.*; | ||
35 | -import org.thingsboard.server.common.data.kv.*; | 31 | +import org.thingsboard.server.common.data.id.CustomerId; |
32 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
33 | +import org.thingsboard.server.common.data.id.EntityId; | ||
34 | +import org.thingsboard.server.common.data.id.TenantId; | ||
35 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
36 | +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | ||
37 | +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | ||
38 | +import org.thingsboard.server.common.data.kv.DoubleDataEntry; | ||
39 | +import org.thingsboard.server.common.data.kv.KvEntry; | ||
40 | +import org.thingsboard.server.common.data.kv.LongDataEntry; | ||
41 | +import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
36 | import org.thingsboard.server.common.data.page.PageData; | 42 | import org.thingsboard.server.common.data.page.PageData; |
37 | -import org.thingsboard.server.common.data.query.*; | 43 | +import org.thingsboard.server.common.data.query.AssetSearchQueryFilter; |
44 | +import org.thingsboard.server.common.data.query.DeviceSearchQueryFilter; | ||
45 | +import org.thingsboard.server.common.data.query.DeviceTypeFilter; | ||
46 | +import org.thingsboard.server.common.data.query.EntityCountQuery; | ||
47 | +import org.thingsboard.server.common.data.query.EntityData; | ||
48 | +import org.thingsboard.server.common.data.query.EntityDataPageLink; | ||
49 | +import org.thingsboard.server.common.data.query.EntityDataQuery; | ||
50 | +import org.thingsboard.server.common.data.query.EntityDataSortOrder; | ||
51 | +import org.thingsboard.server.common.data.query.EntityKey; | ||
52 | +import org.thingsboard.server.common.data.query.EntityKeyType; | ||
53 | +import org.thingsboard.server.common.data.query.EntityListFilter; | ||
54 | +import org.thingsboard.server.common.data.query.FilterPredicateValue; | ||
55 | +import org.thingsboard.server.common.data.query.KeyFilter; | ||
56 | +import org.thingsboard.server.common.data.query.NumericFilterPredicate; | ||
57 | +import org.thingsboard.server.common.data.query.RelationsQueryFilter; | ||
58 | +import org.thingsboard.server.common.data.query.StringFilterPredicate; | ||
38 | import org.thingsboard.server.common.data.relation.EntityRelation; | 59 | import org.thingsboard.server.common.data.relation.EntityRelation; |
39 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; | 60 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
40 | -import org.thingsboard.server.common.data.relation.EntityTypeFilter; | 61 | +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; |
41 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; | 62 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
42 | -import org.thingsboard.server.common.data.rule.RuleChain; | ||
43 | -import org.thingsboard.server.common.data.rule.RuleChainMetaData; | ||
44 | -import org.thingsboard.server.common.data.rule.RuleNode; | ||
45 | import org.thingsboard.server.dao.attributes.AttributesService; | 63 | import org.thingsboard.server.dao.attributes.AttributesService; |
46 | import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; | 64 | import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; |
47 | -import org.thingsboard.server.dao.rule.RuleChainService; | ||
48 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 65 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
49 | -import org.thingsboard.server.dao.util.DaoTestUtil; | ||
50 | -import org.thingsboard.server.dao.util.SqlDbType; | ||
51 | 66 | ||
52 | -import java.util.*; | 67 | +import java.util.ArrayList; |
68 | +import java.util.Arrays; | ||
69 | +import java.util.Collections; | ||
70 | +import java.util.Comparator; | ||
71 | +import java.util.List; | ||
72 | +import java.util.Random; | ||
53 | import java.util.concurrent.ExecutionException; | 73 | import java.util.concurrent.ExecutionException; |
54 | import java.util.stream.Collectors; | 74 | import java.util.stream.Collectors; |
55 | 75 | ||
@@ -140,13 +160,13 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { | @@ -140,13 +160,13 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { | ||
140 | long count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); | 160 | long count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
141 | Assert.assertEquals(30, count); | 161 | Assert.assertEquals(30, count); |
142 | 162 | ||
143 | - filter.setFilters(Collections.singletonList(new EntityTypeFilter("Contains", Collections.singletonList(EntityType.DEVICE)))); | 163 | + filter.setFilters(Collections.singletonList(new RelationEntityTypeFilter("Contains", Collections.singletonList(EntityType.DEVICE)))); |
144 | count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); | 164 | count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
145 | Assert.assertEquals(25, count); | 165 | Assert.assertEquals(25, count); |
146 | 166 | ||
147 | filter.setRootEntity(devices.get(0).getId()); | 167 | filter.setRootEntity(devices.get(0).getId()); |
148 | filter.setDirection(EntitySearchDirection.TO); | 168 | filter.setDirection(EntitySearchDirection.TO); |
149 | - filter.setFilters(Collections.singletonList(new EntityTypeFilter("Manages", Collections.singletonList(EntityType.TENANT)))); | 169 | + filter.setFilters(Collections.singletonList(new RelationEntityTypeFilter("Manages", Collections.singletonList(EntityType.TENANT)))); |
150 | count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); | 170 | count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
151 | Assert.assertEquals(1, count); | 171 | Assert.assertEquals(1, count); |
152 | 172 | ||
@@ -208,7 +228,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { | @@ -208,7 +228,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { | ||
208 | RelationsQueryFilter filter = new RelationsQueryFilter(); | 228 | RelationsQueryFilter filter = new RelationsQueryFilter(); |
209 | filter.setRootEntity(tenantId); | 229 | filter.setRootEntity(tenantId); |
210 | filter.setDirection(EntitySearchDirection.FROM); | 230 | filter.setDirection(EntitySearchDirection.FROM); |
211 | - filter.setFilters(Collections.singletonList(new EntityTypeFilter("Contains", Collections.singletonList(EntityType.DEVICE)))); | 231 | + filter.setFilters(Collections.singletonList(new RelationEntityTypeFilter("Contains", Collections.singletonList(EntityType.DEVICE)))); |
212 | 232 | ||
213 | EntityDataSortOrder sortOrder = new EntityDataSortOrder( | 233 | EntityDataSortOrder sortOrder = new EntityDataSortOrder( |
214 | new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC | 234 | new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC |
@@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.DeviceId; | @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.DeviceId; | ||
26 | import org.thingsboard.server.common.data.relation.EntityRelation; | 26 | import org.thingsboard.server.common.data.relation.EntityRelation; |
27 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; | 27 | import org.thingsboard.server.common.data.relation.EntityRelationsQuery; |
28 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; | 28 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
29 | -import org.thingsboard.server.common.data.relation.EntityTypeFilter; | 29 | +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; |
30 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; | 30 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
31 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; | 31 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; |
32 | import org.thingsboard.server.dao.exception.DataValidationException; | 32 | import org.thingsboard.server.dao.exception.DataValidationException; |
@@ -221,7 +221,7 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { | @@ -221,7 +221,7 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { | ||
221 | 221 | ||
222 | EntityRelationsQuery query = new EntityRelationsQuery(); | 222 | EntityRelationsQuery query = new EntityRelationsQuery(); |
223 | query.setParameters(new RelationsSearchParameters(assetA, EntitySearchDirection.FROM, -1, false)); | 223 | query.setParameters(new RelationsSearchParameters(assetA, EntitySearchDirection.FROM, -1, false)); |
224 | - query.setFilters(Collections.singletonList(new EntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.singletonList(EntityType.ASSET)))); | 224 | + query.setFilters(Collections.singletonList(new RelationEntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.singletonList(EntityType.ASSET)))); |
225 | List<EntityRelation> relations = relationService.findByQuery(SYSTEM_TENANT_ID, query).get(); | 225 | List<EntityRelation> relations = relationService.findByQuery(SYSTEM_TENANT_ID, query).get(); |
226 | Assert.assertEquals(3, relations.size()); | 226 | Assert.assertEquals(3, relations.size()); |
227 | Assert.assertTrue(relations.contains(relationA)); | 227 | Assert.assertTrue(relations.contains(relationA)); |
@@ -255,7 +255,7 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { | @@ -255,7 +255,7 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { | ||
255 | 255 | ||
256 | EntityRelationsQuery query = new EntityRelationsQuery(); | 256 | EntityRelationsQuery query = new EntityRelationsQuery(); |
257 | query.setParameters(new RelationsSearchParameters(assetA, EntitySearchDirection.FROM, -1, false)); | 257 | query.setParameters(new RelationsSearchParameters(assetA, EntitySearchDirection.FROM, -1, false)); |
258 | - query.setFilters(Collections.singletonList(new EntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.singletonList(EntityType.ASSET)))); | 258 | + query.setFilters(Collections.singletonList(new RelationEntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.singletonList(EntityType.ASSET)))); |
259 | List<EntityRelation> relations = relationService.findByQuery(SYSTEM_TENANT_ID, query).get(); | 259 | List<EntityRelation> relations = relationService.findByQuery(SYSTEM_TENANT_ID, query).get(); |
260 | Assert.assertEquals(2, relations.size()); | 260 | Assert.assertEquals(2, relations.size()); |
261 | Assert.assertTrue(relations.contains(relationAB)); | 261 | Assert.assertTrue(relations.contains(relationAB)); |
@@ -21,7 +21,7 @@ | @@ -21,7 +21,7 @@ | ||
21 | 21 | ||
22 | <parent> | 22 | <parent> |
23 | <groupId>org.thingsboard</groupId> | 23 | <groupId>org.thingsboard</groupId> |
24 | - <version>3.3.0-SNAPSHOT</version> | 24 | + <version>3.2.2-SNAPSHOT</version> |
25 | <artifactId>msa</artifactId> | 25 | <artifactId>msa</artifactId> |
26 | </parent> | 26 | </parent> |
27 | <groupId>org.thingsboard.msa</groupId> | 27 | <groupId>org.thingsboard.msa</groupId> |
1 | { | 1 | { |
2 | "name": "thingsboard-js-executor", | 2 | "name": "thingsboard-js-executor", |
3 | "private": true, | 3 | "private": true, |
4 | - "version": "3.3.0", | 4 | + "version": "3.2.2", |
5 | "description": "ThingsBoard JavaScript Executor Microservice", | 5 | "description": "ThingsBoard JavaScript Executor Microservice", |
6 | "main": "server.js", | 6 | "main": "server.js", |
7 | "bin": "server.js", | 7 | "bin": "server.js", |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>msa</artifactId> | 24 | <artifactId>msa</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa</groupId> | 26 | <groupId>org.thingsboard.msa</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>msa</artifactId> | 26 | <artifactId>msa</artifactId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>msa</artifactId> | 24 | <artifactId>msa</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa</groupId> | 26 | <groupId>org.thingsboard.msa</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>msa</artifactId> | 24 | <artifactId>msa</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa</groupId> | 26 | <groupId>org.thingsboard.msa</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.msa</groupId> | 22 | <groupId>org.thingsboard.msa</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa.transport</groupId> | 26 | <groupId>org.thingsboard.msa.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.msa</groupId> | 22 | <groupId>org.thingsboard.msa</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa.transport</groupId> | 26 | <groupId>org.thingsboard.msa.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard.msa</groupId> | 22 | <groupId>org.thingsboard.msa</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>transport</artifactId> | 24 | <artifactId>transport</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa.transport</groupId> | 26 | <groupId>org.thingsboard.msa.transport</groupId> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>msa</artifactId> | 24 | <artifactId>msa</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa</groupId> | 26 | <groupId>org.thingsboard.msa</groupId> |
1 | { | 1 | { |
2 | "name": "thingsboard-web-ui", | 2 | "name": "thingsboard-web-ui", |
3 | "private": true, | 3 | "private": true, |
4 | - "version": "3.3.0", | 4 | + "version": "3.2.2", |
5 | "description": "ThingsBoard Web UI Microservice", | 5 | "description": "ThingsBoard Web UI Microservice", |
6 | "main": "server.js", | 6 | "main": "server.js", |
7 | "bin": "server.js", | 7 | "bin": "server.js", |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>msa</artifactId> | 24 | <artifactId>msa</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <groupId>org.thingsboard.msa</groupId> | 26 | <groupId>org.thingsboard.msa</groupId> |
@@ -19,11 +19,11 @@ | @@ -19,11 +19,11 @@ | ||
19 | <modelVersion>4.0.0</modelVersion> | 19 | <modelVersion>4.0.0</modelVersion> |
20 | <parent> | 20 | <parent> |
21 | <groupId>org.thingsboard</groupId> | 21 | <groupId>org.thingsboard</groupId> |
22 | - <version>3.3.0-SNAPSHOT</version> | 22 | + <version>3.2.2-SNAPSHOT</version> |
23 | <artifactId>thingsboard</artifactId> | 23 | <artifactId>thingsboard</artifactId> |
24 | </parent> | 24 | </parent> |
25 | <artifactId>netty-mqtt</artifactId> | 25 | <artifactId>netty-mqtt</artifactId> |
26 | - <version>3.3.0-SNAPSHOT</version> | 26 | + <version>3.2.2-SNAPSHOT</version> |
27 | <packaging>jar</packaging> | 27 | <packaging>jar</packaging> |
28 | 28 | ||
29 | <name>Netty MQTT Client</name> | 29 | <name>Netty MQTT Client</name> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <groupId>org.thingsboard</groupId> | 21 | <groupId>org.thingsboard</groupId> |
22 | <artifactId>thingsboard</artifactId> | 22 | <artifactId>thingsboard</artifactId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <packaging>pom</packaging> | 24 | <packaging>pom</packaging> |
25 | 25 | ||
26 | <name>Thingsboard</name> | 26 | <name>Thingsboard</name> |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>rest-client</artifactId> | 26 | <artifactId>rest-client</artifactId> |
@@ -2207,7 +2207,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | @@ -2207,7 +2207,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | ||
2207 | Map<String, String> params = new HashMap<>(); | 2207 | Map<String, String> params = new HashMap<>(); |
2208 | addPageLinkToParam(params, pageLink); | 2208 | addPageLinkToParam(params, pageLink); |
2209 | return restTemplate.exchange( | 2209 | return restTemplate.exchange( |
2210 | - baseURL + "/api/tenantProfiles" + getUrlParams(pageLink), | 2210 | + baseURL + "/api/tenantProfiles?" + getUrlParams(pageLink), |
2211 | HttpMethod.GET, | 2211 | HttpMethod.GET, |
2212 | HttpEntity.EMPTY, | 2212 | HttpEntity.EMPTY, |
2213 | new ParameterizedTypeReference<PageData<TenantProfile>>() { | 2213 | new ParameterizedTypeReference<PageData<TenantProfile>>() { |
@@ -2218,7 +2218,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | @@ -2218,7 +2218,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | ||
2218 | Map<String, String> params = new HashMap<>(); | 2218 | Map<String, String> params = new HashMap<>(); |
2219 | addPageLinkToParam(params, pageLink); | 2219 | addPageLinkToParam(params, pageLink); |
2220 | return restTemplate.exchange( | 2220 | return restTemplate.exchange( |
2221 | - baseURL + "/api/tenantProfileInfos" + getUrlParams(pageLink), | 2221 | + baseURL + "/api/tenantProfileInfos?" + getUrlParams(pageLink), |
2222 | HttpMethod.GET, | 2222 | HttpMethod.GET, |
2223 | HttpEntity.EMPTY, | 2223 | HttpEntity.EMPTY, |
2224 | new ParameterizedTypeReference<PageData<EntityInfo>>() { | 2224 | new ParameterizedTypeReference<PageData<EntityInfo>>() { |
@@ -2275,7 +2275,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | @@ -2275,7 +2275,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { | ||
2275 | Map<String, String> params = new HashMap<>(); | 2275 | Map<String, String> params = new HashMap<>(); |
2276 | addPageLinkToParam(params, pageLink); | 2276 | addPageLinkToParam(params, pageLink); |
2277 | return restTemplate.exchange( | 2277 | return restTemplate.exchange( |
2278 | - baseURL + "/api/users" + getUrlParams(pageLink), | 2278 | + baseURL + "/api/users?" + getUrlParams(pageLink), |
2279 | HttpMethod.GET, | 2279 | HttpMethod.GET, |
2280 | HttpEntity.EMPTY, | 2280 | HttpEntity.EMPTY, |
2281 | new ParameterizedTypeReference<PageData<User>>() { | 2281 | new ParameterizedTypeReference<PageData<User>>() { |
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | <modelVersion>4.0.0</modelVersion> | 20 | <modelVersion>4.0.0</modelVersion> |
21 | <parent> | 21 | <parent> |
22 | <groupId>org.thingsboard</groupId> | 22 | <groupId>org.thingsboard</groupId> |
23 | - <version>3.3.0-SNAPSHOT</version> | 23 | + <version>3.2.2-SNAPSHOT</version> |
24 | <artifactId>thingsboard</artifactId> | 24 | <artifactId>thingsboard</artifactId> |
25 | </parent> | 25 | </parent> |
26 | <artifactId>rule-engine</artifactId> | 26 | <artifactId>rule-engine</artifactId> |