diff --git a/application/src/main/data/json/system/widget_types/unread_notifications.json b/application/src/main/data/json/system/widget_types/unread_notifications.json index 7e3e6b0477..ed7a8492ad 100644 --- a/application/src/main/data/json/system/widget_types/unread_notifications.json +++ b/application/src/main/data/json/system/widget_types/unread_notifications.json @@ -17,7 +17,7 @@ "settingsDirective": "tb-unread-notification-widget-settings", "hasBasicMode": true, "basicModeDirective": "tb-unread-notification-basic-config", - "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0\",\"settings\":{\"cardHtml\":\"
HTML code here
\",\"cardCss\":\".card {\\n font-weight: bold;\\n font-size: 32px;\\n color: #999;\\n width: 100%;\\n height: 100%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\",\"maxNotificationDisplay\":6,\"showCounter\":true,\"counterValueFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"\"},\"counterValueColor\":\"#fff\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"enableViewAll\":true,\"enableFilter\":true,\"enableMarkAsRead\":true},\"title\":\"Unread notification\",\"dropShadow\":true,\"configMode\":\"basic\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"#000000\",\"showTitleIcon\":true,\"iconSize\":\"22px\",\"titleIcon\":\"notifications\",\"iconColor\":\"#000000\",\"actions\":{},\"enableFullscreen\":false,\"borderRadius\":\"4px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" + "defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0\",\"settings\":{\"cardHtml\":\"
HTML code here
\",\"cardCss\":\".card {\\n font-weight: bold;\\n font-size: 32px;\\n color: #999;\\n width: 100%;\\n height: 100%;\\n display: flex;\\n align-items: center;\\n justify-content: center;\\n}\",\"maxNotificationDisplay\":6,\"showCounter\":true,\"counterValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"\"},\"counterValueColor\":\"#fff\",\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"enableViewAll\":true,\"enableFilter\":true,\"enableMarkAsRead\":true},\"title\":\"Unread notification\",\"dropShadow\":true,\"configMode\":\"basic\",\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"#000000\",\"showTitleIcon\":true,\"iconSize\":\"22px\",\"titleIcon\":\"notifications\",\"iconColor\":\"#000000\",\"actions\":{},\"enableFullscreen\":false,\"borderRadius\":\"4px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" }, "tags": null } diff --git a/application/src/main/data/json/system/widget_types/update_server_string_attribute.json b/application/src/main/data/json/system/widget_types/update_server_string_attribute.json index 193300cb06..d8c33bffcc 100644 --- a/application/src/main/data/json/system/widget_types/update_server_string_attribute.json +++ b/application/src/main/data/json/system/widget_types/update_server_string_attribute.json @@ -11,7 +11,7 @@ "resources": [], "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n \n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", + "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [];\n if (utils.isDefinedAndNotNull(settings.minLength)) {\n validators.push($scope.validators.minLength(settings.minLength));\n }\n if (utils.isDefinedAndNotNull(settings.maxLength)) {\n validators.push($scope.validators.maxLength(settings.maxLength));\n }\n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n \n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SERVER_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", "settingsSchema": "", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-update-string-attribute-widget-settings", diff --git a/application/src/main/data/json/system/widget_types/update_shared_string_attribute.json b/application/src/main/data/json/system/widget_types/update_shared_string_attribute.json index 4c5333ec59..7578838066 100644 --- a/application/src/main/data/json/system/widget_types/update_shared_string_attribute.json +++ b/application/src/main/data/json/system/widget_types/update_shared_string_attribute.json @@ -11,7 +11,7 @@ "resources": [], "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n
\n {{ 'widgets.input-widgets.no-attribute-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.timeseries-not-allowed' | translate }}\n
\n
\n
\n
", "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [$scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)];\n \n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", + "controllerScript": "let $scope;\nlet settings;\nlet attributeService;\nlet utils;\nlet translate;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n attributeService = $scope.$injector.get(self.ctx.servicesMap.get('attributeService'));\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n settings.isRequired = utils.defaultValue(settings.isRequired, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false; \n $scope.message = translate.instant('widgets.input-widgets.no-entity-selected');\n \n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-attribute-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [];\n if (utils.isDefinedAndNotNull(settings.minLength)) {\n validators.push($scope.validators.minLength(settings.minLength));\n }\n if (utils.isDefinedAndNotNull(settings.maxLength)) {\n validators.push($scope.validators.maxLength(settings.maxLength));\n }\n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n \n $scope.attributeUpdateFormGroup = $scope.fb.group({\n currentValue: [undefined, validators]\n });\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType === 'DEVICE') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n \n $scope.entityDetected = true;\n }\n } else {\n $scope.message = translate.instant('widgets.input-widgets.not-allowed-entity');\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"attribute\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n var value = $scope.attributeUpdateFormGroup.get('currentValue').value;\n \n if (!$scope.attributeUpdateFormGroup.get('currentValue').value.length) {\n value = null;\n }\n\n attributeService.saveEntityAttributes(\n datasource.entity.id,\n 'SHARED_SCOPE',\n [\n {\n key: $scope.currentKey,\n value\n }\n ]\n ).subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n}\n\nself.onDataUpdated = function() {\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}", "settingsSchema": "", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-update-string-attribute-widget-settings", diff --git a/application/src/main/data/json/system/widget_types/update_string_timeseries.json b/application/src/main/data/json/system/widget_types/update_string_timeseries.json index 43a96d6f69..d54224ef36 100644 --- a/application/src/main/data/json/system/widget_types/update_string_timeseries.json +++ b/application/src/main/data/json/system/widget_types/update_string_timeseries.json @@ -11,7 +11,7 @@ "resources": [], "templateHtml": "
\n
\n
\n
\n
\n \n {{ settings.showLabel ? labelValue : '' }}\n \n \n {{requiredErrorMessage}}\n \n \n
\n \n
\n \n \n
\n
\n \n
\n {{ 'widgets.input-widgets.no-entity-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.no-timeseries-selected' | translate }}\n
\n
\n {{ 'widgets.input-widgets.attribute-not-allowed' | translate }}\n
\n
\n
\n
", "templateCss": ".attribute-update-form {\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.attribute-update-form__grid {\n display: flex;\n}\n.grid__element:first-child {\n flex: 1;\n}\n.grid__element:last-child {\n margin-top: 19px;\n margin-left: 7px;\n}\n.grid__element {\n display: flex;\n}\n\n.attribute-update-form .mat-button.mat-icon-button {\n width: 32px;\n min-width: 32px;\n height: 32px;\n min-height: 32px;\n padding: 0 !important;\n margin: 0 !important;\n line-height: 20px;\n}\n\n.attribute-update-form .mat-icon-button mat-icon {\n width: 20px;\n min-width: 20px;\n height: 20px;\n min-height: 20px;\n font-size: 20px;\n}\n\n.tb-toast {\n font-size: 14px!important;\n}", - "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, [$scope.validators.required,\n $scope.validators.minLength(settings.minLength),\n $scope.validators.maxLength(settings.maxLength)]]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}\n", + "controllerScript": "let $scope;\nlet settings;\nlet utils;\nlet translate;\nlet http;\n\nself.onInit = function() {\n self.ctx.ngZone.run(function() {\n init(); \n self.ctx.detectChanges(true);\n });\n};\n\n\nfunction init() {\n\n $scope = self.ctx.$scope;\n utils = $scope.$injector.get(self.ctx.servicesMap.get('utils'));\n translate = $scope.$injector.get(self.ctx.servicesMap.get('translate'));\n http = $scope.$injector.get(self.ctx.servicesMap.get('http'));\n $scope.toastTargetId = 'input-widget' + utils.guid();\n settings = utils.deepClone(self.ctx.settings) || {};\n settings.showLabel = utils.defaultValue(settings.showLabel, true);\n settings.showResultMessage = utils.defaultValue(settings.showResultMessage, true);\n $scope.settings = settings;\n $scope.isValidParameter = true;\n $scope.dataKeyDetected = false;\n $scope.requiredErrorMessage = utils.customTranslation(settings.requiredErrorMessage, settings.requiredErrorMessage) || translate.instant('widgets.input-widgets.entity-timeseries-required');\n $scope.labelValue = utils.customTranslation(settings.labelValue, settings.labelValue) || translate.instant('widgets.input-widgets.value');\n \n var validators = [];\n if (utils.isDefinedAndNotNull(settings.minLength)) {\n validators.push($scope.validators.minLength(settings.minLength));\n }\n if (utils.isDefinedAndNotNull(settings.maxLength)) {\n validators.push($scope.validators.maxLength(settings.maxLength));\n }\n if (settings.isRequired) {\n validators.push($scope.validators.required);\n }\n\n $scope.attributeUpdateFormGroup = $scope.fb.group(\n {currentValue: [undefined, validators]}\n );\n\n if (self.ctx.datasources && self.ctx.datasources.length) {\n var datasource = self.ctx.datasources[0];\n if (datasource.type === 'entity') {\n if (datasource.entityType && datasource.entityId) {\n $scope.entityName = datasource.entityName;\n if (settings.widgetTitle && settings.widgetTitle.length) {\n $scope.titleTemplate = utils.customTranslation(settings.widgetTitle, settings.widgetTitle);\n } else {\n $scope.titleTemplate = self.ctx.widgetConfig.title;\n }\n\n $scope.entityDetected = true;\n }\n }\n if (datasource.dataKeys.length) {\n if (datasource.dataKeys[0].type !== \"timeseries\") {\n $scope.isValidParameter = false;\n } else {\n $scope.currentKey = datasource.dataKeys[0].name;\n $scope.dataKeyType = datasource.dataKeys[0].type;\n $scope.dataKeyDetected = true;\n }\n }\n }\n\n self.ctx.widgetTitle = utils.createLabelFromDatasource(self.ctx.datasources[0], $scope.titleTemplate);\n\n $scope.updateAttribute = function () {\n $scope.isFocused = false;\n if ($scope.entityDetected) {\n var datasource = self.ctx.datasources[0];\n\n let observable = saveEntityTimeseries(\n datasource.entityType,\n datasource.entityId,\n [\n {\n key: $scope.currentKey,\n value: $scope.attributeUpdateFormGroup.get('currentValue').value\n }\n ]\n );\n if (observable) {\n observable.subscribe(\n function success() {\n $scope.originalValue = $scope.attributeUpdateFormGroup.get('currentValue').value;\n if (settings.showResultMessage) {\n $scope.showSuccessToast(translate.instant('widgets.input-widgets.update-successful'), 1000, 'bottom', 'left', $scope.toastTargetId);\n }\n },\n function fail() {\n if (settings.showResultMessage) {\n $scope.showErrorToast(translate.instant('widgets.input-widgets.update-failed'), 'bottom', 'left', $scope.toastTargetId);\n }\n }\n );\n }\n }\n };\n\n $scope.changeFocus = function () {\n if ($scope.attributeUpdateFormGroup.get('currentValue').value === $scope.originalValue) {\n $scope.isFocused = false;\n }\n }\n\n function saveEntityTimeseries(entityType, entityId, telemetries) {\n var telemetriesData = {};\n for (var a = 0; a < telemetries.length; a++) {\n if (typeof telemetries[a].value !== 'undefined' && telemetries[a].value !== null) {\n telemetriesData[telemetries[a].key] = telemetries[a].value;\n }\n }\n if (Object.keys(telemetriesData).length) {\n var url = '/api/plugins/telemetry/' + entityType + '/' + entityId + '/timeseries/scope';\n return http.post(url, telemetriesData);\n }\n return null;\n }\n}\n\nself.onDataUpdated = function() {\n\n try {\n if ($scope.dataKeyDetected) {\n if (!$scope.isFocused) {\n $scope.originalValue = self.ctx.data[0].data[0][1];\n $scope.attributeUpdateFormGroup.get('currentValue').patchValue($scope.originalValue);\n self.ctx.detectChanges();\n }\n }\n } catch (e) {\n console.log(e);\n }\n}\n\nself.onResize = function() {\n\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true\n }\n}\n\nself.onDestroy = function() {\n\n}\n", "settingsSchema": "", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-update-string-attribute-widget-settings", diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java index a2ac9d0cdc..4536cb6228 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActor.java @@ -16,9 +16,6 @@ package org.thingsboard.server.actors.device; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; -import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; -import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.TbActorCtx; import org.thingsboard.server.actors.TbActorException; @@ -26,10 +23,13 @@ import org.thingsboard.server.actors.service.ContextAwareActor; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.TbActorMsg; -import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponseActorMsg; import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequestActorMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg; +import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg; +import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; @Slf4j diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 7432038096..b34ddca1af 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -74,6 +74,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheE import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto; +import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseReason; import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; @@ -90,7 +91,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCre import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.gen.transport.TransportProtos.UplinkNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseReason; import org.thingsboard.server.service.rpc.RpcSubmitStrategy; import org.thingsboard.server.service.state.DefaultDeviceStateService; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 0dc9d68984..ca404fc78a 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -106,10 +106,8 @@ import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.queue.TbQueueCallback; -import org.thingsboard.server.queue.TbQueueMsgMetadata; -import org.thingsboard.server.service.executors.PubSubRuleNodeExecutorProvider; import org.thingsboard.server.queue.common.SimpleTbQueueCallback; +import org.thingsboard.server.service.executors.PubSubRuleNodeExecutorProvider; import org.thingsboard.server.service.script.RuleNodeJsScriptEngine; import org.thingsboard.server.service.script.RuleNodeTbelScriptEngine; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java index bae9216446..f05fdb1fb9 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleEngineComponentActor.java @@ -50,6 +50,9 @@ public abstract class RuleEngineComponentActor urls = List.of("/api/plugins/rpc/**", "/api/rpc/**"); + private final AntPathMatcher pathMatcher = new AntPathMatcher(); + private final ThingsboardErrorResponseHandler errorResponseHandler; + + @Value("${transport.http.max_payload_size:65536}") + private int maxPayloadSize; + + @Override + public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + if (request.getContentLength() > maxPayloadSize) { + if (log.isDebugEnabled()) { + log.debug("Too large payload size. Url: {}, client ip: {}, content length: {}", request.getRequestURL(), + request.getRemoteAddr(), request.getContentLength()); + } + errorResponseHandler.handle(new MaxPayloadSizeExceededException(), response); + return; + } + chain.doFilter(request, response); + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + for (String url : urls) { + if (pathMatcher.match(url, request.getRequestURI())) { + return false; + } + } + return true; + } + + @Override + protected boolean shouldNotFilterAsyncDispatch() { + return false; + } + + @Override + protected boolean shouldNotFilterErrorDispatch() { + return false; + } +} diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java index a3111cb137..77ad1a1b9e 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -124,6 +124,9 @@ public class ThingsboardSecurityConfiguration { @Autowired private RateLimitProcessingFilter rateLimitProcessingFilter; + @Autowired + private RequestSizeFilter requestSizeFilter; + @Bean protected FilterRegistrationBean buildEtagFilter() throws Exception { ShallowEtagHeaderFilter etagFilter = new ShallowEtagHeaderFilter(); @@ -225,6 +228,7 @@ public class ThingsboardSecurityConfiguration { .addFilterBefore(buildRestPublicLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class) .addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(requestSizeFilter, UsernamePasswordAuthenticationFilter.class) .addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class); if (oauth2Configuration != null) { http.oauth2Login(login -> login diff --git a/application/src/main/java/org/thingsboard/server/config/WebConfig.java b/application/src/main/java/org/thingsboard/server/config/WebConfig.java index 70afc8b99c..5d4830c552 100644 --- a/application/src/main/java/org/thingsboard/server/config/WebConfig.java +++ b/application/src/main/java/org/thingsboard/server/config/WebConfig.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.config; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.thingsboard.server.utils.MiscUtils; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @Controller diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java index 15918f0a08..5865c96d33 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java @@ -16,11 +16,8 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 535700335d..9b786a8d3e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -17,12 +17,9 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 7b4621db28..c54f5b0013 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -18,13 +18,10 @@ package org.thingsboard.server.controller; import com.google.common.util.concurrent.ListenableFuture; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java index a4dc7a1cb0..4b40cf3854 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java @@ -16,13 +16,10 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -40,8 +37,8 @@ import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.config.annotations.ApiOperation; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.asset.profile.TbAssetProfileService; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java index c8ea4073b1..3db2813086 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java @@ -16,10 +16,7 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index f9d1fe7a2f..b723603e8a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -141,6 +141,7 @@ import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.dao.widget.WidgetsBundleService; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.action.EntityActionService; @@ -314,6 +315,9 @@ public abstract class BaseController { @Autowired protected ExportableEntitiesService entitiesService; + @Autowired + protected TbServiceInfoProvider serviceInfoProvider; + @Value("${server.log_controller_error_stack_trace}") @Getter private boolean logControllerErrorStackTrace; diff --git a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java index 918021ab95..e4b919a616 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java +++ b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java @@ -1712,4 +1712,6 @@ public class ControllerConstants { MARKDOWN_CODE_BLOCK_START + "[{\"ts\":1634712287000,\"values\":{\"temperature\":26, \"humidity\":87}}, {\"ts\":1634712588000,\"values\":{\"temperature\":25, \"humidity\":88}}]" + MARKDOWN_CODE_BLOCK_END ; + + protected static final String SECURITY_WRITE_CHECK = " Security check is performed to verify that the user has 'WRITE' permission for the entity (entities)."; } diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 013575361d..2949df35a7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -25,7 +25,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -51,8 +50,8 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.config.annotations.ApiOperation; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.dashboard.TbDashboardService; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 416991a996..02baf7f411 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -21,14 +21,12 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.annotation.Nullable; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; @@ -79,7 +77,6 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import jakarta.validation.Valid; import java.util.ArrayList; import java.util.List; import java.util.UUID; diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index b84e801d31..54049a808e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -16,14 +16,11 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -42,8 +39,8 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.config.annotations.ApiOperation; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.device.profile.TbDeviceProfileService; diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java index 37015de22a..ccf29301d8 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java @@ -16,11 +16,8 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java index 783b451a6d..73ba0a053d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java @@ -17,13 +17,10 @@ package org.thingsboard.server.controller; import com.google.common.util.concurrent.ListenableFuture; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java index 372d724912..6945278064 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EventController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java @@ -16,12 +16,9 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java index 25e49eb198..719767ab43 100644 --- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java +++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java @@ -16,12 +16,9 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java index ee7bfcc0dc..58e8b3138d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java @@ -18,7 +18,6 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ByteArrayResource; @@ -52,7 +51,6 @@ import org.thingsboard.server.service.security.permission.Resource; import java.io.IOException; -import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_ID_PARAM_DESCRIPTION; import static org.thingsboard.server.controller.ControllerConstants.OTA_PACKAGE_DESCRIPTION; diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java index 6c08f4a2e2..67ff1b3fc5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java @@ -46,10 +46,10 @@ import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rpc.RpcStatus; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; import org.thingsboard.server.config.annotations.ApiOperation; import org.thingsboard.server.exception.ToErrorResponseEntity; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg; import org.thingsboard.server.service.security.permission.Operation; import java.util.UUID; @@ -118,6 +118,7 @@ public class RpcV2Controller extends AbstractRpcController { @ApiResponse(responseCode = "200", description = "Persistent RPC request was saved to the database or lightweight RPC request was sent to the device."), @ApiResponse(responseCode = "400", description = "Invalid structure of the request."), @ApiResponse(responseCode = "401", description = "User is not authorized to send the RPC request. Most likely, User belongs to different Customer or Tenant."), + @ApiResponse(responseCode = "413", description = "Request payload is too large"), @ApiResponse(responseCode = "504", description = "Timeout to process the RPC call. Most likely, device is offline."), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @@ -136,6 +137,7 @@ public class RpcV2Controller extends AbstractRpcController { @ApiResponse(responseCode = "200", description = "Persistent RPC request was saved to the database or lightweight RPC response received."), @ApiResponse(responseCode = "400", description = "Invalid structure of the request."), @ApiResponse(responseCode = "401", description = "User is not authorized to send the RPC request. Most likely, User belongs to different Customer or Tenant."), + @ApiResponse(responseCode = "413", description = "Request payload is too large"), @ApiResponse(responseCode = "504", description = "Timeout to process the RPC call. Most likely, device is offline."), }) @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java b/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java new file mode 100644 index 0000000000..8cda3d2212 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/RuleEngineController.java @@ -0,0 +1,237 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import com.google.common.util.concurrent.FutureCallback; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.annotation.Nullable; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.async.DeferredResult; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.config.annotations.ApiOperation; +import org.thingsboard.server.exception.ToErrorResponseEntity; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.ruleengine.RuleEngineCallService; +import org.thingsboard.server.service.security.AccessValidator; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; + +import java.util.HashMap; +import java.util.UUID; +import java.util.concurrent.TimeoutException; + +import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID_PARAM_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.ENTITY_TYPE_PARAM_DESCRIPTION; + +@RestController +@TbCoreComponent +@RequestMapping(TbUrlConstants.RULE_ENGINE_URL_PREFIX) +@Slf4j +public class RuleEngineController extends BaseController { + public static final int DEFAULT_TIMEOUT = 10000; + private static final String MSG_DESCRIPTION_PREFIX = "Creates the Message with type 'REST_API_REQUEST' and payload taken from the request body. "; + private static final String MSG_DESCRIPTION = "This method allows you to extend the regular platform API with the power of Rule Engine. You may use default and custom rule nodes to handle the message. " + + "The generated message contains two important metadata fields:\n\n" + + " * **'serviceId'** to identify the platform server that received the request;\n" + + " * **'requestUUID'** to identify the request and route possible response from the Rule Engine;\n\n" + + "Use **'rest call reply'** rule node to push the reply from rule engine back as a REST API call response. "; + + @Autowired + private RuleEngineCallService ruleEngineCallService; + @Autowired + private AccessValidator accessValidator; + + @ApiOperation(value = "Push user message to the rule engine (handleRuleEngineRequest)", + notes = MSG_DESCRIPTION_PREFIX + + "Uses current User Id ( the one which credentials is used to perform the request) as the Rule Engine message originator. " + + MSG_DESCRIPTION + + "The default timeout of the request processing is 10 seconds." + + "\n\n" + ControllerConstants.SECURITY_WRITE_CHECK) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/", method = RequestMethod.POST) + @ResponseBody + public DeferredResult handleRuleEngineRequest( + @Parameter(description = "A JSON value representing the message.", required = true) + @RequestBody String requestBody) throws ThingsboardException { + return handleRuleEngineRequest(null, null, null, DEFAULT_TIMEOUT, requestBody); + } + + @ApiOperation(value = "Push entity message to the rule engine (handleRuleEngineRequest)", + notes = MSG_DESCRIPTION_PREFIX + + "Uses specified Entity Id as the Rule Engine message originator. " + + MSG_DESCRIPTION + + "The default timeout of the request processing is 10 seconds." + + "\n\n" + ControllerConstants.SECURITY_WRITE_CHECK) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}", method = RequestMethod.POST) + @ResponseBody + public DeferredResult handleRuleEngineRequest( + @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) + @PathVariable("entityType") String entityType, + @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) + @PathVariable("entityId") String entityIdStr, + @Parameter(description = "A JSON value representing the message.", required = true) + @RequestBody String requestBody) throws ThingsboardException { + return handleRuleEngineRequest(entityType, entityIdStr, null, DEFAULT_TIMEOUT, requestBody); + } + + @ApiOperation(value = "Push entity message with timeout to the rule engine (handleRuleEngineRequest)", + notes = MSG_DESCRIPTION_PREFIX + + "Uses specified Entity Id as the Rule Engine message originator. " + + MSG_DESCRIPTION + + "The platform expects the timeout value in milliseconds." + + "\n\n" + ControllerConstants.SECURITY_WRITE_CHECK) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/{timeout}", method = RequestMethod.POST) + @ResponseBody + public DeferredResult handleRuleEngineRequest( + @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) + @PathVariable("entityType") String entityType, + @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) + @PathVariable("entityId") String entityIdStr, + @Parameter(description = "Timeout to process the request in milliseconds", required = true) + @PathVariable("timeout") int timeout, + @Parameter(description = "A JSON value representing the message.", required = true) + @RequestBody String requestBody) throws ThingsboardException { + return handleRuleEngineRequest(entityType, entityIdStr, null, timeout, requestBody); + } + + @ApiOperation(value = "Push entity message with timeout and specified queue to the rule engine (handleRuleEngineRequest)", + notes = MSG_DESCRIPTION_PREFIX + + "Uses specified Entity Id as the Rule Engine message originator. " + + MSG_DESCRIPTION + + "If request sent for Device/Device Profile or Asset/Asset Profile entity, specified queue will be used instead of the queue selected in the device or asset profile. " + + "The platform expects the timeout value in milliseconds." + + "\n\n" + ControllerConstants.SECURITY_WRITE_CHECK) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/{entityType}/{entityId}/{queueName}/{timeout}", method = RequestMethod.POST) + @ResponseBody + public DeferredResult handleRuleEngineRequest( + @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) + @PathVariable("entityType") String entityType, + @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) + @PathVariable("entityId") String entityIdStr, + @Parameter(description = "Queue name to process the request in the rule engine", required = true) + @PathVariable("queueName") String queueName, + @Parameter(description = "Timeout to process the request in milliseconds", required = true) + @PathVariable("timeout") int timeout, + @Parameter(description = "A JSON value representing the message.", required = true) + @RequestBody String requestBody) throws ThingsboardException { + try { + SecurityUser currentUser = getCurrentUser(); + EntityId entityId; + if (StringUtils.isEmpty(entityType) || StringUtils.isEmpty(entityIdStr)) { + entityId = currentUser.getId(); + } else { + entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); + } + //Check that this is a valid JSON + JacksonUtil.toJsonNode(requestBody); + final DeferredResult response = new DeferredResult<>(); + accessValidator.validate(currentUser, Operation.WRITE, entityId, new HttpValidationCallback(response, new FutureCallback>() { + @Override + public void onSuccess(@Nullable DeferredResult result) { + long expTime = System.currentTimeMillis() + timeout; + HashMap metaData = new HashMap<>(); + UUID requestId = UUID.randomUUID(); + metaData.put("serviceId", serviceInfoProvider.getServiceId()); + metaData.put("requestUUID", requestId.toString()); + metaData.put("expirationTime", Long.toString(expTime)); + TbMsg msg = TbMsg.newMsg(queueName, TbMsgType.REST_API_REQUEST, entityId, currentUser.getCustomerId(), new TbMsgMetaData(metaData), requestBody); + ruleEngineCallService.processRestApiCallToRuleEngine(currentUser.getTenantId(), requestId, msg, queueName != null, + reply -> reply(new LocalRequestMetaData(msg, currentUser, result), reply)); + } + + @Override + public void onFailure(Throwable e) { + ResponseEntity entity; + if (e instanceof ToErrorResponseEntity) { + entity = ((ToErrorResponseEntity) e).toErrorResponseEntity(); + } else { + entity = new ResponseEntity(HttpStatus.UNAUTHORIZED); + } + logRuleEngineCall(currentUser, entityId, requestBody, null, e); + response.setResult(entity); + } + })); + return response; + } catch (IllegalArgumentException iae) { + throw new ThingsboardException("Invalid request body", iae, ThingsboardErrorCode.BAD_REQUEST_PARAMS); + } + } + + private void reply(LocalRequestMetaData rpcRequest, TbMsg response) { + DeferredResult responseWriter = rpcRequest.responseWriter(); + if (response == null) { + logRuleEngineCall(rpcRequest, null, new TimeoutException("Processing timeout detected!")); + responseWriter.setResult(new ResponseEntity<>(HttpStatus.REQUEST_TIMEOUT)); + } else { + String responseData = response.getData(); + if (!StringUtils.isEmpty(responseData)) { + try { + logRuleEngineCall(rpcRequest, response, null); + responseWriter.setResult(new ResponseEntity<>(JacksonUtil.toJsonNode(responseData), HttpStatus.OK)); + } catch (IllegalArgumentException e) { + log.debug("Failed to decode device response: {}", responseData, e); + logRuleEngineCall(rpcRequest, response, e); + responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_ACCEPTABLE)); + } + } else { + logRuleEngineCall(rpcRequest, response, null); + responseWriter.setResult(new ResponseEntity<>(HttpStatus.OK)); + } + } + } + + private void logRuleEngineCall(LocalRequestMetaData rpcRequest, TbMsg response, Throwable e) { + logRuleEngineCall(rpcRequest.user(), rpcRequest.request().getOriginator(), rpcRequest.request().getData(), response, e); + } + + private void logRuleEngineCall(SecurityUser user, EntityId entityId, String request, TbMsg response, Throwable e) { + auditLogService.logEntityAction( + user.getTenantId(), + user.getCustomerId(), + user.getId(), + user.getName(), + entityId, + null, + ActionType.REST_API_RULE_ENGINE_CALL, + BaseController.toException(e), + request, + response != null ? response.getData() : ""); + } + + private record LocalRequestMetaData(TbMsg request, SecurityUser user, DeferredResult responseWriter) {} +} diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 74bc087abf..8feb898a65 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -17,9 +17,7 @@ package org.thingsboard.server.controller; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; -import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; diff --git a/application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java b/application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java index 99d94522ac..74575fd9d5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbUrlConstants.java @@ -22,4 +22,5 @@ public class TbUrlConstants { public static final String TELEMETRY_URL_PREFIX = "/api/plugins/telemetry"; public static final String RPC_V1_URL_PREFIX = "/api/plugins/rpc"; public static final String RPC_V2_URL_PREFIX = "/api/rpc"; + public static final String RULE_ENGINE_URL_PREFIX = "/api/rule-engine/"; } diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java index 4c48dbd434..649a58f22e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetTypeController.java @@ -19,7 +19,6 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; diff --git a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java index f652e78b85..ecd63561f4 100644 --- a/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java +++ b/application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java @@ -36,8 +36,8 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.widget.WidgetsBundle; -import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.config.annotations.ApiOperation; +import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.widgets.bundle.TbWidgetsBundleService; import org.thingsboard.server.service.security.permission.Operation; diff --git a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java index f386cc8e42..c37381c585 100644 --- a/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java +++ b/application/src/main/java/org/thingsboard/server/controller/plugin/TbWebSocketHandler.java @@ -39,6 +39,7 @@ import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.adapter.NativeWebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.id.CustomerId; @@ -48,7 +49,6 @@ import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.config.WebSocketConfiguration; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; -import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider; import org.thingsboard.server.service.security.exception.JwtExpiredTokenException; diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index d097beb4c2..fd73a424a3 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -46,6 +46,7 @@ import org.springframework.web.util.WebUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.msg.tools.MaxPayloadSizeExceededException; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; import org.thingsboard.server.service.security.exception.JwtExpiredTokenException; @@ -146,6 +147,8 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand handleAccessDeniedException(response); } else if (exception instanceof AuthenticationException) { handleAuthenticationException((AuthenticationException) exception, response); + } else if (exception instanceof MaxPayloadSizeExceededException) { + handleMaxPayloadSizeExceededException(response, (MaxPayloadSizeExceededException) exception); } else { response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of(exception.getMessage(), @@ -184,6 +187,13 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS)); } + private void handleMaxPayloadSizeExceededException(HttpServletResponse response, MaxPayloadSizeExceededException exception) throws IOException { + response.setStatus(HttpStatus.PAYLOAD_TOO_LARGE.value()); + JacksonUtil.writeValue(response.getWriter(), + ThingsboardErrorResponse.of(exception.getMessage(), + ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.PAYLOAD_TOO_LARGE)); + } + private void handleSubscriptionException(ThingsboardException subscriptionException, HttpServletResponse response) throws IOException { response.setStatus(HttpStatus.FORBIDDEN.value()); JacksonUtil.writeValue(response.getWriter(), diff --git a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java index 63ee2cb9eb..67d1e40ffa 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.component; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -41,7 +42,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.dao.component.ComponentDescriptorService; -import jakarta.annotation.PostConstruct; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; diff --git a/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java b/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java index 973ce17574..b3f9faa711 100644 --- a/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java +++ b/application/src/main/java/org/thingsboard/server/service/component/ComponentDiscoveryService.java @@ -19,9 +19,7 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.rule.RuleChainType; -import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java index 11974d2ebc..3de4c95cc3 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmApiCallResult; import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.alarm.EntityAlarm; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; @@ -194,7 +195,7 @@ public class EdgeEventSourcingListener { } break; case ALARM: - if (entity instanceof AlarmApiCallResult || entity instanceof Alarm) { + if (entity instanceof AlarmApiCallResult || entity instanceof Alarm || entity instanceof EntityAlarm) { return false; } break; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java index f7c19773ae..b14e605b15 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/customer/DefaultTbCustomerService.java @@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; @Service diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java index 685eda0858..ee8b2d7e47 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java @@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardService; -import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java index 74061ee9e5..bb855d527a 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/tenant/DefaultTbTenantService.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.entitiy.tenant; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.beans.factory.annotation.Value; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.TenantId; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java index 68595bed2e..299d499daa 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/DefaultUserService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.entitiy.user; +import jakarta.servlet.http.HttpServletRequest; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -32,8 +33,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import org.thingsboard.server.service.security.system.SystemSecurityService; -import jakarta.servlet.http.HttpServletRequest; - import static org.thingsboard.server.controller.UserController.ACTIVATE_URL_PATTERN; @Service diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java index ae6b40e0fe..388db9df40 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/user/TbUserService.java @@ -15,13 +15,12 @@ */ package org.thingsboard.server.service.entitiy.user; +import jakarta.servlet.http.HttpServletRequest; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; -import jakarta.servlet.http.HttpServletRequest; - public interface TbUserService { User save(TenantId tenantId, CustomerId customerId, User tbUser, boolean sendActivationMail, HttpServletRequest request, User user) throws ThingsboardException; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java index fab6029886..76027f3621 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/widgets/type/DefaultWidgetTypeService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.service.entitiy.widgets.type; import lombok.AllArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.StringUtils; @@ -25,7 +24,6 @@ import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; -import org.thingsboard.server.dao.resource.ImageService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; diff --git a/application/src/main/java/org/thingsboard/server/service/executors/SharedEventLoopGroupService.java b/application/src/main/java/org/thingsboard/server/service/executors/SharedEventLoopGroupService.java index d6d4d8f626..9bc62fbdab 100644 --- a/application/src/main/java/org/thingsboard/server/service/executors/SharedEventLoopGroupService.java +++ b/application/src/main/java/org/thingsboard/server/service/executors/SharedEventLoopGroupService.java @@ -17,11 +17,11 @@ package org.thingsboard.server.service.executors; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Getter; import org.springframework.stereotype.Component; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.concurrent.TimeUnit; @Component diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsUnassignTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsUnassignTaskProcessor.java index 3ba6ecde52..ba596da500 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsUnassignTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsUnassignTaskProcessor.java @@ -18,10 +18,10 @@ package org.thingsboard.server.service.housekeeper.processor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.housekeeper.AlarmsUnassignHousekeeperTask; +import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType; -import org.thingsboard.server.common.data.housekeeper.AlarmsUnassignHousekeeperTask; import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AttributesDeletionTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AttributesDeletionTaskProcessor.java index 5514921e2e..337f5ed73c 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AttributesDeletionTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AttributesDeletionTaskProcessor.java @@ -18,9 +18,9 @@ package org.thingsboard.server.service.housekeeper.processor; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.common.data.housekeeper.HousekeeperTask; import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType; +import org.thingsboard.server.dao.attributes.AttributesService; @Component @RequiredArgsConstructor diff --git a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EventsDeletionTaskProcessor.java b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EventsDeletionTaskProcessor.java index bf9c528eb7..4b4865ea56 100644 --- a/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EventsDeletionTaskProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EventsDeletionTaskProcessor.java @@ -17,9 +17,9 @@ package org.thingsboard.server.service.housekeeper.processor; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; -import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.common.data.housekeeper.HousekeeperTask; import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType; +import org.thingsboard.server.dao.event.EventService; @Component @RequiredArgsConstructor diff --git a/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraTsLatestToSqlMigrateService.java b/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraTsLatestToSqlMigrateService.java index 9cad1d614f..b2ea8f9f59 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraTsLatestToSqlMigrateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraTsLatestToSqlMigrateService.java @@ -24,8 +24,8 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.UUIDConverter; import org.thingsboard.server.dao.cassandra.CassandraCluster; -import org.thingsboard.server.dao.model.sqlts.dictionary.KeyDictionaryEntry; import org.thingsboard.server.dao.model.sqlts.dictionary.KeyDictionaryCompositeKey; +import org.thingsboard.server.dao.model.sqlts.dictionary.KeyDictionaryEntry; import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; import org.thingsboard.server.dao.sqlts.dictionary.KeyDictionaryRepository; import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository; diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 257dfa14b1..3577683729 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -19,6 +19,9 @@ import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.Futures; import freemarker.template.Configuration; import freemarker.template.Template; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.mail.internet.MimeMessage; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -53,9 +56,6 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; -import jakarta.mail.internet.MimeMessage; import java.io.ByteArrayInputStream; import java.util.HashMap; import java.util.Locale; diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultTbMailConfigTemplateService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultTbMailConfigTemplateService.java index a7726c3824..14e52c1326 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultTbMailConfigTemplateService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultTbMailConfigTemplateService.java @@ -16,12 +16,12 @@ package org.thingsboard.server.service.mail; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import jakarta.annotation.PostConstruct; import java.io.IOException; @Service diff --git a/application/src/main/java/org/thingsboard/server/service/mail/TbMailSender.java b/application/src/main/java/org/thingsboard/server/service/mail/TbMailSender.java index 63f2b9e9e4..10586bc41a 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/TbMailSender.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/TbMailSender.java @@ -23,10 +23,11 @@ import com.google.api.client.auth.oauth2.TokenResponse; import com.google.api.client.http.GenericUrl; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; import lombok.extern.slf4j.Slf4j; import org.springframework.lang.Nullable; import org.springframework.mail.MailException; -import org.springframework.mail.MailSendException; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.StringUtils; @@ -34,8 +35,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.mail.MailOauth2Provider; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import jakarta.mail.MessagingException; -import jakarta.mail.internet.MimeMessage; import java.time.Duration; import java.time.Instant; import java.util.Properties; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java index 916fe6da6c..5ba86198db 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationSchedulerService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.notification; import com.google.common.util.concurrent.ListenableFuture; +import jakarta.annotation.PostConstruct; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -40,7 +41,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.executors.NotificationExecutorService; import org.thingsboard.server.service.partition.AbstractPartitionBasedService; -import jakarta.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.Collections; import java.util.HashSet; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java index a86315ce86..dd31642aac 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/DefaultNotificationRuleProcessor.java @@ -22,6 +22,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.NotificationCenter; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.NotificationRequestId; @@ -41,7 +42,6 @@ import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.dao.notification.NotificationRequestService; -import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.notification.NotificationDeduplicationService; import org.thingsboard.server.service.executors.NotificationExecutorService; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java index 36c2d91b8e..9cef99fc38 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java @@ -22,10 +22,10 @@ import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.notification.info.AlarmAssignmentNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmAssignmentNotificationRuleTriggerConfig.Action; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmAssignmentTrigger; import static org.apache.commons.collections4.CollectionUtils.isEmpty; import static org.thingsboard.server.common.data.util.CollectionsUtil.emptyOrContains; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java index bd2bccecb2..6adead3963 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java @@ -24,9 +24,9 @@ import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.notification.info.AlarmCommentNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmCommentTrigger; import org.thingsboard.server.dao.entity.EntityService; import static org.apache.commons.collections4.CollectionUtils.isEmpty; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ApiUsageLimitTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ApiUsageLimitTriggerProcessor.java index 7b21e5ca28..4b707d0c6d 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ApiUsageLimitTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/ApiUsageLimitTriggerProcessor.java @@ -19,9 +19,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.notification.info.ApiUsageLimitNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.ApiUsageLimitTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.config.ApiUsageLimitNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.rule.trigger.ApiUsageLimitTrigger; import org.thingsboard.server.dao.tenant.TenantService; import static org.thingsboard.server.common.data.util.CollectionsUtil.emptyOrContains; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/DeviceActivityTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/DeviceActivityTriggerProcessor.java index e2d5484f59..e5500c45d1 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/DeviceActivityTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/DeviceActivityTriggerProcessor.java @@ -23,10 +23,10 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.info.DeviceActivityNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig.DeviceEvent; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityTrigger; import org.thingsboard.server.service.profile.TbDeviceProfileCache; @Service diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java index 301955eb36..ccbbc8c5d8 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/EntityActionTriggerProcessor.java @@ -20,9 +20,9 @@ import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.notification.info.EntityActionNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.config.EntityActionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.rule.trigger.EntityActionTrigger; import static org.thingsboard.server.common.data.util.CollectionsUtil.emptyOrContains; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java index 184acac061..6d9fb2d573 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NewPlatformVersionTriggerProcessor.java @@ -20,9 +20,9 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.UpdateMessage; import org.thingsboard.server.common.data.notification.info.NewPlatformVersionNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.config.NewPlatformVersionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.rule.trigger.NewPlatformVersionTrigger; @Service @RequiredArgsConstructor diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NotificationRuleTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NotificationRuleTriggerProcessor.java index 95f362f647..06d6b6d283 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NotificationRuleTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/NotificationRuleTriggerProcessor.java @@ -16,9 +16,9 @@ package org.thingsboard.server.service.notification.rule.trigger; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTrigger; public interface NotificationRuleTriggerProcessor { diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RateLimitsTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RateLimitsTriggerProcessor.java index ca1478b9bb..0b3ce26991 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RateLimitsTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RateLimitsTriggerProcessor.java @@ -21,10 +21,10 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.info.RateLimitsNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.notification.rule.trigger.config.RateLimitsNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.util.CollectionsUtil; -import org.thingsboard.server.common.data.notification.rule.trigger.RateLimitsTrigger; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.tenant.TenantService; diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java index a009841897..bc7b716b46 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/RuleEngineComponentLifecycleEventTriggerProcessor.java @@ -23,10 +23,10 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.notification.info.RuleEngineComponentLifecycleEventNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventTrigger; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.queue.discovery.PartitionService; diff --git a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index 7cb9a0fc0d..54ca38ad78 100644 --- a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -16,13 +16,13 @@ package org.thingsboard.server.service.ota; import com.google.common.util.concurrent.FutureCallback; +import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; -import org.thingsboard.server.common.data.AttributeScope; -import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.ota.OtaPackageService; @@ -52,7 +53,6 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.provider.TbRuleEngineQueueFactory; -import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java b/application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java index 2759ab7774..25c13f5a57 100644 --- a/application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java +++ b/application/src/main/java/org/thingsboard/server/service/query/DefaultEntityQueryService.java @@ -62,7 +62,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope; import java.util.ArrayList; import java.util.Collection; diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java index 483c0d4688..70a39f0851 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbClusterService.java @@ -22,6 +22,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.DataConstants; @@ -35,6 +36,7 @@ import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; +import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.AssetId; @@ -45,6 +47,7 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -188,6 +191,14 @@ public class DefaultTbClusterService implements TbClusterService { toCoreNfs.incrementAndGet(); } + @Override + public void pushNotificationToCore(String targetServiceId, TransportProtos.RestApiCallResponseMsgProto responseMsgProto, TbQueueCallback callback) { + TopicPartitionInfo tpi = topicService.getNotificationsTopic(ServiceType.TB_CORE, targetServiceId); + ToCoreNotificationMsg msg = ToCoreNotificationMsg.newBuilder().setRestApiCallResponseMsg(responseMsgProto).build(); + producerProvider.getTbCoreNotificationsMsgProducer().send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), callback); + toCoreNfs.incrementAndGet(); + } + @Override public void pushMsgToRuleEngine(TopicPartitionInfo tpi, UUID msgId, ToRuleEngineMsg msg, TbQueueCallback callback) { log.trace("PUSHING msg: {} to:{}", msg, tpi); @@ -197,6 +208,11 @@ public class DefaultTbClusterService implements TbClusterService { @Override public void pushMsgToRuleEngine(TenantId tenantId, EntityId entityId, TbMsg tbMsg, TbQueueCallback callback) { + pushMsgToRuleEngine(tenantId, entityId, tbMsg, false, callback); + } + + @Override + public void pushMsgToRuleEngine(TenantId tenantId, EntityId entityId, TbMsg tbMsg, boolean useQueueFromTbMsg, TbQueueCallback callback) { if (tenantId == null || tenantId.isNullUid()) { if (entityId.getEntityType().equals(EntityType.TENANT)) { tenantId = TenantId.fromUUID(entityId.getId()); @@ -205,31 +221,56 @@ public class DefaultTbClusterService implements TbClusterService { return; } } else { - HasRuleEngineProfile ruleEngineProfile = getRuleEngineProfileForEntityOrElseNull(tenantId, entityId); - tbMsg = transformMsg(tbMsg, ruleEngineProfile); + HasRuleEngineProfile ruleEngineProfile = getRuleEngineProfileForEntityOrElseNull(tenantId, entityId, tbMsg); + tbMsg = transformMsg(tbMsg, ruleEngineProfile, useQueueFromTbMsg); } - ruleEngineProducerService.sendToRuleEngine(producerProvider.getRuleEngineMsgProducer(), tenantId, tbMsg, callback); toRuleEngineMsgs.incrementAndGet(); } - private HasRuleEngineProfile getRuleEngineProfileForEntityOrElseNull(TenantId tenantId, EntityId entityId) { + HasRuleEngineProfile getRuleEngineProfileForEntityOrElseNull(TenantId tenantId, EntityId entityId, TbMsg tbMsg) { if (entityId.getEntityType().equals(EntityType.DEVICE)) { - return deviceProfileCache.get(tenantId, new DeviceId(entityId.getId())); + if (TbMsgType.ENTITY_DELETED.equals(tbMsg.getInternalType())) { + try { + Device deletedDevice = JacksonUtil.fromString(tbMsg.getData(), Device.class); + if (deletedDevice == null) { + return null; + } + return deviceProfileCache.get(tenantId, deletedDevice.getDeviceProfileId()); + } catch (Exception e) { + log.warn("[{}][{}] Failed to deserialize device: {}", tenantId, entityId, tbMsg, e); + return null; + } + } else { + return deviceProfileCache.get(tenantId, new DeviceId(entityId.getId())); + } } else if (entityId.getEntityType().equals(EntityType.DEVICE_PROFILE)) { return deviceProfileCache.get(tenantId, new DeviceProfileId(entityId.getId())); } else if (entityId.getEntityType().equals(EntityType.ASSET)) { - return assetProfileCache.get(tenantId, new AssetId(entityId.getId())); + if (TbMsgType.ENTITY_DELETED.equals(tbMsg.getInternalType())) { + try { + Asset deletedAsset = JacksonUtil.fromString(tbMsg.getData(), Asset.class); + if (deletedAsset == null) { + return null; + } + return assetProfileCache.get(tenantId, deletedAsset.getAssetProfileId()); + } catch (Exception e) { + log.warn("[{}][{}] Failed to deserialize asset: {}", tenantId, entityId, tbMsg, e); + return null; + } + } else { + return assetProfileCache.get(tenantId, new AssetId(entityId.getId())); + } } else if (entityId.getEntityType().equals(EntityType.ASSET_PROFILE)) { return assetProfileCache.get(tenantId, new AssetProfileId(entityId.getId())); } return null; } - private TbMsg transformMsg(TbMsg tbMsg, HasRuleEngineProfile ruleEngineProfile) { + private TbMsg transformMsg(TbMsg tbMsg, HasRuleEngineProfile ruleEngineProfile, boolean useQueueFromTbMsg) { if (ruleEngineProfile != null) { RuleChainId targetRuleChainId = ruleEngineProfile.getDefaultRuleChainId(); - String targetQueueName = ruleEngineProfile.getDefaultQueueName(); + String targetQueueName = useQueueFromTbMsg ? tbMsg.getQueueName() : ruleEngineProfile.getDefaultQueueName(); boolean isRuleChainTransform = targetRuleChainId != null && !targetRuleChainId.equals(tbMsg.getRuleChainId()); boolean isQueueTransform = targetQueueName != null && !targetQueueName.equals(tbMsg.getQueueName()); diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index ae8ea8f7f4..1dc42218fb 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -96,6 +96,7 @@ import org.thingsboard.server.service.queue.processing.AbstractConsumerService; import org.thingsboard.server.service.queue.processing.IdMsgPair; import org.thingsboard.server.service.resource.TbImageService; import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; +import org.thingsboard.server.service.ruleengine.RuleEngineCallService; import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService; import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.subscription.SubscriptionManagerService; @@ -149,6 +150,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService, CoreQueueConfig> mainConsumer; @@ -177,7 +179,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> requests = new ConcurrentHashMap<>(); + + public DefaultRuleEngineCallService(TbClusterService clusterService) { + this.clusterService = clusterService; + } + + @PostConstruct + public void initExecutor() { + executor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("re-rest-callback")); + } + + @PreDestroy + public void shutdownExecutor() { + if (executor != null) { + executor.shutdownNow(); + } + } + + @Override + public void processRestApiCallToRuleEngine(TenantId tenantId, UUID requestId, TbMsg request, boolean useQueueFromTbMsg, Consumer responseConsumer) { + log.trace("[{}] Processing REST API call to rule engine: [{}] for entity: [{}]", tenantId, requestId, request.getOriginator()); + requests.put(requestId, responseConsumer); + sendRequestToRuleEngine(tenantId, request, useQueueFromTbMsg); + scheduleTimeout(request, requestId, requests); + } + + @Override + public void onQueueMsg(TransportProtos.RestApiCallResponseMsgProto restApiCallResponseMsg, TbCallback callback) { + UUID requestId = new UUID(restApiCallResponseMsg.getRequestIdMSB(), restApiCallResponseMsg.getRequestIdLSB()); + Consumer consumer = requests.remove(requestId); + if (consumer != null) { + consumer.accept(TbMsg.fromBytes(null, restApiCallResponseMsg.getResponse().toByteArray(), TbMsgCallback.EMPTY)); + } else { + log.trace("[{}] Unknown or stale rest api call response received", requestId); + } + callback.onSuccess(); + } + + private void sendRequestToRuleEngine(TenantId tenantId, TbMsg msg, boolean useQueueFromTbMsg) { + clusterService.pushMsgToRuleEngine(tenantId, msg.getOriginator(), msg, useQueueFromTbMsg, null); + } + + private void scheduleTimeout(TbMsg request, UUID requestId, ConcurrentMap> requestsMap) { + long expirationTime = Long.parseLong(request.getMetaData().getValue("expirationTime")); + long timeout = Math.max(0, expirationTime - System.currentTimeMillis()); + log.trace("[{}] processing the request: [{}]", this.hashCode(), requestId); + executor.schedule(() -> { + Consumer consumer = requestsMap.remove(requestId); + if (consumer != null) { + log.trace("[{}] request timeout detected: [{}]", this.hashCode(), requestId); + consumer.accept(null); + } + }, timeout, TimeUnit.MILLISECONDS); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/ruleengine/RuleEngineCallService.java b/application/src/main/java/org/thingsboard/server/service/ruleengine/RuleEngineCallService.java new file mode 100644 index 0000000000..cdaa90398a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ruleengine/RuleEngineCallService.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ruleengine; + +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.UUID; +import java.util.function.Consumer; + +public interface RuleEngineCallService { + + void processRestApiCallToRuleEngine(TenantId tenantId, UUID requestId, TbMsg request, boolean useQueueFromTbMsg, Consumer responseConsumer); + + void onQueueMsg(TransportProtos.RestApiCallResponseMsgProto restApiCallResponseMsg, TbCallback callback); +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java index ebbd29e589..683dfc30be 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java @@ -19,6 +19,9 @@ import com.google.common.base.Function; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -81,9 +84,6 @@ import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import jakarta.annotation.Nullable; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.BiConsumer; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java index bc80eb404a..acd43dd719 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/JwtTokenAuthenticationProcessingFilter.java @@ -15,6 +15,10 @@ */ package org.thingsboard.server.service.security.auth.jwt; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -27,10 +31,6 @@ import org.thingsboard.server.service.security.auth.JwtAuthenticationToken; import org.thingsboard.server.service.security.auth.jwt.extractor.TokenExtractor; import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; public class JwtTokenAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java index 9b6f1805c0..b0dce5ec44 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/RefreshTokenProcessingFilter.java @@ -15,6 +15,10 @@ */ package org.thingsboard.server.service.security.auth.jwt; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationServiceException; @@ -30,10 +34,6 @@ import org.thingsboard.server.service.security.auth.RefreshAuthenticationToken; import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @Slf4j diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java index eeb94e3a6f..e99be65041 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/SkipPathRequestMatcher.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.service.security.auth.jwt; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; -import jakarta.servlet.http.HttpServletRequest; import java.util.List; import java.util.stream.Collectors; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java index 51024dfd60..3b191e0285 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtHeaderTokenExtractor.java @@ -15,13 +15,12 @@ */ package org.thingsboard.server.service.security.auth.jwt.extractor; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.config.ThingsboardSecurityConfiguration; -import jakarta.servlet.http.HttpServletRequest; - @Component(value="jwtHeaderTokenExtractor") public class JwtHeaderTokenExtractor implements TokenExtractor { public static final String HEADER_PREFIX = "Bearer "; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java index f611e94b12..d631bd4f25 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/jwt/extractor/JwtQueryTokenExtractor.java @@ -15,13 +15,12 @@ */ package org.thingsboard.server.service.security.auth.jwt.extractor; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.config.ThingsboardSecurityConfiguration; -import jakarta.servlet.http.HttpServletRequest; - @Component(value="jwtQueryTokenExtractor") public class JwtQueryTokenExtractor implements TokenExtractor { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java index d4425372e1..ab7fc19202 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/mfa/DefaultTwoFactorAuthService.java @@ -20,19 +20,19 @@ import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.LockedException; import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; import org.thingsboard.server.dao.user.UserService; -import org.thingsboard.server.common.data.limit.LimitedApi; -import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager; import org.thingsboard.server.service.security.auth.mfa.provider.TwoFaProvider; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java index 22d3b9b5ea..917b0f4f82 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java @@ -42,7 +42,6 @@ import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; -import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AppleOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AppleOAuth2ClientMapper.java index 26935ad0ee..23a60f45b6 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AppleOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AppleOAuth2ClientMapper.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.security.auth.oauth2; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Service; @@ -29,7 +30,6 @@ import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java index 693dfa5434..02fb9c6258 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.security.auth.oauth2; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Service; @@ -24,7 +25,6 @@ import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import jakarta.servlet.http.HttpServletRequest; import java.util.Map; @Service(value = "basicOAuth2ClientMapper") diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtils.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtils.java index a974c562be..280c9f1149 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtils.java @@ -17,12 +17,12 @@ package org.thingsboard.server.service.security.auth.oauth2; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.jackson2.SecurityJackson2Modules; - import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.jackson2.SecurityJackson2Modules; + import java.io.IOException; import java.util.Arrays; import java.util.Base64; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java index ed1d5cfd6d..e6cfb5a32a 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.security.auth.oauth2; import com.fasterxml.jackson.core.JsonProcessingException; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; @@ -30,8 +31,6 @@ import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import jakarta.servlet.http.HttpServletRequest; - @Service(value = "customOAuth2ClientMapper") @Slf4j @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java index 1b42946ad6..5619788a21 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.security.auth.oauth2; +import jakarta.servlet.http.HttpServletRequest; import lombok.Data; import lombok.ToString; import lombok.extern.slf4j.Slf4j; @@ -30,7 +31,6 @@ import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Map; import java.util.Optional; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepository.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepository.java index 7d697d5687..c0ba5de006 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/HttpCookieOAuth2AuthorizationRequestRepository.java @@ -15,14 +15,13 @@ */ package org.thingsboard.server.service.security.auth.oauth2; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.stereotype.Component; import org.thingsboard.server.queue.util.TbCoreComponent; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - @Component @TbCoreComponent public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java index cc877f36d4..a27ac410c1 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java @@ -15,12 +15,11 @@ */ package org.thingsboard.server.service.security.auth.oauth2; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.service.security.model.SecurityUser; -import jakarta.servlet.http.HttpServletRequest; - public interface OAuth2ClientMapper { SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java index be4555a36f..f2ce5e1e05 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.service.security.auth.oauth2; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.AuthenticationException; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; @@ -27,9 +30,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.system.SystemSecurityService; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 09ebb75e29..4605819894 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.service.security.auth.oauth2; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; @@ -38,9 +41,6 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.system.SystemSecurityService; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java index f570718a03..4aa948e2fd 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetails.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.service.security.auth.rest; +import jakarta.servlet.http.HttpServletRequest; import lombok.Data; import ua_parser.Client; import ua_parser.Parser; -import jakarta.servlet.http.HttpServletRequest; import java.io.Serializable; @Data diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetailsSource.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetailsSource.java index 077dea18df..d282e05daa 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetailsSource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAuthenticationDetailsSource.java @@ -15,9 +15,8 @@ */ package org.thingsboard.server.service.security.auth.rest; -import org.springframework.security.authentication.AuthenticationDetailsSource; - import jakarta.servlet.http.HttpServletRequest; +import org.springframework.security.authentication.AuthenticationDetailsSource; public class RestAuthenticationDetailsSource implements AuthenticationDetailsSource { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationFailureHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationFailureHandler.java index 045ae44015..088a497c19 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationFailureHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationFailureHandler.java @@ -15,15 +15,15 @@ */ package org.thingsboard.server.service.security.auth.rest; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.stereotype.Component; import org.thingsboard.server.exception.ThingsboardErrorResponseHandler; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @Component(value = "defaultAuthenticationFailureHandler") diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java index b05c3d7292..5dbcac0f84 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestAwareAuthenticationSuccessHandler.java @@ -15,6 +15,10 @@ */ package org.thingsboard.server.service.security.auth.rest; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -30,10 +34,6 @@ import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManage import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; import java.io.IOException; import java.util.Optional; import java.util.concurrent.TimeUnit; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java index e97fb02b1d..2b561cbf11 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java @@ -15,6 +15,10 @@ */ package org.thingsboard.server.service.security.auth.rest; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationDetailsSource; @@ -31,10 +35,6 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; import org.thingsboard.server.service.security.model.UserPrincipal; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @Slf4j diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestPublicLoginProcessingFilter.java b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestPublicLoginProcessingFilter.java index ffe17a1372..eda82e6571 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestPublicLoginProcessingFilter.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestPublicLoginProcessingFilter.java @@ -15,6 +15,10 @@ */ package org.thingsboard.server.service.security.auth.rest; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationServiceException; @@ -30,10 +34,6 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.service.security.exception.AuthMethodNotSupportedException; import org.thingsboard.server.service.security.model.UserPrincipal; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @Slf4j diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java index af1b34ae71..46cd1c2979 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java @@ -19,8 +19,6 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.HasTenantId; -import org.thingsboard.server.common.data.ResourceType; -import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.DashboardId; diff --git a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java index b12e6d4581..1b8cec2c91 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java @@ -18,6 +18,8 @@ package org.thingsboard.server.service.security.system; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.passay.CharacterRule; import org.passay.EnglishCharacterData; @@ -61,8 +63,6 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.utils.MiscUtils; import ua_parser.Client; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/application/src/main/java/org/thingsboard/server/service/security/system/SystemSecurityService.java b/application/src/main/java/org/thingsboard/server/service/security/system/SystemSecurityService.java index 06e5cece81..737bf95b13 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/system/SystemSecurityService.java +++ b/application/src/main/java/org/thingsboard/server/service/security/system/SystemSecurityService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.security.system; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.security.core.AuthenticationException; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.audit.ActionType; @@ -27,8 +28,6 @@ import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettin import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.service.security.model.SecurityUser; -import jakarta.servlet.http.HttpServletRequest; - public interface SystemSecurityService { SecuritySettings getSecuritySettings(); diff --git a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsService.java b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsService.java index aaac542a76..bf5a8c53b9 100644 --- a/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsService.java +++ b/application/src/main/java/org/thingsboard/server/service/sms/DefaultSmsService.java @@ -16,6 +16,8 @@ package org.thingsboard.server.service.sms; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.core.NestedRuntimeException; import org.springframework.stereotype.Service; @@ -35,9 +37,6 @@ import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; - @Service @Slf4j public class DefaultSmsService implements SmsService { diff --git a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java index be370ec562..1c53920a93 100644 --- a/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java @@ -23,6 +23,10 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @@ -81,10 +85,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.partition.AbstractPartitionBasedService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; diff --git a/application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java b/application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java index 3799dfda3d..3fc389b3d7 100644 --- a/application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java +++ b/application/src/main/java/org/thingsboard/server/service/stats/DefaultJsInvokeStats.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.stats; +import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.actors.JsInvokeStats; @@ -22,8 +23,6 @@ import org.thingsboard.server.common.stats.StatsCounter; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.common.stats.StatsType; -import jakarta.annotation.PostConstruct; - @Service public class DefaultJsInvokeStats implements JsInvokeStats { private static final String REQUESTS = "requests"; diff --git a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java index 03747d3062..e0c78e0afa 100644 --- a/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java +++ b/application/src/main/java/org/thingsboard/server/service/stats/DefaultRuleEngineStatisticsService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.stats; import com.google.common.util.concurrent.FutureCallback; +import jakarta.annotation.Nullable; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -36,7 +37,6 @@ import org.thingsboard.server.queue.util.TbRuleEngineComponent; import org.thingsboard.server.service.queue.TbRuleEngineConsumerStats; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; -import jakarta.annotation.Nullable; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java index 66b73781a0..b18cf7992c 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultSubscriptionManagerService.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.service.subscription; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import org.thingsboard.server.cluster.TbClusterService; -import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -54,7 +54,6 @@ import org.thingsboard.server.service.state.DeviceStateService; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscriptionUpdate; -import jakarta.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java index 984af019b4..b20abb001e 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java @@ -19,6 +19,8 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; @@ -66,8 +68,6 @@ import org.thingsboard.server.service.ws.telemetry.cmd.v2.LatestValueCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.TimeSeriesCmd; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java index 9f7b108bd0..bfe22137a7 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.service.subscription; import org.springframework.context.ApplicationListener; -import org.springframework.context.event.EventListener; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -24,10 +23,8 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.msg.queue.TbCallback; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.event.OtherServiceShutdownEvent; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; -import org.thingsboard.server.service.ws.notification.sub.NotificationRequestUpdate; import org.thingsboard.server.service.ws.notification.sub.NotificationUpdate; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityRemoteSubsInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityRemoteSubsInfo.java index 255f0d9a92..b77f990edb 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityRemoteSubsInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntityRemoteSubsInfo.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.subscription; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.util.Pair; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntitySubEvent.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntitySubEvent.java index 02924710fc..708c5494f2 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbEntitySubEvent.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbEntitySubEvent.java @@ -21,8 +21,6 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import java.util.Set; - /** * Information about the local websocket subscriptions. */ diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java index aa8ea70eab..48464d5297 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionsInfo.java @@ -20,9 +20,7 @@ import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import lombok.ToString; -import java.util.HashSet; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; /** * Information about the local websocket subscriptions. diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java index 6df897c179..a9b278a19d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.audit.ActionType; @@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.sync.ie.EntityImportResult; import org.thingsboard.server.common.data.util.ThrowingRunnable; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.entitiy.TbLogEntityActionService; import org.thingsboard.server.service.sync.ie.exporting.EntityExportService; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java index 881492f8e1..4c753ffffa 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java @@ -26,9 +26,9 @@ import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; import java.util.Collection; import java.util.Set; -import java.util.regex.Pattern; import java.util.UUID; import java.util.function.Function; +import java.util.regex.Pattern; import java.util.stream.Stream; public abstract class BaseEntityExportService, D extends EntityExportData> extends DefaultEntityExportService { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java index 89d1902069..d575f76665 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java @@ -20,7 +20,6 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.sync.ie.WidgetsBundleExportData; -import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.queue.util.TbCoreComponent; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index 3890e5791d..dd0f49c244 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -19,6 +19,9 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.FutureCallback; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Data; import lombok.SneakyThrows; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -29,6 +32,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasAdditionalInfo; @@ -48,7 +52,7 @@ import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumn import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; -import org.thingsboard.server.common.adaptor.JsonConverter; +import org.thingsboard.server.common.data.util.TypeCastUtil; import org.thingsboard.server.controller.BaseController; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.service.action.EntityActionService; @@ -59,11 +63,7 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import org.thingsboard.server.utils.CsvUtils; -import org.thingsboard.server.common.data.util.TypeCastUtil; -import jakarta.annotation.Nullable; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java index 68c9c81239..0073f8caac 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/AssetProfileImportService.java @@ -21,10 +21,8 @@ import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.sync.ie.EntityExportData; import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/EntityViewImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/EntityViewImportService.java index 6fd956e0db..bf0cd4b654 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/EntityViewImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/EntityViewImportService.java @@ -25,7 +25,6 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.sync.ie.EntityExportData; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.queue.util.TbCoreComponent; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java index 73b31ff02a..988dd8eec7 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java @@ -32,8 +32,8 @@ import org.thingsboard.server.common.data.notification.rule.EscalatedNotificatio import org.thingsboard.server.common.data.notification.rule.NotificationRule; import org.thingsboard.server.common.data.notification.rule.NotificationRuleRecipientsConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.DeviceActivityNotificationRuleTriggerConfig; -import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeCommunicationFailureNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.config.EdgeConnectionNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java index 4e0fdc33b8..20b8723b75 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java @@ -26,8 +26,6 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.UUIDBased; -import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.targets.NotificationTarget; import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java index f0a0f0b8d6..5325a2e0c5 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/RuleChainImportService.java @@ -24,10 +24,8 @@ import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.sync.ie.RuleChainExportData; import org.thingsboard.server.dao.rule.RuleChainService; diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index cd95e9504d..55319f1218 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -17,6 +17,8 @@ package org.thingsboard.server.service.system; import com.google.common.util.concurrent.FutureCallback; import com.google.protobuf.ProtocolStringList; +import jakarta.annotation.Nullable; +import jakarta.annotation.PreDestroy; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -49,8 +51,6 @@ import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; -import jakarta.annotation.Nullable; -import jakarta.annotation.PreDestroy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java index 6624f43095..846c32b7fb 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/AbstractSubscriptionService.java @@ -18,6 +18,9 @@ package org.thingsboard.server.service.telemetry; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.ThingsBoardThreadFactory; @@ -32,9 +35,6 @@ import org.thingsboard.server.queue.discovery.TbApplicationEventListener; import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; import org.thingsboard.server.service.subscription.SubscriptionManagerService; -import jakarta.annotation.Nullable; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java index 70962e821a..5513c41929 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java @@ -20,6 +20,9 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.SettableFuture; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; @@ -50,9 +53,6 @@ import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.entitiy.entityview.TbEntityViewService; import org.thingsboard.server.service.subscription.TbSubscriptionUtils; -import jakarta.annotation.Nullable; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index f5bd42e29d..8b29af34ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.transport; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; @@ -101,13 +100,11 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509Ce import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; -import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.List; -import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.Lock; @@ -136,7 +133,6 @@ public class DefaultTransportApiService implements TransportApiService { private final DeviceProfileService deviceProfileService; private final RelationService relationService; private final DeviceCredentialsService deviceCredentialsService; - private final DbCallbackExecutorService dbCallbackExecutorService; private final TbClusterService tbClusterService; private final DeviceProvisionService deviceProvisionService; private final ResourceService resourceService; @@ -170,52 +166,54 @@ public class DefaultTransportApiService implements TransportApiService { @Override public ListenableFuture> handle(TbProtoQueueMsg tbProtoQueueMsg) { TransportApiRequestMsg transportApiRequestMsg = tbProtoQueueMsg.getValue(); - ListenableFuture result = null; + return handlerExecutor.submit(() -> { + TransportApiResponseMsg result = handle(transportApiRequestMsg); + return new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), result, tbProtoQueueMsg.getHeaders()); + }); + } + private TransportApiResponseMsg handle(TransportApiRequestMsg transportApiRequestMsg) { if (transportApiRequestMsg.hasValidateTokenRequestMsg()) { ValidateDeviceTokenRequestMsg msg = transportApiRequestMsg.getValidateTokenRequestMsg(); final String token = msg.getToken(); - result = handlerExecutor.submit(() -> validateCredentials(token, DeviceCredentialsType.ACCESS_TOKEN)); + return validateCredentials(token, DeviceCredentialsType.ACCESS_TOKEN); } else if (transportApiRequestMsg.hasValidateBasicMqttCredRequestMsg()) { TransportProtos.ValidateBasicMqttCredRequestMsg msg = transportApiRequestMsg.getValidateBasicMqttCredRequestMsg(); - result = handlerExecutor.submit(() -> validateCredentials(msg)); + return validateCredentials(msg); } else if (transportApiRequestMsg.hasValidateX509CertRequestMsg()) { ValidateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateX509CertRequestMsg(); final String hash = msg.getHash(); - result = handlerExecutor.submit(() -> validateCredentials(hash, DeviceCredentialsType.X509_CERTIFICATE)); + return validateCredentials(hash, DeviceCredentialsType.X509_CERTIFICATE); } else if (transportApiRequestMsg.hasValidateOrCreateX509CertRequestMsg()) { TransportProtos.ValidateOrCreateDeviceX509CertRequestMsg msg = transportApiRequestMsg.getValidateOrCreateX509CertRequestMsg(); final String certChain = msg.getCertificateChain(); - result = handlerExecutor.submit(() -> validateOrCreateDeviceX509Certificate(certChain)); + return validateOrCreateDeviceX509Certificate(certChain); } else if (transportApiRequestMsg.hasGetOrCreateDeviceRequestMsg()) { - result = handlerExecutor.submit(() -> handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg())); + return handle(transportApiRequestMsg.getGetOrCreateDeviceRequestMsg()); } else if (transportApiRequestMsg.hasEntityProfileRequestMsg()) { - result = handle(transportApiRequestMsg.getEntityProfileRequestMsg()); + return handle(transportApiRequestMsg.getEntityProfileRequestMsg()); } else if (transportApiRequestMsg.hasLwM2MRequestMsg()) { - result = handle(transportApiRequestMsg.getLwM2MRequestMsg()); + return handle(transportApiRequestMsg.getLwM2MRequestMsg()); } else if (transportApiRequestMsg.hasValidateDeviceLwM2MCredentialsRequestMsg()) { ValidateDeviceLwM2MCredentialsRequestMsg msg = transportApiRequestMsg.getValidateDeviceLwM2MCredentialsRequestMsg(); final String credentialsId = msg.getCredentialsId(); - result = handlerExecutor.submit(() -> validateCredentials(credentialsId, DeviceCredentialsType.LWM2M_CREDENTIALS)); + return validateCredentials(credentialsId, DeviceCredentialsType.LWM2M_CREDENTIALS); } else if (transportApiRequestMsg.hasProvisionDeviceRequestMsg()) { - result = handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()); + return handle(transportApiRequestMsg.getProvisionDeviceRequestMsg()); } else if (transportApiRequestMsg.hasResourceRequestMsg()) { - result = handle(transportApiRequestMsg.getResourceRequestMsg()); + return handle(transportApiRequestMsg.getResourceRequestMsg()); } else if (transportApiRequestMsg.hasSnmpDevicesRequestMsg()) { - result = handle(transportApiRequestMsg.getSnmpDevicesRequestMsg()); + return handle(transportApiRequestMsg.getSnmpDevicesRequestMsg()); } else if (transportApiRequestMsg.hasDeviceRequestMsg()) { - result = handle(transportApiRequestMsg.getDeviceRequestMsg()); + return handle(transportApiRequestMsg.getDeviceRequestMsg()); } else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) { - result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg()); + return handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg()); } else if (transportApiRequestMsg.hasOtaPackageRequestMsg()) { - result = handle(transportApiRequestMsg.getOtaPackageRequestMsg()); + return handle(transportApiRequestMsg.getOtaPackageRequestMsg()); } else if (transportApiRequestMsg.hasGetAllQueueRoutingInfoRequestMsg()) { - return Futures.transform(handle(transportApiRequestMsg.getGetAllQueueRoutingInfoRequestMsg()), value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), MoreExecutors.directExecutor()); + return handle(transportApiRequestMsg.getGetAllQueueRoutingInfoRequestMsg()); } - - return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture), - value -> new TbProtoQueueMsg<>(tbProtoQueueMsg.getKey(), value, tbProtoQueueMsg.getHeaders()), - MoreExecutors.directExecutor()); + return getEmptyTransportApiResponse(); } private TransportApiResponseMsg validateCredentials(String credentialsId, DeviceCredentialsType credentialsType) { @@ -405,10 +403,10 @@ public class DefaultTransportApiService implements TransportApiService { } } - private ListenableFuture handle(ProvisionDeviceRequestMsg requestMsg) { - ListenableFuture provisionResponseFuture; + private TransportApiResponseMsg handle(ProvisionDeviceRequestMsg requestMsg) { + ProvisionResponse provisionResponse; try { - provisionResponseFuture = Futures.immediateFuture(deviceProvisionService.provisionDevice( + provisionResponse = deviceProvisionService.provisionDevice( new ProvisionRequest( requestMsg.getDeviceName(), requestMsg.getCredentialsType() != null ? DeviceCredentialsType.valueOf(requestMsg.getCredentialsType().name()) : null, @@ -419,18 +417,14 @@ public class DefaultTransportApiService implements TransportApiService { requestMsg.getCredentialsDataProto().getValidateDeviceX509CertRequestMsg().getHash()), new ProvisionDeviceProfileCredentials( requestMsg.getProvisionDeviceCredentialsMsg().getProvisionDeviceKey(), - requestMsg.getProvisionDeviceCredentialsMsg().getProvisionDeviceSecret())))); + requestMsg.getProvisionDeviceCredentialsMsg().getProvisionDeviceSecret()))); } catch (ProvisionFailedException e) { - return Futures.immediateFuture(getTransportApiResponseMsg( - new DeviceCredentials(), - TransportProtos.ResponseStatus.valueOf(e.getMessage()))); + return getTransportApiResponseMsg(new DeviceCredentials(), TransportProtos.ResponseStatus.valueOf(e.getMessage())); } - return Futures.transform(provisionResponseFuture, provisionResponse -> getTransportApiResponseMsg(provisionResponse.getDeviceCredentials(), TransportProtos.ResponseStatus.SUCCESS), - dbCallbackExecutorService); + return getTransportApiResponseMsg(provisionResponse.getDeviceCredentials(), TransportProtos.ResponseStatus.SUCCESS); } - private TransportApiResponseMsg getTransportApiResponseMsg(DeviceCredentials - deviceCredentials, TransportProtos.ResponseStatus status) { + private TransportApiResponseMsg getTransportApiResponseMsg(DeviceCredentials deviceCredentials, TransportProtos.ResponseStatus status) { if (!status.equals(TransportProtos.ResponseStatus.SUCCESS)) { return TransportApiResponseMsg.newBuilder().setProvisionDeviceResponseMsg(TransportProtos.ProvisionDeviceResponseMsg.newBuilder().setStatus(status).build()).build(); } @@ -453,7 +447,7 @@ public class DefaultTransportApiService implements TransportApiService { .build(); } - private ListenableFuture handle(GetEntityProfileRequestMsg requestMsg) { + private TransportApiResponseMsg handle(GetEntityProfileRequestMsg requestMsg) { EntityType entityType = EntityType.valueOf(requestMsg.getEntityType()); UUID entityUuid = new UUID(requestMsg.getEntityIdMSB(), requestMsg.getEntityIdLSB()); GetEntityProfileResponseMsg.Builder builder = GetEntityProfileResponseMsg.newBuilder(); @@ -470,10 +464,10 @@ public class DefaultTransportApiService implements TransportApiService { } else { throw new RuntimeException("Invalid entity profile request: " + entityType); } - return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build()); + return TransportApiResponseMsg.newBuilder().setEntityProfileResponseMsg(builder).build(); } - private ListenableFuture handle(GetDeviceRequestMsg requestMsg) { + private TransportApiResponseMsg handle(GetDeviceRequestMsg requestMsg) { DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); Device device = deviceService.findDeviceById(TenantId.SYS_TENANT_ID, deviceId); @@ -491,21 +485,20 @@ public class DefaultTransportApiService implements TransportApiService { } else { responseMsg = TransportApiResponseMsg.getDefaultInstance(); } - - return Futures.immediateFuture(responseMsg); + return responseMsg; } - private ListenableFuture handle(GetDeviceCredentialsRequestMsg requestMsg) { + private TransportApiResponseMsg handle(GetDeviceCredentialsRequestMsg requestMsg) { DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, deviceId); - return Futures.immediateFuture(TransportApiResponseMsg.newBuilder() + return TransportApiResponseMsg.newBuilder() .setDeviceCredentialsResponseMsg(TransportProtos.GetDeviceCredentialsResponseMsg.newBuilder() .setDeviceCredentialsData(ProtoUtils.toProto(deviceCredentials))) - .build()); + .build(); } - private ListenableFuture handle(GetResourceRequestMsg requestMsg) { + private TransportApiResponseMsg handle(GetResourceRequestMsg requestMsg) { TenantId tenantId = TenantId.fromUUID(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); ResourceType resourceType = ResourceType.valueOf(requestMsg.getResourceType()); String resourceKey = requestMsg.getResourceKey(); @@ -520,10 +513,10 @@ public class DefaultTransportApiService implements TransportApiService { builder.setResource(ProtoUtils.toProto(resource)); } - return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setResourceResponseMsg(builder).build()); + return TransportApiResponseMsg.newBuilder().setResourceResponseMsg(builder).build(); } - private ListenableFuture handle(GetSnmpDevicesRequestMsg requestMsg) { + private TransportApiResponseMsg handle(GetSnmpDevicesRequestMsg requestMsg) { PageLink pageLink = new PageLink(requestMsg.getPageSize(), requestMsg.getPage()); PageData result = deviceService.findDevicesIdsByDeviceProfileTransportType(DeviceTransportType.SNMP, pageLink); @@ -534,9 +527,9 @@ public class DefaultTransportApiService implements TransportApiService { .setHasNextPage(result.hasNext()) .build(); - return Futures.immediateFuture(TransportApiResponseMsg.newBuilder() + return TransportApiResponseMsg.newBuilder() .setSnmpDevicesResponseMsg(responseMsg) - .build()); + .build(); } TransportApiResponseMsg getDeviceInfo(DeviceCredentials credentials) { @@ -565,31 +558,26 @@ public class DefaultTransportApiService implements TransportApiService { } } - private ListenableFuture getEmptyTransportApiResponseFuture() { - return Futures.immediateFuture(getEmptyTransportApiResponse()); - } - private TransportApiResponseMsg getEmptyTransportApiResponse() { return TransportApiResponseMsg.newBuilder() .setValidateCredResponseMsg(ValidateDeviceCredentialsResponseMsg.getDefaultInstance()).build(); } - private ListenableFuture handle(TransportProtos.LwM2MRequestMsg requestMsg) { + private TransportApiResponseMsg handle(TransportProtos.LwM2MRequestMsg requestMsg) { if (requestMsg.hasRegistrationMsg()) { return handleRegistration(requestMsg.getRegistrationMsg()); } else { - return Futures.immediateFailedFuture(new RuntimeException("Not supported!")); + throw new RuntimeException("Not supported!"); } } - private ListenableFuture handle(TransportProtos.GetOtaPackageRequestMsg requestMsg) { + private TransportApiResponseMsg handle(TransportProtos.GetOtaPackageRequestMsg requestMsg) { TenantId tenantId = TenantId.fromUUID(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); OtaPackageType otaPackageType = OtaPackageType.valueOf(requestMsg.getType()); Device device = deviceService.findDeviceById(tenantId, deviceId); - if (device == null) { - return getEmptyTransportApiResponseFuture(); + return getEmptyTransportApiResponse(); } OtaPackageId otaPackageId = OtaPackageUtil.getOtaPackageId(device, otaPackageType); @@ -626,14 +614,12 @@ public class DefaultTransportApiService implements TransportApiService { } } - return Futures.immediateFuture( - TransportApiResponseMsg.newBuilder() - .setOtaPackageResponseMsg(builder.build()) - .build()); + return TransportApiResponseMsg.newBuilder() + .setOtaPackageResponseMsg(builder.build()) + .build(); } - private ListenableFuture handleRegistration - (TransportProtos.LwM2MRegistrationRequestMsg msg) { + private TransportApiResponseMsg handleRegistration(TransportProtos.LwM2MRegistrationRequestMsg msg) { TenantId tenantId = TenantId.fromUUID(UUID.fromString(msg.getTenantId())); String deviceName = msg.getEndpoint(); Lock deviceCreationLock = deviceCreationLocks.computeIfAbsent(deviceName, id -> new ReentrantLock()); @@ -651,21 +637,18 @@ public class DefaultTransportApiService implements TransportApiService { TransportProtos.LwM2MRegistrationResponseMsg.newBuilder() .setDeviceInfo(ProtoUtils.toDeviceInfoProto(device)).build(); TransportProtos.LwM2MResponseMsg responseMsg = TransportProtos.LwM2MResponseMsg.newBuilder().setRegistrationMsg(registrationResponseMsg).build(); - return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setLwM2MResponseMsg(responseMsg).build()); + return TransportApiResponseMsg.newBuilder().setLwM2MResponseMsg(responseMsg).build(); } catch (JsonProcessingException e) { - log.warn("[{}][{}] Failed to lookup device by gateway id and name", tenantId, deviceName, e); + log.warn("[{}][{}] Failed to lookup device by name", tenantId, deviceName, e); throw new RuntimeException(e); } finally { deviceCreationLock.unlock(); } } - private ListenableFuture handle(TransportProtos.GetAllQueueRoutingInfoRequestMsg requestMsg) { - return queuesToTransportApiResponseMsg(queueService.findAllQueues()); - } - - private ListenableFuture queuesToTransportApiResponseMsg(List queues) { - return Futures.immediateFuture(TransportApiResponseMsg.newBuilder() + private TransportApiResponseMsg handle(TransportProtos.GetAllQueueRoutingInfoRequestMsg requestMsg) { + List queues = queueService.findAllQueues(); + return TransportApiResponseMsg.newBuilder() .addAllGetQueueRoutingInfoResponseMsgs(queues.stream() .map(queue -> TransportProtos.GetQueueRoutingInfoResponseMsg.newBuilder() .setTenantIdMSB(queue.getTenantId().getId().getMostSignificantBits()) @@ -676,7 +659,7 @@ public class DefaultTransportApiService implements TransportApiService { .setQueueTopic(queue.getTopic()) .setPartitions(queue.getPartitions()) .setDuplicateMsgToAllPartitions(queue.isDuplicateMsgToAllPartitions()) - .build()).collect(Collectors.toList())).build()); + .build()).collect(Collectors.toList())).build(); } private ProvisionRequest createProvisionRequest(String certificateValue) { diff --git a/application/src/main/java/org/thingsboard/server/service/transport/TbCoreTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/TbCoreTransportApiService.java index 15679986e6..b38fbf4fec 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/TbCoreTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/TbCoreTransportApiService.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.service.transport; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -34,8 +36,6 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbCoreComponent; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.concurrent.ExecutorService; /** diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java index c0f3158c61..dd80eba6bc 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java @@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileCon import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.alarm.AlarmService; -import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.queue.discovery.PartitionService; diff --git a/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java b/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java index a0695c226f..b2d919e136 100644 --- a/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.update; import com.fasterxml.jackson.databind.node.ObjectNode; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -36,7 +37,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.instructions.EdgeInstallInstructionsService; import org.thingsboard.server.service.edge.instructions.EdgeUpgradeInstructionsService; -import jakarta.annotation.PreDestroy; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; diff --git a/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java index d7091bbad2..b72a1a5841 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/DefaultWebSocketService.java @@ -21,6 +21,9 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -80,10 +83,6 @@ import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.UnsubscribeCmd; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; -import jakarta.annotation.Nullable; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; - import java.io.IOException; import java.util.ArrayList; import java.util.Collections; diff --git a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java index 8b09b547b2..7807c3541f 100644 --- a/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java +++ b/application/src/main/java/org/thingsboard/server/service/ws/telemetry/cmd/v1/SubscriptionCmd.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.ws.telemetry.cmd.v1; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import org.thingsboard.server.service.ws.telemetry.TelemetryFeature; @NoArgsConstructor @AllArgsConstructor diff --git a/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java index 95b9254b66..3faf09d993 100644 --- a/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/LwM2mObjectModelUtils.java @@ -31,7 +31,6 @@ import org.thingsboard.server.dao.exception.DataValidationException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Base64; import java.util.List; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; diff --git a/application/src/main/java/org/thingsboard/server/utils/MiscUtils.java b/application/src/main/java/org/thingsboard/server/utils/MiscUtils.java index 9d324bc206..7e6a1322b3 100644 --- a/application/src/main/java/org/thingsboard/server/utils/MiscUtils.java +++ b/application/src/main/java/org/thingsboard/server/utils/MiscUtils.java @@ -17,8 +17,8 @@ package org.thingsboard.server.utils; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; - import jakarta.servlet.http.HttpServletRequest; + import java.nio.charset.Charset; diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index e00eb4850d..3c9afc4b33 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -966,6 +966,8 @@ transport: request_timeout: "${HTTP_REQUEST_TIMEOUT:60000}" # HTTP maximum request processing timeout in milliseconds max_request_timeout: "${HTTP_MAX_REQUEST_TIMEOUT:300000}" + # Maximum request size + max_payload_size: "${HTTP_MAX_PAYLOAD_SIZE:65536}" # max payload size in bytes # Local MQTT transport parameters mqtt: # Enable/disable mqtt transport protocol. diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index 35c2ea65fa..d549407c44 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -386,6 +386,15 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); } + protected void testLogEntityActionError(EntityId originatorId, TenantId tenantId, + CustomerId customerId, UserId userId, String userName, + ActionType actionType, Exception exception, Object... additionalInfo) { + ArgumentMatcher matcherError = argument -> argument.getMessage().contains(exception.getMessage()) + & argument.getClass().equals(exception.getClass()); + testLogEntityActionErrorAdditionalInfo(Objects::isNull, originatorId, tenantId, customerId, userId, userName, + actionType, 1, matcherError, extractMatcherAdditionalInfo(additionalInfo)); + } + private void testLogEntityActionAdditionalInfo(ArgumentMatcher matcherEntity, ArgumentMatcher matcherOriginatorId, TenantId tenantId, ArgumentMatcher matcherCustomerId, ArgumentMatcher matcherUserId, String userName, ActionType actionType, @@ -529,8 +538,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.argThat(matcherError), - Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(0))), - Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(1)))); + Mockito.argThat(matcherAdditionalInfos.get(0)), + Mockito.argThat(matcherAdditionalInfos.get(1))); + break; case 3: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), @@ -541,9 +551,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.argThat(matcherError), - Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(0))), - Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(1))), - Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(2)))); + Mockito.argThat(matcherAdditionalInfos.get(0)), + Mockito.argThat(matcherAdditionalInfos.get(1)), + Mockito.argThat(matcherAdditionalInfos.get(2))); break; default: Mockito.verify(auditLogService, times(cntTime)) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 8304d0dd1e..b7cc42c7ac 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -212,6 +212,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected UserId differentCustomerUserId; protected UserId differentTenantCustomerUserId; + protected UserId currentUserId; @SuppressWarnings("rawtypes") private HttpMessageConverter mappingJackson2HttpMessageConverter; @@ -567,6 +568,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { Claims claims = jwsClaims.getPayload(); String subject = claims.getSubject(); Assert.assertEquals(username, subject); + String userId = claims.get("userId", String.class); + this.currentUserId = UserId.fromString(userId); } protected void resetTokens() throws Exception { @@ -633,6 +636,11 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return doPost("/api/device?accessToken=" + accessToken, device, Device.class); } + protected Device assignDeviceToCustomer(DeviceId deviceId, CustomerId customerId) { + String deviceIdStr = String.valueOf(deviceId.getId()); + return doPost("/api/customer/" + customerId.getId() + "/device/" + deviceIdStr, Device.class); + } + protected MqttDeviceProfileTransportConfiguration createMqttDeviceProfileTransportConfiguration(TransportPayloadTypeConfiguration transportPayloadTypeConfiguration, boolean sendAckOnValidationException) { MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = new MqttDeviceProfileTransportConfiguration(); mqttDeviceProfileTransportConfiguration.setDeviceTelemetryTopic(MqttTopics.DEVICE_TELEMETRY_TOPIC); @@ -803,6 +811,10 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return readResponse(doPost(urlTemplate, content, params).andExpect(resultMatcher), responseType); } + protected R doPostAsyncWithTypedResponse(String urlTemplate, T content, TypeReference responseType, ResultMatcher resultMatcher, String... params) throws Exception { + return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseType); + } + protected R doPostAsync(String urlTemplate, T content, Class responseClass, ResultMatcher resultMatcher, String... params) throws Exception { return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass); } diff --git a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java index eb4f6fa083..17de555bdc 100644 --- a/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/EntityQueryControllerTest.java @@ -22,7 +22,6 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.JacksonUtil; @@ -36,7 +35,6 @@ import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmSeverity; -import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.page.PageData; @@ -56,8 +54,8 @@ import org.thingsboard.server.common.data.query.EntityTypeFilter; import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.data.query.KeyFilter; import org.thingsboard.server.common.data.query.NumericFilterPredicate; -import org.thingsboard.server.common.data.query.StringFilterPredicate; import org.thingsboard.server.common.data.query.RelationsQueryFilter; +import org.thingsboard.server.common.data.query.StringFilterPredicate; import org.thingsboard.server.common.data.query.TsValue; import org.thingsboard.server.common.data.queue.QueueStats; import org.thingsboard.server.common.data.relation.EntityRelation; diff --git a/application/src/test/java/org/thingsboard/server/controller/RpcControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/RpcControllerTest.java index d0f6426ab1..0cf84572c2 100644 --- a/application/src/test/java/org/thingsboard/server/controller/RpcControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/RpcControllerTest.java @@ -22,6 +22,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.MvcResult; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; @@ -38,6 +39,9 @@ import java.util.List; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @DaoSqlTest +@TestPropertySource(properties = { + "transport.http.max_payload_size=10000" +}) public class RpcControllerTest extends AbstractControllerTest { private Tenant savedTenant; @@ -78,13 +82,18 @@ public class RpcControllerTest extends AbstractControllerTest { } private ObjectNode createDefaultRpc() { + return createDefaultRpc(100); + } + + private ObjectNode createDefaultRpc(int size) { ObjectNode rpc = JacksonUtil.newObjectNode(); rpc.put("method", "setGpio"); ObjectNode params = JacksonUtil.newObjectNode(); params.put("pin", 7); - params.put("value", 1); + String value = "a".repeat(size - 83); + params.put("value", value); rpc.set("params", params); rpc.put("persistent", true); @@ -122,6 +131,28 @@ public class RpcControllerTest extends AbstractControllerTest { Assert.assertEquals(savedDevice.getId(), savedRpc.getDeviceId()); } + @Test + public void testSaveLargeRpc() throws Exception { + Device device = createDefaultDevice(); + Device savedDevice = doPost("/api/device", device, Device.class); + + ObjectNode rpc = createDefaultRpc(10001); + doPost( + "/api/rpc/oneway/" + savedDevice.getId().getId().toString(), + JacksonUtil.toString(rpc), + String.class, + status().isPayloadTooLarge() + ); + + ObjectNode validRpc = createDefaultRpc(10000); + doPost( + "/api/rpc/oneway/" + savedDevice.getId().getId().toString(), + JacksonUtil.toString(validRpc), + String.class, + status().isOk() + ); + } + @Test public void testDeleteRpc() throws Exception { Device device = createDefaultDevice(); diff --git a/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java new file mode 100644 index 0000000000..9c9322dc38 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/RuleEngineControllerTest.java @@ -0,0 +1,249 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.service.ruleengine.RuleEngineCallService; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@DaoSqlTest +public class RuleEngineControllerTest extends AbstractControllerTest { + + private final String REQUEST_BODY = "{\"request\":\"download\"}"; + private final String RESPONSE_BODY = "{\"response\":\"downloadOk\"}"; + + @SpyBean + private RuleEngineCallService ruleEngineCallService; + + @Test + public void testHandleRuleEngineRequestWithMsgOriginatorUser() throws Exception { + loginSysAdmin(); + TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, currentUserId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + mockRestApiCallToRuleEngine(responseMsg); + + JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/", REQUEST_BODY, new TypeReference<>() { + }, status().isOk()); + + assertThat(JacksonUtil.toString(apiResponse)).isEqualTo(RESPONSE_BODY); + ArgumentCaptor requestMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ruleEngineCallService).processRestApiCallToRuleEngine(eq(TenantId.SYS_TENANT_ID), any(UUID.class), requestMsgCaptor.capture(), eq(false), any(Consumer.class)); + TbMsg requestMsgCaptorValue = requestMsgCaptor.getValue(); + assertThat(requestMsgCaptorValue.getData()).isEqualTo(REQUEST_BODY); + assertThat(requestMsgCaptorValue.getType()).isEqualTo(TbMsgType.REST_API_REQUEST.name()); + assertThat(requestMsgCaptorValue.getOriginator()).isEqualTo(currentUserId); + assertThat(requestMsgCaptorValue.getCustomerId()).isNull(); + checkMetadataProperties(requestMsgCaptorValue.getMetaData()); + testLogEntityAction(null, currentUserId, TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), currentUserId, + SYS_ADMIN_EMAIL, ActionType.REST_API_RULE_ENGINE_CALL, 1, REQUEST_BODY, RESPONSE_BODY); + } + + @Test + public void testHandleRuleEngineRequestWithMsgOriginatorDevice() throws Exception { + loginTenantAdmin(); + Device device = createDevice("Test", "123"); + DeviceId deviceId = device.getId(); + TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + mockRestApiCallToRuleEngine(responseMsg); + + JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId(), REQUEST_BODY, new TypeReference<>() { + }, status().isOk()); + + assertThat(JacksonUtil.toString(apiResponse)).isEqualTo(RESPONSE_BODY); + ArgumentCaptor requestMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ruleEngineCallService).processRestApiCallToRuleEngine(eq(tenantId), any(UUID.class), requestMsgCaptor.capture(), eq(false), any(Consumer.class)); + TbMsg requestMsgCaptorValue = requestMsgCaptor.getValue(); + assertThat(requestMsgCaptorValue.getData()).isEqualTo(REQUEST_BODY); + assertThat(requestMsgCaptorValue.getType()).isEqualTo(TbMsgType.REST_API_REQUEST.name()); + assertThat(requestMsgCaptorValue.getOriginator()).isEqualTo(deviceId); + assertThat(requestMsgCaptorValue.getCustomerId()).isNull(); + checkMetadataProperties(requestMsgCaptorValue.getMetaData()); + testLogEntityAction(null, deviceId, tenantId, new CustomerId(EntityId.NULL_UUID), tenantAdminUserId, + TENANT_ADMIN_EMAIL, ActionType.REST_API_RULE_ENGINE_CALL, 1, REQUEST_BODY, RESPONSE_BODY); + } + + @Test + public void testHandleRuleEngineRequestWithMsgOriginatorDeviceAndSpecifiedTimeout() throws Exception { + loginTenantAdmin(); + Device device = createDevice("Test", "123"); + DeviceId deviceId = device.getId(); + TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + mockRestApiCallToRuleEngine(responseMsg); + + JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId() + "/15000", REQUEST_BODY, new TypeReference<>() { + }, status().isOk()); + + assertThat(JacksonUtil.toString(apiResponse)).isEqualTo(RESPONSE_BODY); + ArgumentCaptor requestMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ruleEngineCallService).processRestApiCallToRuleEngine(eq(tenantId), any(UUID.class), requestMsgCaptor.capture(), eq(false), any(Consumer.class)); + TbMsg requestMsgCaptorValue = requestMsgCaptor.getValue(); + assertThat(requestMsgCaptorValue.getData()).isEqualTo(REQUEST_BODY); + assertThat(requestMsgCaptorValue.getType()).isEqualTo(TbMsgType.REST_API_REQUEST.name()); + assertThat(requestMsgCaptorValue.getOriginator()).isEqualTo(deviceId); + assertThat(requestMsgCaptorValue.getCustomerId()).isNull(); + checkMetadataProperties(requestMsgCaptorValue.getMetaData()); + testLogEntityAction(null, deviceId, tenantId, new CustomerId(EntityId.NULL_UUID), tenantAdminUserId, + TENANT_ADMIN_EMAIL, ActionType.REST_API_RULE_ENGINE_CALL, 1, REQUEST_BODY, RESPONSE_BODY); + } + + @Test + public void testHandleRuleEngineRequestWithMsgOriginatorDeviceAndResponseIsNull() throws Exception { + loginTenantAdmin(); + Device device = createDevice("Test", "123"); + DeviceId deviceId = device.getId(); + mockRestApiCallToRuleEngine(null); + + doPostAsync("/api/rule-engine/DEVICE/" + deviceId.getId() + "/15000", REQUEST_BODY, String.class, status().isRequestTimeout()); + + ArgumentCaptor requestMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ruleEngineCallService).processRestApiCallToRuleEngine(eq(tenantId), any(UUID.class), requestMsgCaptor.capture(), eq(false), any(Consumer.class)); + TbMsg requestMsgCaptorValue = requestMsgCaptor.getValue(); + assertThat(requestMsgCaptorValue.getData()).isEqualTo(REQUEST_BODY); + assertThat(requestMsgCaptorValue.getType()).isEqualTo(TbMsgType.REST_API_REQUEST.name()); + assertThat(requestMsgCaptorValue.getOriginator()).isEqualTo(deviceId); + assertThat(requestMsgCaptorValue.getCustomerId()).isNull(); + checkMetadataProperties(requestMsgCaptorValue.getMetaData()); + Exception exception = new TimeoutException("Processing timeout detected!"); + testLogEntityActionError(deviceId, tenantId, new CustomerId(EntityId.NULL_UUID), tenantAdminUserId, + TENANT_ADMIN_EMAIL, ActionType.REST_API_RULE_ENGINE_CALL, exception, REQUEST_BODY, ""); + } + + @Test + public void testHandleRuleEngineRequestWithMsgOriginatorDeviceAndSpecifiedQueue() throws Exception { + loginTenantAdmin(); + Device device = createDevice("Test", "123"); + DeviceId deviceId = device.getId(); + TbMsg responseMsg = TbMsg.newMsg(DataConstants.HP_QUEUE_NAME, TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + mockRestApiCallToRuleEngine(responseMsg); + + JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId() + "/HighPriority/1000", REQUEST_BODY, new TypeReference<>() { + }, status().isOk()); + + assertThat(JacksonUtil.toString(apiResponse)).isEqualTo(RESPONSE_BODY); + ArgumentCaptor requestMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ruleEngineCallService).processRestApiCallToRuleEngine(eq(tenantId), any(UUID.class), requestMsgCaptor.capture(), eq(true), any(Consumer.class)); + TbMsg requestMsgCaptorValue = requestMsgCaptor.getValue(); + assertThat(requestMsgCaptorValue.getData()).isEqualTo(REQUEST_BODY); + assertThat(requestMsgCaptorValue.getType()).isEqualTo(TbMsgType.REST_API_REQUEST.name()); + assertThat(requestMsgCaptorValue.getQueueName()).isEqualTo(DataConstants.HP_QUEUE_NAME); + assertThat(requestMsgCaptorValue.getOriginator()).isEqualTo(deviceId); + assertThat(requestMsgCaptorValue.getCustomerId()).isNull(); + checkMetadataProperties(requestMsgCaptorValue.getMetaData()); + testLogEntityAction(null, deviceId, tenantId, new CustomerId(EntityId.NULL_UUID), tenantAdminUserId, + TENANT_ADMIN_EMAIL, ActionType.REST_API_RULE_ENGINE_CALL, 1, REQUEST_BODY, RESPONSE_BODY); + } + + @Test + public void testHandleRuleEngineRequestWithInvalidRequestBody() throws Exception { + loginSysAdmin(); + + doPost("/api/rule-engine/", (Object) "@") + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Invalid request body"))); + + verifyNoInteractions(ruleEngineCallService); + } + + @Test + public void testHandleRuleEngineRequestWithAuthorityCustomerUser() throws Exception { + loginTenantAdmin(); + Device device = createDevice("Test", "123"); + DeviceId deviceId = device.getId(); + assignDeviceToCustomer(deviceId, customerId); + loginCustomerUser(); + + TbMsg responseMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, customerId, TbMsgMetaData.EMPTY, RESPONSE_BODY); + mockRestApiCallToRuleEngine(responseMsg); + + JsonNode apiResponse = doPostAsyncWithTypedResponse("/api/rule-engine/DEVICE/" + deviceId.getId(), REQUEST_BODY, new TypeReference<>() { + }, status().isOk()); + + assertThat(JacksonUtil.toString(apiResponse)).isEqualTo(RESPONSE_BODY); + ArgumentCaptor requestMsgCaptor = ArgumentCaptor.forClass(TbMsg.class); + verify(ruleEngineCallService).processRestApiCallToRuleEngine(eq(tenantId), any(UUID.class), requestMsgCaptor.capture(), eq(false), any(Consumer.class)); + TbMsg requestMsgCaptorValue = requestMsgCaptor.getValue(); + assertThat(requestMsgCaptorValue.getData()).isEqualTo(REQUEST_BODY); + assertThat(requestMsgCaptorValue.getType()).isEqualTo(TbMsgType.REST_API_REQUEST.name()); + assertThat(requestMsgCaptorValue.getOriginator()).isEqualTo(deviceId); + assertThat(requestMsgCaptorValue.getCustomerId()).isEqualTo(customerId); + checkMetadataProperties(requestMsgCaptorValue.getMetaData()); + testLogEntityAction(null, deviceId, tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, + ActionType.REST_API_RULE_ENGINE_CALL, 1, REQUEST_BODY, RESPONSE_BODY); + } + + @Test + public void testHandleRuleEngineRequestWithoutPermission() throws Exception { + loginTenantAdmin(); + Device device = createDevice("test", "123"); + loginCustomerUser(); + + doPostAsync("/api/rule-engine/DEVICE/" + device.getId().getId(), (Object) REQUEST_BODY, -1L) + .andExpect(status().isForbidden()) + .andExpect(content().string("You don't have permission to perform this operation!")); + + verifyNoInteractions(ruleEngineCallService); + } + + @Test + public void testHandleRuleEngineRequestUnauthorized() throws Exception { + doPost("/api/rule-engine/", (Object) REQUEST_BODY) + .andExpect(status().isUnauthorized()) + .andExpect(statusReason(containsString("Authentication failed"))); + } + + private void mockRestApiCallToRuleEngine(TbMsg responseMsg) { + doAnswer(invocation -> { + Consumer consumer = invocation.getArgument(4); + consumer.accept(responseMsg); + return null; + }).when(ruleEngineCallService).processRestApiCallToRuleEngine(any(TenantId.class), any(UUID.class), any(TbMsg.class), anyBoolean(), any(Consumer.class)); + } + + public void checkMetadataProperties(TbMsgMetaData metaData) { + Map data = metaData.getData(); + assertThat(data).containsKeys("serviceId", "requestUUID", "expirationTime"); + } +} diff --git a/application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java index 0a110ef2d0..3aab891322 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TelemetryControllerTest.java @@ -15,17 +15,14 @@ */ package org.thingsboard.server.controller; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.junit.Assert; import org.junit.Test; import org.springframework.test.context.TestPropertySource; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.SingleEntityFilter; import org.thingsboard.server.common.data.security.DeviceCredentials; diff --git a/application/src/test/java/org/thingsboard/server/controller/plugin/TbWebSocketHandlerTest.java b/application/src/test/java/org/thingsboard/server/controller/plugin/TbWebSocketHandlerTest.java index 6e1198758d..b87f82de77 100644 --- a/application/src/test/java/org/thingsboard/server/controller/plugin/TbWebSocketHandlerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/plugin/TbWebSocketHandlerTest.java @@ -15,6 +15,10 @@ */ package org.thingsboard.server.controller.plugin; +import jakarta.websocket.RemoteEndpoint; +import jakarta.websocket.SendHandler; +import jakarta.websocket.SendResult; +import jakarta.websocket.Session; import lombok.extern.slf4j.Slf4j; import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterEach; @@ -26,10 +30,6 @@ import org.springframework.web.socket.adapter.NativeWebSocketSession; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.service.ws.WebSocketSessionRef; -import jakarta.websocket.RemoteEndpoint; -import jakarta.websocket.SendHandler; -import jakarta.websocket.SendResult; -import jakarta.websocket.Session; import java.io.IOException; import java.util.Collection; import java.util.Deque; diff --git a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java index dbef55539f..c468387c55 100644 --- a/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/queue/discovery/HashPartitionServiceTest.java @@ -21,8 +21,8 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; diff --git a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java index 2f3ec5315f..4c4e887644 100644 --- a/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java @@ -24,9 +24,9 @@ import org.junit.Test; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.rule.engine.metadata.TbGetAttributesNode; import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +import org.thingsboard.rule.engine.util.TbMsgSource; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DataConstants; diff --git a/application/src/test/java/org/thingsboard/server/service/mail/TbMailSenderTest.java b/application/src/test/java/org/thingsboard/server/service/mail/TbMailSenderTest.java index a299b56dbe..e5e729b634 100644 --- a/application/src/test/java/org/thingsboard/server/service/mail/TbMailSenderTest.java +++ b/application/src/test/java/org/thingsboard/server/service/mail/TbMailSenderTest.java @@ -15,15 +15,16 @@ */ package org.thingsboard.server.service.mail; +import jakarta.mail.MessagingException; +import jakarta.mail.Session; +import jakarta.mail.internet.MimeMessage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mockito; -import jakarta.mail.MessagingException; -import jakarta.mail.Session; -import jakarta.mail.internet.MimeMessage; + import java.util.ArrayList; import java.util.List; import java.util.Properties; diff --git a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java index 22f3620d8c..59cca779f5 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbClusterServiceTest.java @@ -19,19 +19,34 @@ import com.google.common.collect.Sets; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.AssetProfileId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueProducer; +import org.thingsboard.server.queue.common.DefaultTbQueueMsgHeaders; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.common.TbRuleEngineProducerService; import org.thingsboard.server.queue.discovery.PartitionService; @@ -44,13 +59,16 @@ import org.thingsboard.server.service.profile.TbDeviceProfileCache; import java.util.List; import java.util.UUID; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @Slf4j @@ -240,6 +258,99 @@ public class DefaultTbClusterServiceTest { .send(eq(topicService.getNotificationsTopic(ServiceType.TB_TRANSPORT, monolith2)), any(TbProtoQueueMsg.class), isNull()); } + @Test + public void testPushNotificationToCoreWithRestApiCallResponseMsgProto() { + TopicPartitionInfo tpi = mock(TopicPartitionInfo.class); + TbQueueCallback callbackMock = mock(TbQueueCallback.class); + TbQueueProducer> tbCoreQueueProducer = mock(TbQueueProducer.class); + + doReturn(tpi).when(topicService).getNotificationsTopic(any(ServiceType.class), any(String.class)); + when(producerProvider.getTbCoreNotificationsMsgProducer()).thenReturn(tbCoreQueueProducer); + TransportProtos.RestApiCallResponseMsgProto responseMsgProto = TransportProtos.RestApiCallResponseMsgProto.getDefaultInstance(); + TransportProtos.ToCoreNotificationMsg toCoreNotificationMsg = TransportProtos.ToCoreNotificationMsg.newBuilder().setRestApiCallResponseMsg(responseMsgProto).build(); + + clusterService.pushNotificationToCore(CORE, responseMsgProto, callbackMock); + + verify(topicService).getNotificationsTopic(ServiceType.TB_CORE, CORE); + verify(producerProvider).getTbCoreNotificationsMsgProducer(); + ArgumentCaptor> protoQueueMsgArgumentCaptor = ArgumentCaptor.forClass(TbProtoQueueMsg.class); + verify(tbCoreQueueProducer).send(eq(tpi), protoQueueMsgArgumentCaptor.capture(), eq(callbackMock)); + TbProtoQueueMsg protoQueueMsgArgumentCaptorValue = protoQueueMsgArgumentCaptor.getValue(); + assertThat(protoQueueMsgArgumentCaptorValue.getKey()).isNotNull(); + assertThat(protoQueueMsgArgumentCaptorValue.getValue()).isEqualTo(toCoreNotificationMsg); + assertThat(protoQueueMsgArgumentCaptorValue.getHeaders().getData()).isEqualTo(new DefaultTbQueueMsgHeaders().getData()); + } + + @Test + public void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsTenantUseQueueFromMsgIsTrue() { + TbQueueProducer> tbREQueueProducer = mock(TbQueueProducer.class); + TbQueueCallback callback = mock(TbQueueCallback.class); + + TenantId tenantId = TenantId.fromUUID(UUID.fromString("3c8bd350-1239-4a3b-b9c3-4dd76f8e20f1")); + TbMsg requestMsg = TbMsg.newMsg(DataConstants.HP_QUEUE_NAME, TbMsgType.REST_API_REQUEST, tenantId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + when(producerProvider.getRuleEngineMsgProducer()).thenReturn(tbREQueueProducer); + + clusterService.pushMsgToRuleEngine(TenantId.SYS_TENANT_ID, tenantId, requestMsg, true, callback); + + verify(producerProvider).getRuleEngineMsgProducer(); + verify(ruleEngineProducerService).sendToRuleEngine(tbREQueueProducer, tenantId, requestMsg, callback); + } + + @Test + public void testPushMsgToRuleEngineWithTenantIdIsNullUuidAndEntityIsDevice() { + TenantId tenantId = TenantId.SYS_TENANT_ID; + DeviceId deviceId = new DeviceId(UUID.fromString("aa6d112d-2914-4a22-a9e3-bee33edbdb14")); + TbMsg requestMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbQueueCallback callback = mock(TbQueueCallback.class); + + clusterService.pushMsgToRuleEngine(tenantId, deviceId, requestMsg, false, callback); + + verifyNoMoreInteractions(partitionService, producerProvider); + } + + @Test + public void testPushMsgToRuleEngineWithTenantIdIsNotNullUuidUseQueueFromMsgIsTrue() { + TbQueueProducer> tbREQueueProducer = mock(TbQueueProducer.class); + TbQueueCallback callback = mock(TbQueueCallback.class); + + TenantId tenantId = TenantId.fromUUID(UUID.fromString("3c8bd350-1239-4a3b-b9c3-4dd76f8e20f1")); + DeviceId deviceId = new DeviceId(UUID.fromString("adbb9d41-3367-40fd-9e74-7dd7cc5d30cf")); + DeviceProfile deviceProfile = new DeviceProfile(new DeviceProfileId(UUID.fromString("552f5d6d-0b2b-43e1-a7d2-a51cb2a96927"))); + TbMsg requestMsg = TbMsg.newMsg(DataConstants.HP_QUEUE_NAME, TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + when(deviceProfileCache.get(any(TenantId.class), any(DeviceId.class))).thenReturn(deviceProfile); + when(producerProvider.getRuleEngineMsgProducer()).thenReturn(tbREQueueProducer); + + clusterService.pushMsgToRuleEngine(tenantId, deviceId, requestMsg, true, callback); + + verify(producerProvider).getRuleEngineMsgProducer(); + verify(ruleEngineProducerService).sendToRuleEngine(tbREQueueProducer, tenantId, requestMsg, callback); + } + + @Test + public void testPushMsgToRuleEngineUseQueueFromMsgIsFalse() { + TbQueueProducer> tbREQueueProducer = mock(TbQueueProducer.class); + TbQueueCallback callback = mock(TbQueueCallback.class); + + TenantId tenantId = TenantId.fromUUID(UUID.fromString("5377c8d0-26e5-4d81-84c6-4344043973c8")); + DeviceId deviceId = new DeviceId(UUID.fromString("016c2abb-f46f-49f9-a83d-4d28b803cfe6")); + DeviceProfile deviceProfile = new DeviceProfile(new DeviceProfileId(UUID.fromString("dc5766e2-1a32-4022-859b-743050097ab7"))); + deviceProfile.setDefaultQueueName(DataConstants.MAIN_QUEUE_NAME); + TbMsg requestMsg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + when(deviceProfileCache.get(any(TenantId.class), any(DeviceId.class))).thenReturn(deviceProfile); + when(producerProvider.getRuleEngineMsgProducer()).thenReturn(tbREQueueProducer); + + clusterService.pushMsgToRuleEngine(tenantId, deviceId, requestMsg, false, callback); + + verify(producerProvider).getRuleEngineMsgProducer(); + TbMsg expectedMsg = TbMsg.transformMsgQueueName(requestMsg, DataConstants.MAIN_QUEUE_NAME); + ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); + verify(ruleEngineProducerService).sendToRuleEngine(eq(tbREQueueProducer), eq(tenantId), actualMsg.capture(), eq(callback)); + assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); + } + protected Queue createTestQueue() { TenantId tenantId = TenantId.SYS_TENANT_ID; Queue queue = new Queue(new QueueId(UUID.randomUUID())); @@ -249,4 +360,44 @@ public class DefaultTbClusterServiceTest { queue.setPartitions(10); return queue; } + + @Test + public void testGetRuleEngineProfileForUpdatedAndDeletedDevice() { + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TenantId tenantId = new TenantId(UUID.randomUUID()); + DeviceProfileId deviceProfileId = new DeviceProfileId(UUID.randomUUID()); + + Device device = new Device(deviceId); + device.setDeviceProfileId(deviceProfileId); + + // device updated + TbMsg tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_UPDATED).build(); + ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, deviceId, tbMsg); + verify(deviceProfileCache, times(1)).get(tenantId, deviceId); + + // device deleted + tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(device)).build(); + ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, deviceId, tbMsg); + verify(deviceProfileCache, times(1)).get(tenantId, deviceProfileId); + } + + @Test + public void testGetRuleEngineProfileForUpdatedAndDeletedAsset() { + AssetId assetId = new AssetId(UUID.randomUUID()); + TenantId tenantId = new TenantId(UUID.randomUUID()); + AssetProfileId assetProfileId = new AssetProfileId(UUID.randomUUID()); + + Asset asset = new Asset(assetId); + asset.setAssetProfileId(assetProfileId); + + // asset updated + TbMsg tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_UPDATED).build(); + ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, assetId, tbMsg); + verify(assetProfileCache, times(1)).get(tenantId, assetId); + + // asset deleted + tbMsg = TbMsg.builder().internalType(TbMsgType.ENTITY_DELETED).data(JacksonUtil.toString(asset)).build(); + ((DefaultTbClusterService) clusterService).getRuleEngineProfileForEntityOrElseNull(tenantId, assetId, tbMsg); + verify(assetProfileCache, times(1)).get(tenantId, assetProfileId); + } } \ No newline at end of file diff --git a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerServiceTest.java b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerServiceTest.java index 621d4c5a91..ca6e6728ff 100644 --- a/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerServiceTest.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.service.ruleengine.RuleEngineCallService; import org.thingsboard.server.service.state.DeviceStateService; import java.util.UUID; @@ -48,6 +49,8 @@ public class DefaultTbCoreConsumerServiceTest { private DeviceStateService stateServiceMock; @Mock private TbCoreConsumerStats statsMock; + @Mock + private RuleEngineCallService ruleEngineCallServiceMock; @Mock private TbCallback tbCallbackMock; @@ -529,4 +532,17 @@ public class DefaultTbCoreConsumerServiceTest { then(statsMock).should(never()).log(inactivityMsg); } + @Test + public void givenRestApiCallResponseMsgProto_whenForwardToRuleEngineCallService_thenCallOnQueueMsg() { + // GIVEN + ReflectionTestUtils.setField(defaultTbCoreConsumerServiceMock, "ruleEngineCallService", ruleEngineCallServiceMock); + var restApiCallResponseMsgProto = TransportProtos.RestApiCallResponseMsgProto.getDefaultInstance(); + doCallRealMethod().when(defaultTbCoreConsumerServiceMock).forwardToRuleEngineCallService(restApiCallResponseMsgProto, tbCallbackMock); + + // WHEN + defaultTbCoreConsumerServiceMock.forwardToRuleEngineCallService(restApiCallResponseMsgProto, tbCallbackMock); + + // THEN + then(ruleEngineCallServiceMock).should().onQueueMsg(restApiCallResponseMsgProto, tbCallbackMock); + } } diff --git a/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java new file mode 100644 index 0000000000..098c622e31 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/queue/ruleengine/TbRuleEngineStrategyTest.java @@ -0,0 +1,286 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.queue.ruleengine; + +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; +import org.thingsboard.server.actors.ActorSystemContext; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.queue.ProcessingStrategy; +import org.thingsboard.server.common.data.queue.ProcessingStrategyType; +import org.thingsboard.server.common.data.queue.Queue; +import org.thingsboard.server.common.data.queue.SubmitStrategy; +import org.thingsboard.server.common.data.queue.SubmitStrategyType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgDataType; +import org.thingsboard.server.common.msg.TbMsgProcessingCtx; +import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg; +import org.thingsboard.server.common.msg.queue.RuleEngineException; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; +import org.thingsboard.server.queue.TbQueueConsumer; +import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.discovery.QueueKey; +import org.thingsboard.server.service.queue.processing.TbRuleEngineProcessingStrategyFactory; +import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrategyFactory; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.thingsboard.server.common.data.queue.ProcessingStrategyType.RETRY_ALL; +import static org.thingsboard.server.common.data.queue.ProcessingStrategyType.RETRY_FAILED; +import static org.thingsboard.server.common.data.queue.ProcessingStrategyType.RETRY_FAILED_AND_TIMED_OUT; +import static org.thingsboard.server.common.data.queue.ProcessingStrategyType.RETRY_TIMED_OUT; +import static org.thingsboard.server.common.data.queue.ProcessingStrategyType.SKIP_ALL_FAILURES; +import static org.thingsboard.server.common.data.queue.ProcessingStrategyType.SKIP_ALL_FAILURES_AND_TIMED_OUT; +import static org.thingsboard.server.common.data.queue.SubmitStrategyType.BATCH; +import static org.thingsboard.server.common.data.queue.SubmitStrategyType.BURST; +import static org.thingsboard.server.common.data.queue.SubmitStrategyType.SEQUENTIAL_BY_ORIGINATOR; + +@Slf4j +@MockitoSettings(strictness = Strictness.LENIENT) +public class TbRuleEngineStrategyTest { + + @Mock + private ActorSystemContext actorContext; + @Mock + private TbQueueConsumer> consumer; + private TbRuleEngineConsumerContext ruleEngineConsumerContext; + + private static UUID tenantId = UUID.randomUUID(); + private static DeviceId deviceId = new DeviceId(UUID.randomUUID()); + + @BeforeEach + public void beforeEach() { + ruleEngineConsumerContext = new TbRuleEngineConsumerContext( + actorContext, mock(), new TbRuleEngineSubmitStrategyFactory(), + new TbRuleEngineProcessingStrategyFactory(), mock(), mock(), + mock(), mock(), mock(), mock() + ); + when(consumer.isStopped()).thenReturn(false); + } + + private static Stream testData() { + return Stream.of( + //BURST + Arguments.of(BURST, SKIP_ALL_FAILURES, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, SKIP_ALL_FAILURES, 3, List.of(new ProcessingData(true, false, 1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, SKIP_ALL_FAILURES, 3, List.of(new ProcessingData(false, true, 1), new ProcessingData(1), new ProcessingData(1))), + + Arguments.of(BURST, SKIP_ALL_FAILURES_AND_TIMED_OUT, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, SKIP_ALL_FAILURES_AND_TIMED_OUT, 3, List.of(new ProcessingData(true, false, 1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, SKIP_ALL_FAILURES_AND_TIMED_OUT, 3, List.of(new ProcessingData(false, true, 1), new ProcessingData(1), new ProcessingData(1))), + + Arguments.of(BURST, RETRY_ALL, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, RETRY_ALL, 3, List.of(new ProcessingData(true, false, 2), new ProcessingData(2), new ProcessingData(2))), + Arguments.of(BURST, RETRY_ALL, 3, List.of(new ProcessingData(false, true, 2), new ProcessingData(2), new ProcessingData(2))), + + Arguments.of(BURST, RETRY_FAILED, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, RETRY_FAILED, 3, List.of(new ProcessingData(true, false, 2), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, RETRY_FAILED, 3, List.of(new ProcessingData(false, true, 1), new ProcessingData(1), new ProcessingData(1))), + + Arguments.of(BURST, RETRY_TIMED_OUT, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, RETRY_TIMED_OUT, 3, List.of(new ProcessingData(true, false, 1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, RETRY_TIMED_OUT, 3, List.of(new ProcessingData(false, true, 2), new ProcessingData(1), new ProcessingData(1))), + + Arguments.of(BURST, RETRY_FAILED_AND_TIMED_OUT, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, RETRY_FAILED_AND_TIMED_OUT, 3, List.of(new ProcessingData(true, false, 2), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BURST, RETRY_FAILED_AND_TIMED_OUT, 3, List.of(new ProcessingData(false, true, 2), new ProcessingData(1), new ProcessingData(1))), + + //BATCH + Arguments.of(BATCH, SKIP_ALL_FAILURES, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BATCH, SKIP_ALL_FAILURES, 3, List.of(new ProcessingData(true, false, 1), new ProcessingData(1), new ProcessingData(0))), + Arguments.of(BATCH, SKIP_ALL_FAILURES, 3, List.of(new ProcessingData(false, true, 1), new ProcessingData(1), new ProcessingData(0))), + + Arguments.of(BATCH, SKIP_ALL_FAILURES_AND_TIMED_OUT, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BATCH, SKIP_ALL_FAILURES_AND_TIMED_OUT, 3, List.of(new ProcessingData(true, false, 1), new ProcessingData(1), new ProcessingData(0))), + Arguments.of(BATCH, SKIP_ALL_FAILURES_AND_TIMED_OUT, 3, List.of(new ProcessingData(false, true, 1), new ProcessingData(1), new ProcessingData(0))), + + Arguments.of(BATCH, RETRY_ALL, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BATCH, RETRY_ALL, 3, List.of(new ProcessingData(true, false, 2), new ProcessingData(2), new ProcessingData(1))), + Arguments.of(BATCH, RETRY_ALL, 3, List.of(new ProcessingData(false, true, 2), new ProcessingData(2), new ProcessingData(1))), + + Arguments.of(BATCH, RETRY_FAILED, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BATCH, RETRY_FAILED, 3, List.of(new ProcessingData(true, false, 2), new ProcessingData(1), new ProcessingData(0))), + Arguments.of(BATCH, RETRY_FAILED, 3, List.of(new ProcessingData(false, true, 1), new ProcessingData(1), new ProcessingData(0))), + + Arguments.of(BATCH, RETRY_TIMED_OUT, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BATCH, RETRY_TIMED_OUT, 3, List.of(new ProcessingData(true, false, 1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BATCH, RETRY_TIMED_OUT, 3, List.of(new ProcessingData(false, true, 2), new ProcessingData(1), new ProcessingData(1))), + + Arguments.of(BATCH, RETRY_FAILED_AND_TIMED_OUT, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BATCH, RETRY_FAILED_AND_TIMED_OUT, 3, List.of(new ProcessingData(true, false, 2), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(BATCH, RETRY_FAILED_AND_TIMED_OUT, 3, List.of(new ProcessingData(false, true, 2), new ProcessingData(1), new ProcessingData(1))), + + //SEQUENTIAL_BY_ORIGINATOR + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, SKIP_ALL_FAILURES, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, SKIP_ALL_FAILURES, 3, List.of(new ProcessingData(true, false, 1), new ProcessingData(0), new ProcessingData(0))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, SKIP_ALL_FAILURES, 3, List.of(new ProcessingData(false, true, 1), new ProcessingData(0), new ProcessingData(0))), + + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, SKIP_ALL_FAILURES_AND_TIMED_OUT, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, SKIP_ALL_FAILURES_AND_TIMED_OUT, 3, List.of(new ProcessingData(true, false, 1), new ProcessingData(0), new ProcessingData(0))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, SKIP_ALL_FAILURES_AND_TIMED_OUT, 3, List.of(new ProcessingData(false, true, 1), new ProcessingData(0), new ProcessingData(0))), + + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_ALL, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_ALL, 3, List.of(new ProcessingData(true, false, 2), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_ALL, 3, List.of(new ProcessingData(false, true, 2), new ProcessingData(1), new ProcessingData(1))), + + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_FAILED, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_FAILED, 3, List.of(new ProcessingData(true, false, 2), new ProcessingData(0), new ProcessingData(0))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_FAILED, 3, List.of(new ProcessingData(false, true, 1), new ProcessingData(0), new ProcessingData(0))), + + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_TIMED_OUT, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_TIMED_OUT, 3, List.of(new ProcessingData(true, false, 1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_TIMED_OUT, 3, List.of(new ProcessingData(false, true, 2), new ProcessingData(1), new ProcessingData(1))), + + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_FAILED_AND_TIMED_OUT, 3, List.of(new ProcessingData(1), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_FAILED_AND_TIMED_OUT, 3, List.of(new ProcessingData(true, false, 2), new ProcessingData(1), new ProcessingData(1))), + Arguments.of(SEQUENTIAL_BY_ORIGINATOR, RETRY_FAILED_AND_TIMED_OUT, 3, List.of(new ProcessingData(false, true, 2), new ProcessingData(1), new ProcessingData(1))) + ); + } + + @ParameterizedTest + @MethodSource("testData") + public void processMsgsTest(SubmitStrategyType submitStrategyType, ProcessingStrategyType processingStrategyType, int retries, List processingData) throws Exception { + var queue = new Queue(); + queue.setName("Test"); + queue.setPackProcessingTimeout(100); + SubmitStrategy submitStrategy = new SubmitStrategy(); + submitStrategy.setType(submitStrategyType); + submitStrategy.setBatchSize(2); + queue.setSubmitStrategy(submitStrategy); + ProcessingStrategy processingStrategy = new ProcessingStrategy(); + processingStrategy.setType(processingStrategyType); + processingStrategy.setRetries(retries); + queue.setProcessingStrategy(processingStrategy); + + QueueKey queueKey = new QueueKey(ServiceType.TB_RULE_ENGINE, queue); + var consumerManager = TbRuleEngineQueueConsumerManager.create() + .ctx(ruleEngineConsumerContext) + .queueKey(queueKey) + .build(); + + consumerManager.init(queue); + + Map msgsMap = processingData.stream().collect(Collectors.toMap(data -> data.tbMsg.getId(), data -> data)); + Map attemptsMap = new HashMap<>(); + Map failedMap = new HashMap<>(); + Map timeoutMap = new HashMap<>(); + + doAnswer(inv -> { + QueueToRuleEngineMsg msg = inv.getArgument(0); + + UUID msgId = msg.getMsg().getId(); + var data = msgsMap.get(msgId); + + var attempts = attemptsMap.computeIfAbsent(msgId, key -> new AtomicInteger(0)); + Assertions.assertTrue(attempts.getAndIncrement() <= data.attempts); + + var callback = msg.getMsg().getCallback(); + + if (data.shouldFailed) { + var alreadyFailed = failedMap.computeIfAbsent(msgId, key -> new AtomicBoolean(false)); + if (!alreadyFailed.getAndSet(true)) { + callback.onFailure(new RuleEngineException("Failed to process test msg!")); + return null; + } + } + + if (data.shouldTimeout) { + var alreadyTimedOuted = timeoutMap.computeIfAbsent(msgId, key -> new AtomicBoolean(false)); + if (!alreadyTimedOuted.getAndSet(true)) { + return null; + } + } + + callback.onSuccess(); + return null; + }).when(actorContext).tell(any()); + + List> protoMsgs = processingData.stream() + .map(data -> data.tbMsg) + .map(this::toProto) + .toList(); + + consumerManager.processMsgs(protoMsgs, consumer, queue); + + processingData.forEach(data -> { + verify(actorContext, times(data.attempts)).tell(argThat(msg -> + ((QueueToRuleEngineMsg) msg).getMsg().getId().equals(data.tbMsg.getId()) + )); + }); + } + + private static TbMsg createRandomMsg() { + return TbMsg.builder() + .id(UUID.randomUUID()) + .type("test type") + .originator(deviceId) + .dataType(TbMsgDataType.TEXT) + .data("test data") + .ctx(new TbMsgProcessingCtx()) + .build(); + } + + private TbProtoQueueMsg toProto(TbMsg tbMsg) { + return new TbProtoQueueMsg<>(UUID.randomUUID(), ToRuleEngineMsg.newBuilder() + .setTenantIdMSB(tenantId.getMostSignificantBits()) + .setTenantIdLSB(tenantId.getLeastSignificantBits()) + .addRelationTypes("Success") + .setTbMsg(TbMsg.toByteString(tbMsg)) + .build()); + } + + @RequiredArgsConstructor + @EqualsAndHashCode + @ToString(exclude = "tbMsg") + private static class ProcessingData { + private final TbMsg tbMsg = createRandomMsg(); + private final boolean shouldFailed; + private final boolean shouldTimeout; + private final int attempts; + + public ProcessingData(int attempts) { + shouldFailed = false; + shouldTimeout = false; + this.attempts = attempts; + } + } + +} diff --git a/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java b/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java new file mode 100644 index 0000000000..0ed53b9561 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/rpc/DefaultTbRuleEngineRpcServiceTest.java @@ -0,0 +1,61 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.rpc; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.gen.transport.TransportProtos; +import java.util.UUID; + +import static org.mockito.BDDMockito.then; + +@ExtendWith(MockitoExtension.class) +class DefaultTbRuleEngineRpcServiceTest { + + @Mock + private TbClusterService tbClusterServiceMock; + + @InjectMocks + private DefaultTbRuleEngineRpcService tbRuleEngineRpcService; + + @Test + public void givenTbMsg_whenSendRestApiCallReply_thenPushNotificationToCore() { + // GIVEN + String serviceId = "tb-core-0"; + UUID requestId = UUID.fromString("f64a20df-eb1e-46a3-ba6f-0b3ae053ee0a"); + DeviceId deviceId = new DeviceId(UUID.fromString("1d9f771a-7cdc-4ac7-838c-ba193d05a012")); + TbMsg msg = TbMsg.newMsg(TbMsgType.REST_API_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + var restApiCallResponseMsgProto = TransportProtos.RestApiCallResponseMsgProto.newBuilder() + .setRequestIdMSB(requestId.getMostSignificantBits()) + .setRequestIdLSB(requestId.getLeastSignificantBits()) + .setResponse(TbMsg.toByteString(msg)) + .build(); + + // WHEN + tbRuleEngineRpcService.sendRestApiCallReply(serviceId, requestId, msg); + + // THEN + then(tbClusterServiceMock).should().pushNotificationToCore(serviceId, restApiCallResponseMsgProto, null); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java b/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java new file mode 100644 index 0000000000..282563574f --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/service/ruleengine/DefaultRuleEngineCallServiceTest.java @@ -0,0 +1,141 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.service.ruleengine; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; +import org.testcontainers.shaded.org.awaitility.Awaitility; +import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.common.msg.queue.TbCallback; +import org.thingsboard.server.gen.transport.TransportProtos; + +import java.util.HashMap; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +public class DefaultRuleEngineCallServiceTest { + + private static final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("d7210c7f-a152-4e91-8186-19ae85499a6b")); + + private final ConcurrentMap> requests = new ConcurrentHashMap<>(); + + @Mock + private TbClusterService tbClusterServiceMock; + + private DefaultRuleEngineCallService ruleEngineCallService; + private ScheduledExecutorService executor; + + @BeforeEach + void setUp() { + executor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("re-rest-callback")); + ruleEngineCallService = new DefaultRuleEngineCallService(tbClusterServiceMock); + ReflectionTestUtils.setField(ruleEngineCallService, "executor", executor); + ReflectionTestUtils.setField(ruleEngineCallService, "requests", requests); + } + + @AfterEach + void tearDown() { + requests.clear(); + if (executor != null) { + executor.shutdownNow(); + } + } + + @Test + void givenRequest_whenProcessRestApiCallToRuleEngine_thenPushMsgToRuleEngineAndCheckRemovedDueTimeout() { + long timeout = 1L; + long expTime = System.currentTimeMillis() + timeout; + HashMap metaData = new HashMap<>(); + UUID requestId = UUID.randomUUID(); + metaData.put("serviceId", "core"); + metaData.put("requestUUID", requestId.toString()); + metaData.put("expirationTime", Long.toString(expTime)); + TbMsg msg = TbMsg.newMsg(DataConstants.MAIN_QUEUE_NAME, TbMsgType.REST_API_REQUEST, TENANT_ID, new TbMsgMetaData(metaData), "{\"key\":\"value\"}"); + + Consumer anyConsumer = TbMsg::getData; + doAnswer(invocation -> { + //check the presence of request in the map after pushMsgToRuleEngine() + assertThat(requests.size()).isEqualTo(1); + assertThat(requests.get(requestId)).isEqualTo(anyConsumer); + return null; + }).when(tbClusterServiceMock).pushMsgToRuleEngine(any(), any(), any(), anyBoolean(), any()); + ruleEngineCallService.processRestApiCallToRuleEngine(TENANT_ID, requestId, msg, true, anyConsumer); + + verify(tbClusterServiceMock).pushMsgToRuleEngine(TENANT_ID, TENANT_ID, msg, true, null); + //check map is empty after scheduleTimeout() + Awaitility.await("Await until request was deleted from map due to timeout") + .pollDelay(25, TimeUnit.MILLISECONDS) + .atMost(10, TimeUnit.SECONDS) + .until(requests::isEmpty); + } + + @Test + void givenResponse_whenOnQueue_thenAcceptTbMsgResponse() { + long timeout = 10000L; + long expTime = System.currentTimeMillis() + timeout; + HashMap metaData = new HashMap<>(); + UUID requestId = UUID.randomUUID(); + metaData.put("serviceId", "core"); + metaData.put("requestUUID", requestId.toString()); + metaData.put("expirationTime", Long.toString(expTime)); + TbMsg msg = TbMsg.newMsg(DataConstants.MAIN_QUEUE_NAME, TbMsgType.REST_API_REQUEST, TENANT_ID, new TbMsgMetaData(metaData), "{\"key\":\"value\"}"); + + Consumer anyConsumer = TbMsg::getData; + doAnswer(invocation -> { + //check the presence of request in the map after pushMsgToRuleEngine() + assertThat(requests.size()).isEqualTo(1); + assertThat(requests.get(requestId)).isEqualTo(anyConsumer); + ruleEngineCallService.onQueueMsg(getResponse(requestId, msg), TbCallback.EMPTY); + //check map is empty after onQueueMsg() + assertThat(requests.size()).isEqualTo(0); + return null; + }).when(tbClusterServiceMock).pushMsgToRuleEngine(any(), any(), any(), anyBoolean(), any()); + ruleEngineCallService.processRestApiCallToRuleEngine(TENANT_ID, requestId, msg, true, anyConsumer); + + verify(tbClusterServiceMock).pushMsgToRuleEngine(TENANT_ID, TENANT_ID, msg, true, null); + } + + private TransportProtos.RestApiCallResponseMsgProto getResponse(UUID requestId, TbMsg msg) { + return TransportProtos.RestApiCallResponseMsgProto.newBuilder() + .setResponse(TbMsg.toByteString(msg)) + .setRequestIdMSB(requestId.getMostSignificantBits()) + .setRequestIdLSB(requestId.getLeastSignificantBits()) + .build(); + } +} diff --git a/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java b/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java index 28834a0ab7..33e7ccea23 100644 --- a/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java @@ -39,12 +39,13 @@ import java.util.concurrent.TimeoutException; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_REQUEST; @DaoSqlTest @TestPropertySource(properties = { "js.evaluator=local", - "js.max_script_body_size=50", + "js.max_script_body_size=10000", "js.max_total_args_size=50", "js.max_result_size=50", "js.local.max_errors=2", @@ -87,7 +88,7 @@ class NashornJsInvokeServiceTest extends AbstractControllerTest { @Test void givenSimpleScriptMultiThreadTestPerformance() throws ExecutionException, InterruptedException, TimeoutException { - int iterations = 1000*4; + int iterations = 1000 * 4; List> futures = new ArrayList<>(iterations); UUID scriptId = evalScript("return msg.temperature > 20 ;"); // warmup @@ -125,7 +126,7 @@ class NashornJsInvokeServiceTest extends AbstractControllerTest { @Test void givenTooBigScriptForEval_thenReturnError() { - String hugeScript = "var a = 'qwertyqwertywertyqwabababer'; return {a: a};"; + String hugeScript = "var a = '" + "a".repeat(10000) + "'; return {a: a};"; assertThatThrownBy(() -> { evalScript(hugeScript); @@ -159,6 +160,46 @@ class NashornJsInvokeServiceTest extends AbstractControllerTest { assertThatScriptIsBlocked(scriptId); } + @Test + void givenComplexScript_testCompile() { + String script = """ + function(data) { + if (data.get("propertyA") == "a special value 1" || data.get("propertyA") == "a special value 2") { + return "a special value 1"; + } else if (data.get("propertyB") == "a special value 3" && (data.get("propertyC") == "a special value 1" || data.get("propertyJ") == "a special value 1" || data.get("propertyV") == "a special value 1")) { + return "a special value 1"; + } else if (data.get("propertyB") == "4" && (data.get("propertyD") == "a special value 1" || data.get("propertyV") == "a special value 1" || data.get("propertyW") == "a special value 1")) { + return "a special value 1"; + } else if (data.get("propertyB") == "a special value 2" && (data.get("propertyE") == "a special value 1" || data.get("propertyF") == "a special value 1" || data.get("propertyL") == "a special value 1")) { + return "a special value 1"; + } else if (data.get("propertyB") == "a special value 3" && (data.get("propertyE") == "a special value 1" || data.get("propertyF") == "a special value 1" || data.get("propertyL") == "a special value 1")) { + return "a special value 1"; + } else if (data.get("propertyB") == "a special value 3" && (data.get("propertyM") == "a special value 1" || data.get("propertyY") == "a special value 1" || data.get("propertyH") == "a special value 1")) { + return "a special value 1"; + } else if (data.get("propertyB") == "a special value 3" && (data.get("propertyM") == "a special value 1" || data.get("propertyY") == "a special value 1" || data.get("propertyH") == "a special value 1")) { + return "a special value 1"; + } else if (data.get("propertyB") == "a special value 3" && (data.get("propertyM") == "a special value 1" || data.get("propertyY") == "a special value 1" || data.get("propertyH") == "a special value 1")) { + return "a special value 1"; + } else if (data.get("propertyB") == "a special value 3" && (data.get("propertyM") == "a special value 1" || data.get("propertyY") == "a special value 1" || data.get("propertyH") == "a special value 1")) { + return "a special value 1"; + } else if (data.get("propertyB") == "a special value 3" && (data.get("propertyM") == "a special value 1" || data.get("propertyY") == "a special value 1" || data.get("propertyH") == "a special value 1")) { + return "a special value 1"; + } else if (data.get("propertyB") == "a special value 3" && (data.get("propertyM") == "a special value 1" || data.get("propertyY") == "a special value 1" || data.get("propertyH") == "a special value 1")) { + return "a special value 1"; + } else if (data.get("propertyB") == "a special value 3" && (data.get("propertyM") == "a special value 1" || data.get("propertyY") == "a special value 1" || data.get("propertyH") == "a special value 1")) { + return "a special value 1"; + } else { + return "0" + }; + } + """; + + // with delight-nashorn-sandbox 0.4.2, this would throw delight.nashornsandbox.exceptions.ScriptCPUAbuseException: Regular expression running for too many iterations. The operation could NOT be gracefully interrupted. + assertDoesNotThrow(() -> { + evalScript(script); + }); + } + private void assertThatScriptIsBlocked(UUID scriptId) { assertThatThrownBy(() -> { invokeScript(scriptId, "{}"); diff --git a/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtilsTest.java b/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtilsTest.java index 7a2a97c169..9f77d1dbc2 100644 --- a/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtilsTest.java +++ b/application/src/test/java/org/thingsboard/server/service/security/auth/oauth2/CookieUtilsTest.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.service.security.auth.oauth2; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; import org.junit.Test; import org.mockito.Mockito; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; import java.util.LinkedHashMap; import java.util.Map; diff --git a/application/src/test/java/org/thingsboard/server/service/sms/DefaultSmsServiceTest.java b/application/src/test/java/org/thingsboard/server/service/sms/DefaultSmsServiceTest.java index bae67fe3a3..f16c5211ae 100644 --- a/application/src/test/java/org/thingsboard/server/service/sms/DefaultSmsServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/sms/DefaultSmsServiceTest.java @@ -27,7 +27,6 @@ import org.springframework.test.context.TestPropertySource; import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.AdminSettings; -import org.thingsboard.server.common.data.FeaturesInfo; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; diff --git a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java index f80c8c4c82..9833396a0a 100644 --- a/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/state/DefaultDeviceStateServiceTest.java @@ -27,7 +27,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; import org.thingsboard.server.cluster.TbClusterService; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.DeviceIdInfo; import org.thingsboard.server.common.data.id.DeviceId; @@ -79,7 +78,6 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.thingsboard.server.common.data.DataConstants.SERVER_SCOPE; import static org.thingsboard.server.service.state.DefaultDeviceStateService.ACTIVITY_STATE; import static org.thingsboard.server.service.state.DefaultDeviceStateService.INACTIVITY_ALARM_TIME; import static org.thingsboard.server.service.state.DefaultDeviceStateService.INACTIVITY_TIMEOUT; diff --git a/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java b/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java index 532b212af9..78e64b4f0a 100644 --- a/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java +++ b/application/src/test/java/org/thingsboard/server/system/BaseHttpDeviceApiTest.java @@ -20,6 +20,7 @@ import org.junit.Test; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.controller.AbstractControllerTest; @@ -29,6 +30,7 @@ import java.util.Map; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; +import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -40,6 +42,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @TestPropertySource(properties = { "transport.http.enabled=true", + "transport.http.max_payload_size=10000" }) public abstract class BaseHttpDeviceApiTest extends AbstractControllerTest { @@ -74,6 +77,44 @@ public abstract class BaseHttpDeviceApiTest extends AbstractControllerTest { doGetAsync("/api/v1/" + deviceCredentials.getCredentialsId() + "/attributes?clientKeys=keyA,keyB,keyC").andExpect(status().isOk()); } + @Test + public void testReplyToCommandWithLargeResponse() throws Exception { + String errorResponse = doPost("/api/v1/" + deviceCredentials.getCredentialsId() + "/rpc/5", + JacksonUtil.toString(createRpcResponsePayload(10001)), + String.class, + status().isPayloadTooLarge()); + assertThat(errorResponse).contains("Payload size exceeds the limit"); + + doPost("/api/v1/" + deviceCredentials.getCredentialsId() + "/rpc/5", + JacksonUtil.toString(createRpcResponsePayload(10000)), + String.class, + status().isOk()); + } + + @Test + public void testPostRpcRequestWithLargeResponse() throws Exception { + String errorResponse = doPost("/api/v1/" + deviceCredentials.getCredentialsId() + "/rpc", + JacksonUtil.toString(createRpcRequestPayload(10001)), + String.class, + status().isPayloadTooLarge()); + assertThat(errorResponse).contains("Payload size exceeds the limit"); + + doPost("/api/v1/" + deviceCredentials.getCredentialsId() + "/rpc", + JacksonUtil.toString(createRpcRequestPayload(10000)), + String.class, + status().isOk()); + } + + private String createRpcResponsePayload(int size) { + String value = "a".repeat(size - 19); + return "{\"result\":\"" + value + "\"}"; + } + + private String createRpcRequestPayload(int size) { + String value = "a".repeat(size - 50); + return "{\"method\":\"get\",\"params\":{\"value\":\"" + value + "\"}}"; + } + protected ResultActions doGetAsync(String urlTemplate, Object... urlVariables) throws Exception { MockHttpServletRequestBuilder getRequest; getRequest = get(urlTemplate, urlVariables); diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesIntegrationTest.java index a1bf7932e9..dbeb61c267 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/attributes/updates/CoapAttributesUpdatesIntegrationTest.java @@ -16,7 +16,6 @@ package org.thingsboard.server.transport.coap.attributes.updates; import lombok.extern.slf4j.Slf4j; -import org.eclipse.californium.core.server.resources.Resource; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -25,11 +24,8 @@ import org.thingsboard.server.coapserver.DefaultCoapServerService; import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.transport.coap.CoapTestConfigProperties; -import org.thingsboard.server.transport.coap.CoapTransportResource; import org.thingsboard.server.transport.coap.attributes.AbstractCoapAttributesIntegrationTest; -import static org.mockito.Mockito.spy; - @Slf4j @DaoSqlTest public class CoapAttributesUpdatesIntegrationTest extends AbstractCoapAttributesIntegrationTest { diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java index 2684f210ca..e5c9cbf469 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.security.sql; +import jakarta.servlet.http.HttpServletResponse; import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.core.util.Hex; import org.junit.Test; @@ -24,7 +25,6 @@ import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCred import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; -import jakarta.servlet.http.HttpServletResponse; import java.nio.charset.StandardCharsets; import static org.eclipse.leshan.client.object.Security.psk; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java index b6ebfc442e..c33d4b5059 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.security.sql; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.core.util.Hex; @@ -25,7 +26,6 @@ import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCred import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; -import jakarta.servlet.http.HttpServletResponse; import java.security.PrivateKey; import java.security.cert.X509Certificate; diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java index fc9df4e69b..452deef586 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java @@ -45,8 +45,8 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestCallback; -import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestSubscribeOnTopicCallback; import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestClient; +import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestSubscribeOnTopicCallback; import java.util.ArrayList; import java.util.List; diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/provision/MqttProvisionJsonDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/provision/MqttProvisionJsonDeviceTest.java index 27ed65f54a..e82612963f 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/provision/MqttProvisionJsonDeviceTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/provision/MqttProvisionJsonDeviceTest.java @@ -35,8 +35,8 @@ import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties; import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestCallback; -import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestSubscribeOnTopicCallback; import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestClient; +import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestSubscribeOnTopicCallback; import java.util.concurrent.TimeUnit; diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/provision/MqttProvisionProtoDeviceTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/provision/MqttProvisionProtoDeviceTest.java index 0926459513..2411d39004 100644 --- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/provision/MqttProvisionProtoDeviceTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/provision/MqttProvisionProtoDeviceTest.java @@ -43,8 +43,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509Ce import org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest; import org.thingsboard.server.transport.mqtt.MqttTestConfigProperties; import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestCallback; -import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestSubscribeOnTopicCallback; import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestClient; +import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestSubscribeOnTopicCallback; import java.util.concurrent.TimeUnit; diff --git a/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java b/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java index 301ef69dcc..ec37a5a864 100644 --- a/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java +++ b/common/cluster-api/src/main/java/org/thingsboard/server/cluster/TbClusterService.java @@ -18,7 +18,6 @@ package org.thingsboard.server.cluster; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; @@ -39,6 +38,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToVersionControlServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.RestApiCallResponseMsgProto; import org.thingsboard.server.queue.TbQueueCallback; import org.thingsboard.server.queue.TbQueueClusterService; @@ -58,10 +58,14 @@ public interface TbClusterService extends TbQueueClusterService { void pushNotificationToCore(String targetServiceId, FromDeviceRpcResponse response, TbQueueCallback callback); + void pushNotificationToCore(String targetServiceId, RestApiCallResponseMsgProto msg, TbQueueCallback callback); + void pushMsgToRuleEngine(TopicPartitionInfo tpi, UUID msgId, ToRuleEngineMsg msg, TbQueueCallback callback); void pushMsgToRuleEngine(TenantId tenantId, EntityId entityId, TbMsg msg, TbQueueCallback callback); + void pushMsgToRuleEngine(TenantId tenantId, EntityId entityId, TbMsg msg, boolean useQueueFromTbMsg, TbQueueCallback callback); + void pushNotificationToRuleEngine(String targetServiceId, FromDeviceRpcResponse response, TbQueueCallback callback); void pushNotificationToTransport(String targetServiceId, ToTransportMsg response, TbQueueCallback callback); diff --git a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java index 508103a1db..3ef97a0dee 100644 --- a/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java +++ b/common/coap-server/src/main/java/org/thingsboard/server/coapserver/DefaultCoapServerService.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.coapserver; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.core.CoapServer; import org.eclipse.californium.core.config.CoapConfig; @@ -27,8 +29,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.common.util.ThingsBoardThreadFactory; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java index d6e652c0fc..c7a7fa7798 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java @@ -19,6 +19,7 @@ package org.thingsboard.server.dao.cassandra; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.jmx.JmxReporter; import com.datastax.oss.driver.api.core.ConsistencyLevel; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -29,7 +30,6 @@ import org.thingsboard.server.dao.cassandra.guava.GuavaSession; import org.thingsboard.server.dao.cassandra.guava.GuavaSessionBuilder; import org.thingsboard.server.dao.cassandra.guava.GuavaSessionUtils; -import jakarta.annotation.PreDestroy; import java.nio.file.Paths; @Slf4j diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java index c1ebb98f08..3655efa1e6 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraCluster.java @@ -15,12 +15,11 @@ */ package org.thingsboard.server.dao.cassandra; +import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.thingsboard.server.dao.util.NoSqlAnyDao; -import jakarta.annotation.PostConstruct; - @Component("CassandraCluster") @NoSqlAnyDao public class CassandraCluster extends AbstractCassandraCluster { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraDriverOptions.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraDriverOptions.java index 23ddf6c96f..848d33eedb 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraDriverOptions.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraDriverOptions.java @@ -22,6 +22,7 @@ import com.datastax.oss.driver.api.core.config.DriverConfigLoader; import com.datastax.oss.driver.api.core.config.ProgrammaticDriverConfigLoaderBuilder; import com.datastax.oss.driver.api.core.metrics.DefaultNodeMetric; import com.datastax.oss.driver.api.core.metrics.DefaultSessionMetric; +import jakarta.annotation.PostConstruct; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; @@ -29,7 +30,6 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.dao.util.NoSqlAnyDao; -import jakarta.annotation.PostConstruct; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java index 776c9db852..acbcadcb81 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraInstallCluster.java @@ -15,12 +15,11 @@ */ package org.thingsboard.server.dao.cassandra; +import jakarta.annotation.PostConstruct; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; import org.thingsboard.server.dao.util.NoSqlAnyDao; -import jakarta.annotation.PostConstruct; - @Component("CassandraInstallCluster") @NoSqlAnyDao @Profile("install") diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java index 86b12cfd86..8f3a5934fc 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationService.java @@ -19,8 +19,8 @@ import org.thingsboard.server.common.data.id.NotificationId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.Notification; -import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; +import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java index c3ecf80268..42f635cd43 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java @@ -17,12 +17,12 @@ package org.thingsboard.server.dao.user; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.mobile.MobileSessionInfo; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UserCredentialsId; import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.mobile.MobileSessionInfo; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.UserCredentials; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index 398cd17aa1..7972a75340 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty.Access; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.media.Schema; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index 238ef8744c..81c7c7683b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -17,13 +17,13 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.Valid; import java.util.HashSet; import java.util.Objects; import java.util.Set; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SaveDeviceWithCredentialsRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/SaveDeviceWithCredentialsRequest.java index 24991bb71d..002deb1a8d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/SaveDeviceWithCredentialsRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SaveDeviceWithCredentialsRequest.java @@ -16,10 +16,11 @@ package org.thingsboard.server.common.data; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import lombok.Data; import org.thingsboard.server.common.data.security.DeviceCredentials; -import jakarta.validation.constraints.NotNull;; +; @Schema @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java index 0bd8e3a687..a7cfd7fc90 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java @@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmId; -import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java index 4cf3d5a790..acdf4c5cc4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java @@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.alarm; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Data; import lombok.ToString; @@ -28,9 +30,6 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotNull; - @Data @Builder public class AlarmCreateOrUpdateActiveRequest implements AlarmModificationRequest { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java index 1556e5fa73..c98b652102 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java @@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.alarm; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Data; import lombok.ToString; @@ -25,9 +27,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotNull; - @Data @Builder public class AlarmUpdateRequest implements AlarmModificationRequest { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java index d3c0889d6b..db56acb8b2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java @@ -40,6 +40,7 @@ public enum ActionType { RELATION_ADD_OR_UPDATE(false, TbMsgType.RELATION_ADD_OR_UPDATE), RELATION_DELETED(false, TbMsgType.RELATION_DELETED), RELATIONS_DELETED(false, TbMsgType.RELATIONS_DELETED), + REST_API_RULE_ENGINE_CALL(false, null), // log call to rule engine from REST API ALARM_ACK(false, TbMsgType.ALARM_ACK), ALARM_CLEAR(false, TbMsgType.ALARM_CLEAR), ALARM_DELETE(false, TbMsgType.ALARM_DELETE), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java index 5983c4dd06..d74f21989c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmCondition.java @@ -17,9 +17,9 @@ package org.thingsboard.server.common.data.device.profile; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; import lombok.Data; -import jakarta.validation.Valid; import java.io.Serializable; import java.util.List; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java index 2f6e20f013..58e8935629 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmConditionFilter.java @@ -16,12 +16,12 @@ package org.thingsboard.server.common.data.device.profile; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; import lombok.Data; import org.thingsboard.server.common.data.query.EntityKeyValueType; import org.thingsboard.server.common.data.query.KeyFilterPredicate; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.Valid; import java.io.Serializable; @Schema diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java index a42b0523d2..9743ed1e03 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/AlarmRule.java @@ -16,11 +16,11 @@ package org.thingsboard.server.common.data.device.profile; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; import lombok.Data; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.Valid; import java.io.Serializable; @Schema diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java index ebf39ceec9..edb4857441 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileAlarm.java @@ -16,12 +16,12 @@ package org.thingsboard.server.common.data.device.profile; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; import lombok.Data; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.Valid; import java.io.Serializable; import java.util.List; import java.util.TreeMap; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java index 3c1b684c74..d35ba34781 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileData.java @@ -16,9 +16,9 @@ package org.thingsboard.server.common.data.device.profile; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; import lombok.Data; -import jakarta.validation.Valid; import java.io.Serializable; import java.util.List; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java index 9254789527..99378dbc7c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java @@ -17,7 +17,6 @@ package org.thingsboard.server.common.data.kv; import jakarta.validation.Valid; import lombok.Data; - import java.util.Optional; /** diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbMsgType.java b/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbMsgType.java index 763f245a09..9aa1994ab6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbMsgType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/msg/TbMsgType.java @@ -67,6 +67,7 @@ public enum TbMsgType { PROVISION_SUCCESS, PROVISION_FAILURE, SEND_EMAIL, + REST_API_REQUEST("REST API request"), // tellSelfOnly types GENERATOR_NODE_SELF_MSG(null, true), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java index 0dd7b7fc47..4ad15dad7c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequest.java @@ -16,6 +16,8 @@ package org.thingsboard.server.common.data.notification; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -33,8 +35,6 @@ import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.notification.info.NotificationInfo; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; import java.util.List; import java.util.UUID; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java index 3a7dfc06f1..a6896d8996 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestConfig.java @@ -15,9 +15,8 @@ */ package org.thingsboard.server.common.data.notification; -import lombok.Data; - import jakarta.validation.constraints.Max; +import lombok.Data; @Data public class NotificationRequestConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/DefaultNotificationRuleRecipientsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/DefaultNotificationRuleRecipientsConfig.java index d7423f9f17..494295d19d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/DefaultNotificationRuleRecipientsConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/DefaultNotificationRuleRecipientsConfig.java @@ -15,10 +15,10 @@ */ package org.thingsboard.server.common.data.notification.rule; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import lombok.EqualsAndHashCode; -import jakarta.validation.constraints.NotEmpty; import java.util.List; import java.util.Map; import java.util.UUID; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/EscalatedNotificationRuleRecipientsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/EscalatedNotificationRuleRecipientsConfig.java index ef18d027a8..cc1b25632e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/EscalatedNotificationRuleRecipientsConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/EscalatedNotificationRuleRecipientsConfig.java @@ -15,10 +15,10 @@ */ package org.thingsboard.server.common.data.notification.rule; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import lombok.EqualsAndHashCode; -import jakarta.validation.constraints.NotEmpty; import java.util.List; import java.util.Map; import java.util.UUID; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java index ccd6afdd5b..dc05dcc74d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java @@ -16,6 +16,10 @@ package org.thingsboard.server.common.data.notification.rule; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -31,10 +35,6 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.Notif import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.Valid; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import java.io.Serializable; import java.util.List; import java.util.stream.Collectors; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java index 092c2e1672..b7253ee6bd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRuleRecipientsConfig.java @@ -20,10 +20,10 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import jakarta.validation.constraints.NotNull; import lombok.Data; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; -import jakarta.validation.constraints.NotNull; import java.io.Serializable; import java.util.List; import java.util.Map; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java index b99f19dae8..57db13b13e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmAssignmentNotificationRuleTriggerConfig.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import jakarta.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -22,7 +23,6 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; -import jakarta.validation.constraints.NotEmpty; import java.util.Set; @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java index aea8846d8a..6bea5a5b4a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/AlarmNotificationRuleTriggerConfig.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import jakarta.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -22,7 +23,6 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; -import jakarta.validation.constraints.NotEmpty; import java.io.Serializable; import java.util.Set; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java index f092c1e4e1..2db07ef854 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/DeviceActivityNotificationRuleTriggerConfig.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import jakarta.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import jakarta.validation.constraints.NotEmpty; import java.util.Set; import java.util.UUID; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java index f23cd7906e..e4c6665575 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/EntitiesLimitNotificationRuleTriggerConfig.java @@ -15,13 +15,13 @@ */ package org.thingsboard.server.common.data.notification.rule.trigger.config; +import jakarta.validation.constraints.Max; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.EntityType; -import jakarta.validation.constraints.Max; import java.util.Set; @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java index ea47537b93..9476c9bb36 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/NotificationSettings.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.common.data.notification.settings; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.Data; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotNull; import java.io.Serializable; import java.util.Map; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/SlackNotificationDeliveryMethodConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/SlackNotificationDeliveryMethodConfig.java index 8d65eb3523..2a40cf131c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/SlackNotificationDeliveryMethodConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/SlackNotificationDeliveryMethodConfig.java @@ -15,11 +15,10 @@ */ package org.thingsboard.server.common.data.notification.settings; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; -import jakarta.validation.constraints.NotEmpty; - @Data public class SlackNotificationDeliveryMethodConfig implements NotificationDeliveryMethodConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/UserNotificationSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/UserNotificationSettings.java index 903a239459..fc16211bcd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/UserNotificationSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/settings/UserNotificationSettings.java @@ -18,14 +18,14 @@ package org.thingsboard.server.common.data.notification.settings; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; import lombok.Data; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; -import jakarta.validation.Valid; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotNull; import java.util.Collections; import java.util.Map; import java.util.Set; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/MicrosoftTeamsNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/MicrosoftTeamsNotificationTargetConfig.java index aa68f7aa40..e07abe7c00 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/MicrosoftTeamsNotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/MicrosoftTeamsNotificationTargetConfig.java @@ -15,11 +15,10 @@ */ package org.thingsboard.server.common.data.notification.targets; -import lombok.Data; -import lombok.EqualsAndHashCode; - import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java index 6e45f04e65..afaf1bf2fa 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.notification.targets; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; @@ -26,10 +29,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; - @Data @EqualsAndHashCode(callSuper = true) public class NotificationTarget extends BaseData implements HasTenantId, HasName, ExportableEntity { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/CustomerUsersFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/CustomerUsersFilter.java index 40a863b7d8..3eb3e8680c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/CustomerUsersFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/CustomerUsersFilter.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.common.data.notification.targets.platform; +import jakarta.validation.constraints.NotNull; import lombok.Data; -import jakarta.validation.constraints.NotNull; import java.util.UUID; @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/PlatformUsersNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/PlatformUsersNotificationTargetConfig.java index 0792cb2d07..eb4a3e9c61 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/PlatformUsersNotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/PlatformUsersNotificationTargetConfig.java @@ -15,14 +15,13 @@ */ package org.thingsboard.server.common.data.notification.targets.platform; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotNull; - @Data @EqualsAndHashCode(callSuper = true) public class PlatformUsersNotificationTargetConfig extends NotificationTargetConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UserListFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UserListFilter.java index eea1921f18..bab5e6dfb4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UserListFilter.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/platform/UserListFilter.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.common.data.notification.targets.platform; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; -import jakarta.validation.constraints.NotEmpty; import java.util.List; import java.util.UUID; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java index 63b1f990e3..c0d29913c8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackConversation.java @@ -17,6 +17,8 @@ package org.thingsboard.server.common.data.notification.targets.slack; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -24,9 +26,6 @@ import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.notification.targets.NotificationRecipient; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - import static org.apache.commons.lang3.StringUtils.isEmpty; @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackNotificationTargetConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackNotificationTargetConfig.java index fbf10c370f..adfb07b20f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackNotificationTargetConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/slack/SlackNotificationTargetConfig.java @@ -15,14 +15,13 @@ */ package org.thingsboard.server.common.data.notification.targets.slack; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.notification.targets.NotificationTargetConfig; import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotNull; - @Data @EqualsAndHashCode(callSuper = true) public class SlackNotificationTargetConfig extends NotificationTargetConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java index 9cb1e103d0..63109eca40 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/DeliveryMethodNotificationTemplate.java @@ -20,11 +20,11 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; -import jakarta.validation.constraints.NotEmpty; import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java index acd78e4ffd..448af20915 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/EmailDeliveryMethodNotificationTemplate.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.notification.template; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -23,7 +24,6 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.constraints.NotEmpty; import java.util.List; @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java index 54c11be1e4..f06c73ca63 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.common.data.notification.template; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; @@ -27,10 +30,6 @@ import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - @Data @EqualsAndHashCode(callSuper = true) public class NotificationTemplate extends BaseData implements HasTenantId, HasName, ExportableEntity { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java index 59e50c7885..0aa092ba6a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.common.data.notification.template; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; import java.util.HashMap; import java.util.Map; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java index 55ee91d988..f723b77d97 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/WebDeliveryMethodNotificationTemplate.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -27,7 +28,6 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.constraints.NotEmpty; import java.util.List; import java.util.Optional; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java index aa15466d0e..15c0641731 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.oauth2; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -25,7 +26,6 @@ import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; import org.thingsboard.server.common.data.validation.Length; -import jakarta.validation.Valid; import java.util.List; @EqualsAndHashCode(callSuper = true) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java index be13a032e2..fdf62d7e53 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java @@ -16,13 +16,12 @@ package org.thingsboard.server.common.data.oauth2; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import jakarta.validation.Valid; - @Builder(toBuilder = true) @EqualsAndHashCode @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java index 6183edf461..b5f71165f6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/FilterPredicateValue.java @@ -18,11 +18,11 @@ package org.thingsboard.server.common.data.query; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; import lombok.Data; import lombok.Getter; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.Valid; import java.io.Serializable; @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java index 89d9dc4653..d5aa81f313 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/StringFilterPredicate.java @@ -15,9 +15,8 @@ */ package org.thingsboard.server.common.data.query; -import lombok.Data; - import jakarta.validation.Valid; +import lombok.Data; @Data public class StringFilterPredicate implements SimpleKeyFilterPredicate { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java index 04d50dfe6a..6c648daf02 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/QueueStats.java @@ -19,8 +19,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasTenantId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.QueueStatsId; +import org.thingsboard.server.common.data.id.TenantId; @EqualsAndHashCode(callSuper = true) @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java index a334a5f67c..bd2cd1fad7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java @@ -16,14 +16,14 @@ package org.thingsboard.server.common.data.security.model.mfa; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; -import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig; -import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; - import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig; +import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; + import java.util.List; import java.util.Optional; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/BackupCodeTwoFaAccountConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/BackupCodeTwoFaAccountConfig.java index fe3636b475..b4eac00687 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/BackupCodeTwoFaAccountConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/BackupCodeTwoFaAccountConfig.java @@ -16,11 +16,11 @@ package org.thingsboard.server.common.data.security.model.mfa.account; import com.fasterxml.jackson.annotation.JsonGetter; +import jakarta.validation.constraints.NotEmpty; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; -import jakarta.validation.constraints.NotEmpty; import java.util.Set; @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/EmailTwoFaAccountConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/EmailTwoFaAccountConfig.java index 2b9542e67b..38ac6f32c1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/EmailTwoFaAccountConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/EmailTwoFaAccountConfig.java @@ -15,13 +15,12 @@ */ package org.thingsboard.server.common.data.security.model.mfa.account; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; - @Data @EqualsAndHashCode(callSuper = true) public class EmailTwoFaAccountConfig extends OtpBasedTwoFaAccountConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/SmsTwoFaAccountConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/SmsTwoFaAccountConfig.java index 9c9359796a..c8ee5af278 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/SmsTwoFaAccountConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/SmsTwoFaAccountConfig.java @@ -15,13 +15,12 @@ */ package org.thingsboard.server.common.data.security.model.mfa.account; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; - @EqualsAndHashCode(callSuper = true) @Data public class SmsTwoFaAccountConfig extends OtpBasedTwoFaAccountConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TotpTwoFaAccountConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TotpTwoFaAccountConfig.java index f0450daed1..3dd8f95a5b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TotpTwoFaAccountConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/account/TotpTwoFaAccountConfig.java @@ -15,13 +15,12 @@ */ package org.thingsboard.server.common.data.security.model.mfa.account; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Pattern; - @Data @EqualsAndHashCode(callSuper = true) public class TotpTwoFaAccountConfig extends TwoFaAccountConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java index ed8ef1d2a1..30894b3d3a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/BackupCodeTwoFaProviderConfig.java @@ -15,9 +15,8 @@ */ package org.thingsboard.server.common.data.security.model.mfa.provider; -import lombok.Data; - import jakarta.validation.constraints.Min; +import lombok.Data; @Data public class BackupCodeTwoFaProviderConfig implements TwoFaProviderConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/OtpBasedTwoFaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/OtpBasedTwoFaProviderConfig.java index a2c167a76a..196360e514 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/OtpBasedTwoFaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/OtpBasedTwoFaProviderConfig.java @@ -15,9 +15,8 @@ */ package org.thingsboard.server.common.data.security.model.mfa.provider; -import lombok.Data; - import jakarta.validation.constraints.Min; +import lombok.Data; @Data public abstract class OtpBasedTwoFaProviderConfig implements TwoFaProviderConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/SmsTwoFaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/SmsTwoFaProviderConfig.java index 79ae76331c..f145eb0cb2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/SmsTwoFaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/SmsTwoFaProviderConfig.java @@ -15,11 +15,10 @@ */ package org.thingsboard.server.common.data.security.model.mfa.provider; -import lombok.Data; -import lombok.EqualsAndHashCode; - import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; +import lombok.Data; +import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TotpTwoFaProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TotpTwoFaProviderConfig.java index d9ccad10e0..5e281f3158 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TotpTwoFaProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/provider/TotpTwoFaProviderConfig.java @@ -15,9 +15,8 @@ */ package org.thingsboard.server.common.data.security.model.mfa.provider; -import lombok.Data; - import jakarta.validation.constraints.NotBlank; +import lombok.Data; @Data public class TotpTwoFaProviderConfig implements TwoFaProviderConfig { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/validation/Length.java b/common/data/src/main/java/org/thingsboard/server/common/data/validation/Length.java index 3437530387..31c31d893a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/validation/Length.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/validation/Length.java @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/validation/NoXss.java b/common/data/src/main/java/org/thingsboard/server/common/data/validation/NoXss.java index 2ca8f546f4..4fad289c5b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/validation/NoXss.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/validation/NoXss.java @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.validation; import jakarta.validation.Constraint; import jakarta.validation.Payload; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/UUIDConverterTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/UUIDConverterTest.java index 909eb4c160..52bc53c02b 100644 --- a/common/data/src/test/java/org/thingsboard/server/common/data/UUIDConverterTest.java +++ b/common/data/src/test/java/org/thingsboard/server/common/data/UUIDConverterTest.java @@ -16,8 +16,8 @@ package org.thingsboard.server.common.data; import com.datastax.oss.driver.api.core.uuid.Uuids; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/audit/ActionTypeTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/audit/ActionTypeTest.java index f746f47e19..6a5ed54896 100644 --- a/common/data/src/test/java/org/thingsboard/server/common/data/audit/ActionTypeTest.java +++ b/common/data/src/test/java/org/thingsboard/server/common/data/audit/ActionTypeTest.java @@ -28,6 +28,7 @@ import static org.thingsboard.server.common.data.audit.ActionType.DELETED_COMMEN import static org.thingsboard.server.common.data.audit.ActionType.LOCKOUT; import static org.thingsboard.server.common.data.audit.ActionType.LOGIN; import static org.thingsboard.server.common.data.audit.ActionType.LOGOUT; +import static org.thingsboard.server.common.data.audit.ActionType.REST_API_RULE_ENGINE_CALL; import static org.thingsboard.server.common.data.audit.ActionType.RPC_CALL; import static org.thingsboard.server.common.data.audit.ActionType.SMS_SENT; import static org.thingsboard.server.common.data.audit.ActionType.SUSPENDED; @@ -45,7 +46,8 @@ class ActionTypeTest { LOGOUT, LOCKOUT, DELETED_COMMENT, - SMS_SENT + SMS_SENT, + REST_API_RULE_ENGINE_CALL ); // backward-compatibility tests diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponseActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponseActorMsg.java index 6eec68fa2a..36b13dae15 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponseActorMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/FromDeviceRpcResponseActorMsg.java @@ -20,7 +20,6 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; -import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; @Data public class FromDeviceRpcResponseActorMsg implements ToDeviceActorNotificationMsg { diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequestActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequestActorMsg.java index 3e397a477b..e013a621fd 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequestActorMsg.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/rpc/ToDeviceRpcRequestActorMsg.java @@ -20,7 +20,6 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.msg.MsgType; import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; -import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; /** * Created by ashvayka on 16.04.18. diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/tools/MaxPayloadSizeExceededException.java b/common/message/src/main/java/org/thingsboard/server/common/msg/tools/MaxPayloadSizeExceededException.java new file mode 100644 index 0000000000..8195bf19a7 --- /dev/null +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/tools/MaxPayloadSizeExceededException.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.msg.tools; + +public class MaxPayloadSizeExceededException extends RuntimeException { + + public MaxPayloadSizeExceededException() { + super("Payload size exceeds the limit"); + } +} diff --git a/common/proto/src/main/proto/queue.proto b/common/proto/src/main/proto/queue.proto index cc309afac0..900d521a57 100644 --- a/common/proto/src/main/proto/queue.proto +++ b/common/proto/src/main/proto/queue.proto @@ -101,6 +101,12 @@ message SessionInfoProto { bool isGateway = 18; } +message RestApiCallResponseMsgProto { + int64 requestIdMSB = 1; + int64 requestIdLSB = 2; + bytes response = 5; +} + enum SessionEvent { OPEN = 0; CLOSED = 1; @@ -1487,6 +1493,7 @@ message ToCoreNotificationMsg { ToEdgeSyncRequestMsgProto toEdgeSyncRequest = 11; FromEdgeSyncResponseMsgProto fromEdgeSyncResponse = 12; ResourceCacheInvalidateMsg resourceCacheInvalidateMsg = 13; + RestApiCallResponseMsgProto restApiCallResponseMsg = 50; } /* Messages that are handled by ThingsBoard RuleEngine Service */ diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusQueueConfigs.java b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusQueueConfigs.java index 093e2f2c19..45d39d1cd6 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusQueueConfigs.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/azure/servicebus/TbServiceBusQueueConfigs.java @@ -15,13 +15,13 @@ */ package org.thingsboard.server.queue.azure.servicebus; +import jakarta.annotation.PostConstruct; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.queue.util.PropertyUtils; -import jakarta.annotation.PostConstruct; import java.util.Map; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java index ac263c10c8..9513565ca1 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/AbstractTbQueueConsumerTemplate.java @@ -15,13 +15,13 @@ */ package org.thingsboard.server.queue.common; +import jakarta.annotation.Nonnull; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.queue.TbQueueConsumer; import org.thingsboard.server.queue.TbQueueMsg; -import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java index b7a3db45a0..950519b098 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/common/DefaultTbQueueRequestTemplate.java @@ -18,6 +18,7 @@ package org.thingsboard.server.queue.common; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import jakarta.annotation.Nullable; import lombok.Builder; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -33,7 +34,6 @@ import org.thingsboard.server.queue.TbQueueMsgMetadata; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.TbQueueRequestTemplate; -import jakarta.annotation.Nullable; import java.util.List; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java index ddc17bb8ee..4caa3b92b3 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/DefaultTbServiceInfoProvider.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.discovery; +import jakarta.annotation.PostConstruct; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -29,10 +30,8 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; import org.thingsboard.server.queue.util.AfterContextReady; -import jakarta.annotation.PostConstruct; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java index 97b83fd878..20b2710bbf 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/ZkDiscoveryService.java @@ -17,6 +17,8 @@ package org.thingsboard.server.queue.discovery; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.ProtocolStringList; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.curator.framework.CuratorFramework; @@ -43,8 +45,6 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.event.OtherServiceShutdownEvent; import org.thingsboard.server.queue.util.AfterStartUp; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/OtherServiceShutdownEvent.java b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/OtherServiceShutdownEvent.java index 04258258b8..85ef812693 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/OtherServiceShutdownEvent.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/discovery/event/OtherServiceShutdownEvent.java @@ -15,11 +15,9 @@ */ package org.thingsboard.server.queue.discovery.event; -import com.google.protobuf.ProtocolStringList; import lombok.Getter; import org.thingsboard.server.common.msg.queue.ServiceType; -import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/environment/EnvironmentLogService.java b/common/queue/src/main/java/org/thingsboard/server/queue/environment/EnvironmentLogService.java index 45508ed029..07853855e9 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/environment/EnvironmentLogService.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/environment/EnvironmentLogService.java @@ -15,13 +15,12 @@ */ package org.thingsboard.server.queue.environment; +import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.apache.zookeeper.Environment; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; -import jakarta.annotation.PostConstruct; - /** * Created by igor on 11/24/16. */ diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java index 0cb1194167..2bd283f1e5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaAdmin.java @@ -15,10 +15,8 @@ */ package org.thingsboard.server.queue.kafka; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.admin.CreateTopicsResult; -import org.apache.kafka.clients.admin.ListConsumerGroupOffsetsResult; import org.apache.kafka.clients.admin.NewTopic; import org.apache.kafka.clients.consumer.OffsetAndMetadata; import org.apache.kafka.common.TopicPartition; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java index c55d8a5b8a..6f237894c2 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java @@ -15,13 +15,13 @@ */ package org.thingsboard.server.queue.kafka; +import jakarta.annotation.PostConstruct; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import org.thingsboard.server.queue.util.PropertyUtils; -import jakarta.annotation.PostConstruct; import java.util.Map; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java index 26ec51097f..d40a8bac85 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -41,8 +42,8 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; @@ -55,7 +56,6 @@ import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java index 0c4fe824fa..05e435d7fb 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -39,8 +40,8 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; @@ -53,7 +54,6 @@ import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java index 000578333a..7e6129a748 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbRuleEngineQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -45,7 +46,6 @@ import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java index aa79349dac..b14bb8d8e7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbVersionControlQueueFactory.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.gen.transport.TransportProtos; @@ -31,8 +32,6 @@ import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; -import jakarta.annotation.PreDestroy; - @Component @ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && '${service.type:null}'=='tb-vc-executor'") public class AwsSqsTbVersionControlQueueFactory implements TbVersionControlQueueFactory { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java index ec07847631..407ef86a99 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTransportQueueFactory.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; @@ -43,8 +44,6 @@ import org.thingsboard.server.queue.sqs.TbAwsSqsProducerTemplate; import org.thingsboard.server.queue.sqs.TbAwsSqsQueueAttributes; import org.thingsboard.server.queue.sqs.TbAwsSqsSettings; -import jakarta.annotation.PreDestroy; - @Component @ConditionalOnExpression("'${queue.type:null}'=='aws-sqs' && (('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport')") @Slf4j diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java index 310bc75fe4..225290ed63 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java @@ -28,8 +28,8 @@ import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.memory.InMemoryStorage; import org.thingsboard.server.queue.memory.InMemoryTbQueueConsumer; import org.thingsboard.server.queue.memory.InMemoryTbQueueProducer; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index 9728642b8d..87be03c8c7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -55,7 +56,6 @@ import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicLong; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index ddb2be8f2a..f16f21ad66 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -54,7 +55,6 @@ import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicLong; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java index 31fa7efffb..0b320ba617 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbRuleEngineQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -37,8 +38,8 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.kafka.TbKafkaAdmin; import org.thingsboard.server.queue.kafka.TbKafkaConsumerStatsService; import org.thingsboard.server.queue.kafka.TbKafkaConsumerTemplate; @@ -50,7 +51,6 @@ import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicLong; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java index 40904c7c65..dd260840f5 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbTransportQueueFactory.java @@ -15,10 +15,10 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; @@ -46,8 +46,6 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; - @Component @ConditionalOnExpression("'${queue.type:null}'=='kafka' && (('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport')") @Slf4j diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbVersionControlQueueFactory.java index c6633b030c..aacebb8579 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbVersionControlQueueFactory.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; @@ -37,8 +37,6 @@ import org.thingsboard.server.queue.kafka.TbKafkaTopicConfigs; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import jakarta.annotation.PreDestroy; - @Component @ConditionalOnExpression("'${queue.type:null}'=='kafka' && '${service.type:null}'=='tb-vc-executor'") public class KafkaTbVersionControlQueueFactory implements TbVersionControlQueueFactory { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java index a3734daa91..00f4a1ec4e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -55,7 +56,6 @@ import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java index c48559467d..091909d8f2 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -52,7 +53,6 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java index 3895971655..3e607d0bf9 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbRuleEngineQueueFactory.java @@ -16,13 +16,13 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.queue.Queue; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; @@ -50,7 +50,6 @@ import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java index 3faf35bbc7..b6da4e2973 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbVersionControlQueueFactory.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.gen.transport.TransportProtos; @@ -31,8 +32,6 @@ import org.thingsboard.server.queue.pubsub.TbPubSubSubscriptionSettings; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import jakarta.annotation.PreDestroy; - @Component @ConditionalOnExpression("'${queue.type:null}'=='pubsub' && '${service.type:null}'=='tb-vc-executor'") public class PubSubTbVersionControlQueueFactory implements TbVersionControlQueueFactory { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java index 9f993a553d..a7500d2083 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTransportQueueFactory.java @@ -15,10 +15,10 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; -import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg; @@ -45,8 +45,6 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; - @Component @ConditionalOnExpression("'${queue.type:null}'=='pubsub' && (('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport')") @Slf4j diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java index 40f4fdc2e5..cf60012449 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -55,7 +56,6 @@ import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java index 06fe34c458..57440422be 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -51,7 +52,6 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java index 8d7016404a..a0a87714ca 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbRuleEngineQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -36,8 +37,8 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.DefaultTbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqAdmin; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqConsumerTemplate; import org.thingsboard.server.queue.rabbitmq.TbRabbitMqProducerTemplate; @@ -48,7 +49,6 @@ import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java index d2842a3492..604955be2c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbVersionControlQueueFactory.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.gen.transport.TransportProtos; @@ -31,8 +32,6 @@ import org.thingsboard.server.queue.rabbitmq.TbRabbitMqSettings; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import jakarta.annotation.PreDestroy; - @Component @ConditionalOnExpression("'${queue.type:null}'=='rabbitmq' && '${service.type:null}'=='tb-vc-executor'") public class RabbitMqTbVersionControlQueueFactory implements TbVersionControlQueueFactory { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java index 13df3414d8..9b8a3d3c49 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTransportQueueFactory.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; @@ -44,8 +45,6 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; - @Component @ConditionalOnExpression("'${queue.type:null}'=='rabbitmq' && (('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport')") @Slf4j diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java index c52ec7f7b2..2ec807498c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -54,7 +55,6 @@ import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java index 37fcfad1bd..493e58dba7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -52,7 +53,6 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java index cd2c52c21d..92ef01fa00 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbRuleEngineQueueFactory.java @@ -16,6 +16,7 @@ package org.thingsboard.server.queue.provider; import com.google.protobuf.util.JsonFormat; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -48,7 +49,6 @@ import org.thingsboard.server.queue.settings.TbQueueRemoteJsInvokeSettings; import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; import java.nio.charset.StandardCharsets; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java index c3447a1012..0c363488ce 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbVersionControlQueueFactory.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.gen.transport.TransportProtos; @@ -31,8 +32,6 @@ import org.thingsboard.server.queue.discovery.TopicService; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; import org.thingsboard.server.queue.settings.TbQueueVersionControlSettings; -import jakarta.annotation.PreDestroy; - @Component @ConditionalOnExpression("'${queue.type:null}'=='service-bus' && '${service.type:null}'=='tb-vc-executor'") public class ServiceBusTbVersionControlQueueFactory implements TbVersionControlQueueFactory { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java index f2ed799905..fd493eb2e1 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTransportQueueFactory.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; @@ -44,8 +45,6 @@ import org.thingsboard.server.queue.settings.TbQueueRuleEngineSettings; import org.thingsboard.server.queue.settings.TbQueueTransportApiSettings; import org.thingsboard.server.queue.settings.TbQueueTransportNotificationSettings; -import jakarta.annotation.PreDestroy; - @Component @ConditionalOnExpression("'${queue.type:null}'=='service-bus' && (('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true') || '${service.type:null}'=='tb-transport')") @Slf4j diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java index 3dcd716a96..3fcb49f47c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PostConstruct; import org.springframework.stereotype.Service; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; @@ -28,8 +29,6 @@ import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.util.TbCoreComponent; -import jakarta.annotation.PostConstruct; - @Service @TbCoreComponent public class TbCoreQueueProducerProvider implements TbQueueProducerProvider { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java index 768d6b9c24..908369ea15 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbRuleEngineProducerProvider.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PostConstruct; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; import org.thingsboard.server.gen.transport.TransportProtos; @@ -28,8 +29,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import jakarta.annotation.PostConstruct; - @Service @ConditionalOnExpression("'${service.type:null}'=='tb-rule-engine'") public class TbRuleEngineProducerProvider implements TbQueueProducerProvider { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java index 010eaf404f..df394cd997 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbTransportQueueProducerProvider.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PostConstruct; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; import org.thingsboard.server.gen.transport.TransportProtos; @@ -28,8 +29,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import jakarta.annotation.PostConstruct; - @Service @ConditionalOnExpression("'${service.type:null}'=='tb-transport'") public class TbTransportQueueProducerProvider implements TbQueueProducerProvider { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java index 1e697c644a..7547c73f71 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbVersionControlProducerProvider.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.provider; +import jakarta.annotation.PostConstruct; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; import org.thingsboard.server.gen.transport.TransportProtos; @@ -28,8 +29,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceM import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import jakarta.annotation.PostConstruct; - @Service @ConditionalOnExpression("'${service.type:null}'=='tb-vc-executor'") public class TbVersionControlProducerProvider implements TbQueueProducerProvider { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java index 692673b41d..8a2ba90096 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubConsumerTemplate.java @@ -17,7 +17,6 @@ package org.thingsboard.server.queue.pubsub; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutures; -import com.google.api.gax.core.FixedExecutorProvider; import com.google.cloud.pubsub.v1.stub.GrpcSubscriberStub; import com.google.cloud.pubsub.v1.stub.SubscriberStub; import com.google.cloud.pubsub.v1.stub.SubscriberStubSettings; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java index 83331ccfe1..bb1d4e8976 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubProducerTemplate.java @@ -18,7 +18,6 @@ package org.thingsboard.server.queue.pubsub; import com.google.api.core.ApiFuture; import com.google.api.core.ApiFutureCallback; import com.google.api.core.ApiFutures; -import com.google.api.gax.core.FixedExecutorProvider; import com.google.cloud.pubsub.v1.Publisher; import com.google.gson.Gson; import com.google.protobuf.ByteString; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java index 8652b16eca..a16d3d275f 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSettings.java @@ -19,6 +19,8 @@ import com.google.api.gax.core.CredentialsProvider; import com.google.api.gax.core.FixedCredentialsProvider; import com.google.api.gax.core.FixedExecutorProvider; import com.google.auth.oauth2.ServiceAccountCredentials; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -26,8 +28,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.common.util.ThingsBoardThreadFactory; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.concurrent.Executors; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSubscriptionSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSubscriptionSettings.java index 3b67d6b282..2faf4b1985 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSubscriptionSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/pubsub/TbPubSubSubscriptionSettings.java @@ -15,13 +15,13 @@ */ package org.thingsboard.server.queue.pubsub; +import jakarta.annotation.PostConstruct; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.queue.util.PropertyUtils; -import jakarta.annotation.PostConstruct; import java.util.Map; @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java index b9b7f319df..acd008fb1d 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqQueueArguments.java @@ -15,13 +15,13 @@ */ package org.thingsboard.server.queue.rabbitmq; +import jakarta.annotation.PostConstruct; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; -import jakarta.annotation.PostConstruct; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqSettings.java index 72e1e7b902..432bce1d01 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/rabbitmq/TbRabbitMqSettings.java @@ -16,14 +16,13 @@ package org.thingsboard.server.queue.rabbitmq; import com.rabbitmq.client.ConnectionFactory; +import jakarta.annotation.PostConstruct; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; -import jakarta.annotation.PostConstruct; - @Slf4j @ConditionalOnExpression("'${queue.type:null}'=='rabbitmq'") @Component diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/scheduler/DefaultSchedulerComponent.java b/common/queue/src/main/java/org/thingsboard/server/queue/scheduler/DefaultSchedulerComponent.java index 9dc2785005..a166259256 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/scheduler/DefaultSchedulerComponent.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/scheduler/DefaultSchedulerComponent.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.queue.scheduler; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.springframework.stereotype.Component; import org.thingsboard.common.util.ThingsBoardThreadFactory; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java index e5dfe7738c..92def925d7 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsAdmin.java @@ -26,15 +26,12 @@ import com.amazonaws.services.sqs.model.CreateQueueRequest; import com.amazonaws.services.sqs.model.GetQueueUrlResult; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.common.util.ExecutorProvider; import org.thingsboard.common.util.ThingsBoardExecutors; -import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.queue.TbQueueAdmin; import org.thingsboard.server.queue.util.PropertyUtils; import java.util.Map; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.function.Function; import java.util.stream.Collectors; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java index f9c2ad0e7a..0c61c2d2eb 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/sqs/TbAwsSqsQueueAttributes.java @@ -16,13 +16,13 @@ package org.thingsboard.server.queue.sqs; import com.amazonaws.services.sqs.model.QueueAttributeName; +import jakarta.annotation.PostConstruct; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.StringUtils; -import jakarta.annotation.PostConstruct; import java.util.HashMap; import java.util.Map; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageReportClient.java b/common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageReportClient.java index 4faf5e1a31..e8502145ec 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageReportClient.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/usagestats/DefaultTbApiUsageReportClient.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.queue.usagestats; +import jakarta.annotation.PostConstruct; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -37,7 +38,6 @@ import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.scheduler.SchedulerComponent; -import jakarta.annotation.PostConstruct; import java.util.EnumMap; import java.util.Random; import java.util.UUID; diff --git a/common/script/remote-js-client/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java b/common/script/remote-js-client/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java index 159ff59cea..51244ade4b 100644 --- a/common/script/remote-js-client/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java +++ b/common/script/remote-js-client/src/main/java/org/thingsboard/server/service/script/RemoteJsInvokeService.java @@ -18,6 +18,8 @@ package org.thingsboard.server.service.script; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -37,8 +39,6 @@ import org.thingsboard.server.queue.TbQueueRequestTemplate; import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.Map; import java.util.Optional; import java.util.UUID; diff --git a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java index 852f7a054f..d188111cc3 100644 --- a/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java +++ b/common/script/script-api/src/main/java/org/thingsboard/script/api/tbel/TbUtils.java @@ -61,7 +61,9 @@ public class TbUtils { private static final int HEX_LEN_MIN = -1; private static final int HEX_LEN_INT_MAX = 8; private static final int HEX_LEN_LONG_MAX = 16; + private static final int BYTES_LEN_INT_MAX = 4; private static final int BYTES_LEN_LONG_MAX = 8; + private static final int BIN_LEN_MAX = 8; private static final LinkedHashMap mdnEncodingReplacements = new LinkedHashMap<>(); @@ -117,6 +119,8 @@ public class TbUtils { String.class))); parserConfig.addImport("parseFloat", new MethodStub(TbUtils.class.getMethod("parseFloat", String.class, int.class))); + parserConfig.addImport("parseHexIntLongToFloat", new MethodStub(TbUtils.class.getMethod("parseHexIntLongToFloat", + String.class, boolean.class))); parserConfig.addImport("parseDouble", new MethodStub(TbUtils.class.getMethod("parseDouble", String.class))); parserConfig.addImport("parseLittleEndianHexToInt", new MethodStub(TbUtils.class.getMethod("parseLittleEndianHexToInt", @@ -127,10 +131,18 @@ public class TbUtils { String.class))); parserConfig.addImport("parseHexToInt", new MethodStub(TbUtils.class.getMethod("parseHexToInt", String.class, boolean.class))); + parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", + List.class))); + parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", + List.class, int.class))); parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", List.class, int.class, int.class))); parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", List.class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", + byte[].class))); + parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", + byte[].class, int.class))); parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", byte[].class, int.class, int.class))); parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", @@ -143,10 +155,18 @@ public class TbUtils { String.class))); parserConfig.addImport("parseHexToLong", new MethodStub(TbUtils.class.getMethod("parseHexToLong", String.class, boolean.class))); + parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", + List.class))); + parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", + List.class, int.class))); parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", List.class, int.class, int.class))); parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", List.class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", + byte[].class))); + parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", + byte[].class, int.class))); parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", byte[].class, int.class, int.class))); parserConfig.addImport("parseBytesToLong", new MethodStub(TbUtils.class.getMethod("parseBytesToLong", @@ -160,13 +180,37 @@ public class TbUtils { parserConfig.addImport("parseHexToFloat", new MethodStub(TbUtils.class.getMethod("parseHexToFloat", String.class, boolean.class))); parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", - byte[].class, int.class, boolean.class))); + List.class))); + parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", + List.class, int.class))); + parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", + List.class, int.class, int.class))); + parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", + List.class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", + byte[].class))); parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", byte[].class, int.class))); parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", - List.class, int.class, boolean.class))); + byte[].class, int.class, int.class))); parserConfig.addImport("parseBytesToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesToFloat", + byte[].class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesIntToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesIntToFloat", + List.class))); + parserConfig.addImport("parseBytesIntToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesIntToFloat", List.class, int.class))); + parserConfig.addImport("parseBytesIntToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesIntToFloat", + List.class, int.class, int.class))); + parserConfig.addImport("parseBytesIntToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesIntToFloat", + List.class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesIntToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesIntToFloat", + byte[].class))); + parserConfig.addImport("parseBytesIntToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesIntToFloat", + byte[].class, int.class))); + parserConfig.addImport("parseBytesIntToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesIntToFloat", + byte[].class, int.class, int.class))); + parserConfig.addImport("parseBytesIntToFloat", new MethodStub(TbUtils.class.getMethod("parseBytesIntToFloat", + byte[].class, int.class, int.class, boolean.class))); parserConfig.addImport("parseLittleEndianHexToDouble", new MethodStub(TbUtils.class.getMethod("parseLittleEndianHexToDouble", String.class))); parserConfig.addImport("parseBigEndianHexToDouble", new MethodStub(TbUtils.class.getMethod("parseBigEndianHexToDouble", @@ -176,13 +220,37 @@ public class TbUtils { parserConfig.addImport("parseHexToDouble", new MethodStub(TbUtils.class.getMethod("parseHexToDouble", String.class, boolean.class))); parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", - byte[].class, int.class))); - parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", - byte[].class, int.class, boolean.class))); + List.class))); parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", List.class, int.class))); parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", - List.class, int.class, boolean.class))); + List.class, int.class, int.class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + List.class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + byte[].class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + byte[].class, int.class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + byte[].class, int.class, int.class))); + parserConfig.addImport("parseBytesToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesToDouble", + byte[].class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesLongToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesLongToDouble", + List.class))); + parserConfig.addImport("parseBytesLongToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesLongToDouble", + List.class, int.class))); + parserConfig.addImport("parseBytesLongToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesLongToDouble", + List.class, int.class, int.class))); + parserConfig.addImport("parseBytesLongToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesLongToDouble", + List.class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesLongToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesLongToDouble", + byte[].class))); + parserConfig.addImport("parseBytesLongToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesLongToDouble", + byte[].class, int.class))); + parserConfig.addImport("parseBytesLongToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesLongToDouble", + byte[].class, int.class, int.class))); + parserConfig.addImport("parseBytesLongToDouble", new MethodStub(TbUtils.class.getMethod("parseBytesLongToDouble", + byte[].class, int.class, int.class, boolean.class))); parserConfig.addImport("toFixed", new MethodStub(TbUtils.class.getMethod("toFixed", double.class, int.class))); parserConfig.addImport("toFixed", new MethodStub(TbUtils.class.getMethod("toFixed", @@ -201,17 +269,17 @@ public class TbUtils { Long.class))); parserConfig.addImport("longToHex", new MethodStub(TbUtils.class.getMethod("longToHex", Long.class, boolean.class))); - parserConfig.addImport("longToHex", new MethodStub(TbUtils.class.getMethod("longToHex", + parserConfig.addImport("longToHex", new MethodStub(TbUtils.class.getMethod("longToHex", Long.class, boolean.class, boolean.class))); - parserConfig.addImport("longToHex", new MethodStub(TbUtils.class.getMethod("longToHex", + parserConfig.addImport("longToHex", new MethodStub(TbUtils.class.getMethod("longToHex", Long.class, boolean.class, boolean.class, int.class))); - parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString", + parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString", Long.class))); - parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString", + parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString", Long.class, int.class))); - parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString", + parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString", Long.class, int.class, boolean.class))); - parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString", + parserConfig.addImport("intLongToString", new MethodStub(TbUtils.class.getMethod("intLongToString", Long.class, int.class, boolean.class, boolean.class))); parserConfig.addImport("floatToHex", new MethodStub(TbUtils.class.getMethod("floatToHex", Float.class))); @@ -225,6 +293,8 @@ public class TbUtils { ExecutionContext.class, List.class))); parserConfig.addImport("base64ToHex", new MethodStub(TbUtils.class.getMethod("base64ToHex", String.class))); + parserConfig.addImport("hexToBase64", new MethodStub(TbUtils.class.getMethod("hexToBase64", + String.class))); parserConfig.addImport("base64ToBytes", new MethodStub(TbUtils.class.getMethod("base64ToBytes", String.class))); parserConfig.addImport("bytesToBase64", new MethodStub(TbUtils.class.getMethod("bytesToBase64", @@ -257,6 +327,44 @@ public class TbUtils { String.class))); parserConfig.addImport("isHexadecimal", new MethodStub(TbUtils.class.getMethod("isHexadecimal", String.class))); + parserConfig.addImport("byteArrayToExecutionArrayList", new MethodStub(TbUtils.class.getMethod("bytesToExecutionArrayList", + ExecutionContext.class, byte[].class))); + parserConfig.addImport("padStart", new MethodStub(TbUtils.class.getMethod("padStart", + String.class, int.class, char.class))); + parserConfig.addImport("padEnd", new MethodStub(TbUtils.class.getMethod("padEnd", + String.class, int.class, char.class))); + parserConfig.addImport("parseByteToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseByteToBinaryArray", + byte.class))); + parserConfig.addImport("parseByteToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseByteToBinaryArray", + byte.class, int.class))); + parserConfig.addImport("parseByteToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseByteToBinaryArray", + byte.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseBytesToBinaryArray", + List.class))); + parserConfig.addImport("parseBytesToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseBytesToBinaryArray", + List.class, int.class))); + parserConfig.addImport("parseBytesToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseBytesToBinaryArray", + byte[].class))); + parserConfig.addImport("parseBytesToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseBytesToBinaryArray", + byte[].class))); + parserConfig.addImport("parseBytesToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseBytesToBinaryArray", + byte[].class, int.class))); + parserConfig.addImport("parseLongToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseLongToBinaryArray", + long.class))); + parserConfig.addImport("parseLongToBinaryArray", new MethodStub(TbUtils.class.getMethod("parseLongToBinaryArray", + long.class, int.class))); + parserConfig.addImport("parseBinaryArrayToInt", new MethodStub(TbUtils.class.getMethod("parseBinaryArrayToInt", + List.class))); + parserConfig.addImport("parseBinaryArrayToInt", new MethodStub(TbUtils.class.getMethod("parseBinaryArrayToInt", + List.class, int.class))); + parserConfig.addImport("parseBinaryArrayToInt", new MethodStub(TbUtils.class.getMethod("parseBinaryArrayToInt", + List.class, int.class, int.class))); + parserConfig.addImport("parseBinaryArrayToInt", new MethodStub(TbUtils.class.getMethod("parseBinaryArrayToInt", + byte[].class))); + parserConfig.addImport("parseBinaryArrayToInt", new MethodStub(TbUtils.class.getMethod("parseBinaryArrayToInt", + byte[].class, int.class))); + parserConfig.addImport("parseBinaryArrayToInt", new MethodStub(TbUtils.class.getMethod("parseBinaryArrayToInt", + byte[].class, int.class, int.class))); } public static String btoa(String input) { @@ -334,10 +442,14 @@ public class TbUtils { } public static Integer parseInt(String value, int radix) { - if (StringUtils.isNotBlank(value)) { - String valueP = prepareNumberString(value); + return parseInt(value, radix, true); + } + + private static Integer parseInt(String value, int radix, boolean bigEndian) { + String valueP = prepareNumberString(value, bigEndian); + if (valueP != null) { int radixValue = isValidStringAndRadix(valueP, radix, value); - if (radixValue >= 25 && radixValue <= MAX_RADIX) { + if (radixValue >= 25 && radixValue <= MAX_RADIX) { return (Integer) compareIntLongValueMinMax(valueP, radixValue, Integer.MAX_VALUE, Integer.MIN_VALUE); } return switch (radixValue) { @@ -354,10 +466,14 @@ public class TbUtils { } public static Long parseLong(String value, int radix) { - if (StringUtils.isNotBlank(value)) { - String valueP = prepareNumberString(value); + return parseLong(value, radix, true); + } + + private static Long parseLong(String value, int radix, boolean bigEndian) { + String valueP = prepareNumberString(value, bigEndian); + if (valueP != null) { int radixValue = isValidStringAndRadix(valueP, radix, value); - if (radixValue >= 25 && radixValue <= MAX_RADIX) { + if (radixValue >= 25 && radixValue <= MAX_RADIX) { return (Long) compareIntLongValueMinMax(valueP, radixValue, Long.MAX_VALUE, Long.MIN_VALUE); } return switch (radixValue) { @@ -385,6 +501,7 @@ public class TbUtils { return Integer.parseInt(binaryString, MIN_RADIX); } } + private static long parseBinaryStringAsSignedLong(String binaryString) { if (binaryString.length() != 64) { // Pad the binary string to 64 bits if it is not already @@ -420,34 +537,58 @@ public class TbUtils { } public static Float parseFloat(String value) { - return parseFloat(value, 0); + return parseFloat(value, ZERO_RADIX); } public static Float parseFloat(String value, int radix) { - if (StringUtils.isNotBlank(value)) { - String valueP = prepareNumberString(value); - int radixValue = isValidStringAndRadix(valueP, radix, value); - if (radixValue == DEC_RADIX) { - return Float.parseFloat(value); - } else { - int bits = Integer.parseUnsignedInt(valueP, HEX_RADIX); - return Float.intBitsToFloat(bits); + String valueP = prepareNumberString(value, true); + if (valueP != null) { + return parseFloatFromString(value, valueP, radix); + } + return null; + } + + private static Float parseFloatFromString(String value, String valueP, int radix) { + int radixValue = isValidStringAndRadix(valueP, radix, value); + if (radixValue == HEX_RADIX) { + int bits = (int) Long.parseLong(valueP, HEX_RADIX); + // Hex representation is a standard IEEE 754 float value (eg "0x41200000" for 10.0f). + return Float.intBitsToFloat(bits); + } else { + return Float.parseFloat(value); + } + } + + public static Float parseHexIntLongToFloat(String value, boolean bigEndian) { + String valueP = prepareNumberString(value, bigEndian); + if (valueP != null) { + int radixValue = isValidStringAndRadix(valueP, HEX_RADIX, value); + if (radixValue == HEX_RADIX) { + int bits = (int) Long.parseLong(valueP, HEX_RADIX); + // If the length is not equal to 8 characters, we process it as an integer (eg "0x0A" for 10.0f). + float floatValue = (float) bits; + return Float.valueOf(floatValue); } } return null; } + public static Double parseDouble(String value) { int radix = getRadix10_16(value); return parseDouble(value, radix); } public static Double parseDouble(String value, int radix) { - if (value != null) { - String valueP = prepareNumberString(value); + return parseDouble(value, radix, true); + } + + private static Double parseDouble(String value, int radix, boolean bigEndian) { + String valueP = prepareNumberString(value, bigEndian); + if (valueP != null) { int radixValue = isValidStringAndRadix(valueP, radix, value); if (radixValue == DEC_RADIX) { - return Double.parseDouble(prepareNumberString(value)); + return Double.parseDouble(valueP); } else { long bits = Long.parseUnsignedLong(valueP, HEX_RADIX); return Double.longBitsToDouble(bits); @@ -469,9 +610,7 @@ public class TbUtils { } public static Integer parseHexToInt(String value, boolean bigEndian) { - String hexValue = prepareNumberString(value); - String hex = bigEndian ? hexValue : reverseHexStringByOrder(hexValue); - return parseInt(hex, HEX_RADIX); + return parseInt(value, HEX_RADIX, bigEndian); } public static long parseLittleEndianHexToLong(String hex) { @@ -487,9 +626,7 @@ public class TbUtils { } public static Long parseHexToLong(String value, boolean bigEndian) { - String hexValue = prepareNumberString(value); - String hex = bigEndian ? value : reverseHexStringByOrder(hexValue); - return parseLong(hex, HEX_RADIX); + return parseLong(value, HEX_RADIX, bigEndian); } public static float parseLittleEndianHexToFloat(String hex) { @@ -505,9 +642,11 @@ public class TbUtils { } public static Float parseHexToFloat(String value, boolean bigEndian) { - String hexValue = prepareNumberString(value); - String hex = bigEndian ? value : reverseHexStringByOrder(hexValue); - return parseFloat(hex, HEX_RADIX); + String valueP = prepareNumberString(value, bigEndian); + if (valueP != null) { + return parseFloatFromString(value, valueP, HEX_RADIX); + } + return null; } public static double parseLittleEndianHexToDouble(String hex) { @@ -523,27 +662,25 @@ public class TbUtils { } public static double parseHexToDouble(String value, boolean bigEndian) { - String hexValue = prepareNumberString(value); - String hex = bigEndian ? value : reverseHexStringByOrder(hexValue); - return parseDouble(hex, HEX_RADIX); + return parseDouble(value, HEX_RADIX, bigEndian); } public static ExecutionArrayList hexToBytes(ExecutionContext ctx, String value) { - String hex = prepareNumberString(value); + String hex = prepareNumberString(value, true); + if (hex == null) { + throw new IllegalArgumentException("Hex string must be not empty!"); + } int len = hex.length(); if (len % 2 > 0) { throw new IllegalArgumentException("Hex string must be even-length."); } - ExecutionArrayList data = new ExecutionArrayList<>(ctx); - for (int i = 0; i < hex.length(); i += 2) { - // Extract two characters from the hex string - String byteString = hex.substring(i, i + 2); - // Parse the hex string to a byte - byte byteValue = (byte) Integer.parseInt(byteString, HEX_RADIX); - // Add the byte to the ArrayList - data.add(byteValue); + int radix = isHexadecimal(value); + if (radix != HEX_RADIX) { + throw new NumberFormatException("Value: \"" + value + "\" is not numeric or hexDecimal format!"); } - return data; + + byte [] data = hexToBytes(hex); + return bytesToExecutionArrayList(ctx, data); } public static List printUnsignedBytes(ExecutionContext ctx, List byteArray) { @@ -575,6 +712,7 @@ public class TbUtils { public static String longToHex(Long l) { return prepareNumberHexString(l, true, false, HEX_LEN_MIN, HEX_LEN_LONG_MAX); } + public static String longToHex(Long l, boolean bigEndian) { return prepareNumberHexString(l, bigEndian, false, HEX_LEN_MIN, HEX_LEN_LONG_MAX); } @@ -604,7 +742,7 @@ public class TbUtils { return Long.toString(number, radix); } return switch (radix) { - case MIN_RADIX -> Long.toBinaryString(number); + case MIN_RADIX -> formatBinary(Long.toBinaryString(number)); case OCTAL_RADIX -> Long.toOctalString(number); case DEC_RADIX -> Long.toString(number); case HEX_RADIX -> prepareNumberHexString(number, bigEndian, pref, -1, -1); @@ -646,13 +784,13 @@ public class TbUtils { private static String removeLeadingZero_FF(String hex, Long number, int hexLenMax) { String hexWithoutZero = hex.replaceFirst("^0+(?!$)", ""); // Remove leading zeros except for the last one - hexWithoutZero = hexWithoutZero.length() % 2 > 0 ? "0" + hexWithoutZero : hexWithoutZero; + hexWithoutZero = hexWithoutZero.length() % 2 > 0 ? "0" + hexWithoutZero : hexWithoutZero; if (number >= 0) { return hexWithoutZero; } else { String hexWithoutZeroFF = hexWithoutZero.replaceFirst("^F+(?!$)", ""); - hexWithoutZeroFF = hexWithoutZeroFF.length() % 2 > 0 ? "F" + hexWithoutZeroFF : hexWithoutZeroFF; - if (hexWithoutZeroFF.length() > hexLenMax) { + hexWithoutZeroFF = hexWithoutZeroFF.length() % 2 > 0 ? "F" + hexWithoutZeroFF : hexWithoutZeroFF; + if (hexWithoutZeroFF.length() > hexLenMax) { return hexWithoutZeroFF.substring(hexWithoutZeroFF.length() - hexLenMax); } else if (hexWithoutZeroFF.length() == hexLenMax) { return hexWithoutZeroFF; @@ -668,7 +806,7 @@ public class TbUtils { public static String floatToHex(Float f, boolean bigEndian) { // Convert the float to its raw integer bits representation - int bits = Float.floatToRawIntBits(f); + int bits = Float.floatToIntBits(f); // Format the integer bits as a hexadecimal string String result = String.format("0x%08X", bits); @@ -683,7 +821,7 @@ public class TbUtils { long bits = Double.doubleToRawLongBits(d); // Format the integer bits as a hexadecimal string - String result = String.format("0x%16X", bits); + String result = String.format("0x%016X", bits); return bigEndian ? result : reverseHexStringByOrder(result); } @@ -691,6 +829,10 @@ public class TbUtils { return bytesToHex(Base64.getDecoder().decode(base64)); } + public static String hexToBase64(String hex) { + return bytesToBase64(hexToBytes(hex)); + } + public static String bytesToBase64(byte[] bytes) { return Base64.getEncoder().encodeToString(bytes); } @@ -699,16 +841,28 @@ public class TbUtils { return Base64.getDecoder().decode(input); } + public static int parseBytesToInt(List data) { + return parseBytesToInt(data, 0); + } + + public static int parseBytesToInt(List data, int offset) { + return parseBytesToInt(data, offset, validateLength(data.size(), offset, BYTES_LEN_INT_MAX)); + } + public static int parseBytesToInt(List data, int offset, int length) { return parseBytesToInt(data, offset, length, true); } public static int parseBytesToInt(List data, int offset, int length, boolean bigEndian) { - final byte[] bytes = new byte[data.size()]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = data.get(i); - } - return parseBytesToInt(bytes, offset, length, bigEndian); + return parseBytesToInt(Bytes.toArray(data), offset, length, bigEndian); + } + + public static int parseBytesToInt(byte[] data) { + return parseBytesToInt(data, 0); + } + + public static int parseBytesToInt(byte[] data, int offset) { + return parseBytesToInt(data, offset, validateLength(data.length, offset, BYTES_LEN_INT_MAX)); } public static int parseBytesToInt(byte[] data, int offset, int length) { @@ -716,15 +870,7 @@ public class TbUtils { } public static int parseBytesToInt(byte[] data, int offset, int length, boolean bigEndian) { - if (offset > data.length) { - throw new IllegalArgumentException("Offset: " + offset + " is out of bounds for array with length: " + data.length + "!"); - } - if (length > 4) { - throw new IllegalArgumentException("Length: " + length + " is too large. Maximum 4 bytes is allowed!"); - } - if (offset + length > data.length) { - throw new IllegalArgumentException("Offset: " + offset + " and Length: " + length + " is out of bounds for array with length: " + data.length + "!"); - } + validationNumberByLength(data, offset, length, BYTES_LEN_INT_MAX); var bb = ByteBuffer.allocate(4); if (!bigEndian) { bb.order(ByteOrder.LITTLE_ENDIAN); @@ -735,16 +881,28 @@ public class TbUtils { return bb.getInt(); } + public static long parseBytesToLong(List data) { + return parseBytesToLong(data, 0); + } + + public static long parseBytesToLong(List data, int offset) { + return parseBytesToLong(data, offset, validateLength(data.size(), offset, BYTES_LEN_LONG_MAX)); + } + public static long parseBytesToLong(List data, int offset, int length) { return parseBytesToLong(data, offset, length, true); } public static long parseBytesToLong(List data, int offset, int length, boolean bigEndian) { - final byte[] bytes = new byte[data.size()]; - for (int i = 0; i < bytes.length; i++) { - bytes[i] = data.get(i); - } - return parseBytesToLong(bytes, offset, length, bigEndian); + return parseBytesToLong(Bytes.toArray(data), offset, length, bigEndian); + } + + public static long parseBytesToLong(byte[] data) { + return parseBytesToLong(data, 0); + } + + public static long parseBytesToLong(byte[] data, int offset) { + return parseBytesToLong(data, offset, validateLength(data.length, offset, BYTES_LEN_LONG_MAX)); } public static long parseBytesToLong(byte[] data, int offset, int length) { @@ -752,67 +910,176 @@ public class TbUtils { } public static long parseBytesToLong(byte[] data, int offset, int length, boolean bigEndian) { - if (offset > data.length) { - throw new IllegalArgumentException("Offset: " + offset + " is out of bounds for array with length: " + data.length + "!"); - } - if (length > BYTES_LEN_LONG_MAX) { - throw new IllegalArgumentException("Length: " + length + " is too large. Maximum " + BYTES_LEN_LONG_MAX + " bytes is allowed!"); - } - if (offset + length > data.length) { - throw new IllegalArgumentException("Offset: " + offset + " and Length: " + length + " is out of bounds for array with length: " + data.length + "!"); - } - var bb = ByteBuffer.allocate(8); + validationNumberByLength(data, offset, length, BYTES_LEN_LONG_MAX); + var bb = ByteBuffer.allocate(BYTES_LEN_LONG_MAX); if (!bigEndian) { bb.order(ByteOrder.LITTLE_ENDIAN); } - bb.position(bigEndian ? 8 - length : 0); + bb.position(bigEndian ? BYTES_LEN_LONG_MAX - length : 0); bb.put(data, offset, length); bb.position(0); return bb.getLong(); } - public static float parseBytesToFloat(byte[] data, int offset) { - return parseBytesToFloat(data, offset, true); + public static float parseBytesToFloat(List data) { + return parseBytesToFloat(data, 0); } public static float parseBytesToFloat(List data, int offset) { - return parseBytesToFloat(data, offset, true); + return parseBytesToFloat(data, offset, validateLength(data.size(), offset, BYTES_LEN_INT_MAX)); } - public static float parseBytesToFloat(List data, int offset, boolean bigEndian) { - return parseBytesToFloat(Bytes.toArray(data), offset, bigEndian); + public static float parseBytesToFloat(List data, int offset, int length) { + return parseBytesToFloat(data, offset, length, true); } - public static float parseBytesToFloat(byte[] data, int offset, boolean bigEndian) { - byte[] bytesToNumber = prepareBytesToNumber(data, offset, 4, bigEndian); - return ByteBuffer.wrap(bytesToNumber).getFloat(); + public static float parseBytesToFloat(List data, int offset, int length, boolean bigEndian) { + return parseBytesToFloat(Bytes.toArray(data), offset, length, bigEndian); } + public static float parseBytesToFloat(byte[] data) { + return parseBytesToFloat(data, 0); + } - public static double parseBytesToDouble(byte[] data, int offset) { - return parseBytesToDouble(data, offset, true); + public static float parseBytesToFloat(byte[] data, int offset) { + return parseBytesToFloat(data, offset, validateLength(data.length, offset, BYTES_LEN_INT_MAX)); + } + + public static float parseBytesToFloat(byte[] data, int offset, int length) { + return parseBytesToFloat(data, offset, length, true); + } + + public static float parseBytesToFloat(byte[] data, int offset, int length, boolean bigEndian) { + var bb = ByteBuffer.allocate(BYTES_LEN_INT_MAX); + if (!bigEndian) { + bb.order(ByteOrder.LITTLE_ENDIAN); + } + bb.position(bigEndian ? BYTES_LEN_INT_MAX - length : 0); + bb.put(data, offset, length); + bb.position(0); + float floatValue = bb.getFloat(); + if (Float.isNaN(floatValue)) { + throw new NumberFormatException("byte[] 0x" + bytesToHex(data) + " is a Not-a-Number (NaN) value"); + } + return floatValue; + } + + public static float parseBytesIntToFloat(List data) { + return parseBytesIntToFloat(data, 0); + } + + public static float parseBytesIntToFloat(List data, int offset) { + return parseBytesIntToFloat(data, offset, validateLength(data.size(), offset, BYTES_LEN_INT_MAX)); + } + + public static float parseBytesIntToFloat(List data, int offset, int length) { + return parseBytesIntToFloat(data, offset, length, true); + } + + public static float parseBytesIntToFloat(List data, int offset, int length, boolean bigEndian) { + return parseBytesIntToFloat(Bytes.toArray(data), offset, length, bigEndian); + } + + public static float parseBytesIntToFloat(byte[] data) { + return parseBytesIntToFloat(data, 0); + } + + public static float parseBytesIntToFloat(byte[] data, int offset) { + return parseBytesIntToFloat(data, offset, validateLength(data.length, offset, BYTES_LEN_INT_MAX)); + } + + public static float parseBytesIntToFloat(byte[] data, int offset, int length) { + return parseBytesIntToFloat(data, offset, length, true); + } + + public static float parseBytesIntToFloat(byte[] data, int offset, int length, boolean bigEndian) { + byte[] bytesToNumber = prepareBytesToNumber(data, offset, length, bigEndian, BYTES_LEN_INT_MAX); + long longValue = parseBytesToLong(bytesToNumber, 0, length); + BigDecimal bigDecimalValue = new BigDecimal(longValue); + return bigDecimalValue.floatValue(); + } + + public static double parseBytesToDouble(List data) { + return parseBytesToDouble(data, 0); } public static double parseBytesToDouble(List data, int offset) { - return parseBytesToDouble(data, offset, true); + return parseBytesToDouble(data, offset, validateLength(data.size(), offset, BYTES_LEN_LONG_MAX)); } - public static double parseBytesToDouble(List data, int offset, boolean bigEndian) { - return parseBytesToDouble(Bytes.toArray(data), offset, bigEndian); + public static double parseBytesToDouble(List data, int offset, int length) { + return parseBytesToDouble(data, offset, length, true); } - public static double parseBytesToDouble(byte[] data, int offset, boolean bigEndian) { - byte[] bytesToNumber = prepareBytesToNumber(data, offset, BYTES_LEN_LONG_MAX, bigEndian); - return ByteBuffer.wrap(bytesToNumber).getDouble(); + public static double parseBytesToDouble(List data, int offset, int length, boolean bigEndian) { + return parseBytesToDouble(Bytes.toArray(data), offset, length, bigEndian); } - private static byte[] prepareBytesToNumber(byte[] data, int offset, int length, boolean bigEndian) { - if (offset > data.length) { - throw new IllegalArgumentException("Offset: " + offset + " is out of bounds for array with length: " + data.length + "!"); + public static double parseBytesToDouble(byte[] data) { + return parseBytesToDouble(data, 0); + } + + public static double parseBytesToDouble(byte[] data, int offset) { + return parseBytesToDouble(data, offset, validateLength(data.length, offset, BYTES_LEN_LONG_MAX)); + } + + public static double parseBytesToDouble(byte[] data, int offset, int length) { + return parseBytesToDouble(data, offset, length, true); + } + + public static double parseBytesToDouble(byte[] data, int offset, int length, boolean bigEndian) { + var bb = ByteBuffer.allocate(BYTES_LEN_LONG_MAX); + if (!bigEndian) { + bb.order(ByteOrder.LITTLE_ENDIAN); } - if ((offset + length) > data.length) { - throw new IllegalArgumentException("Default length is always " + length + " bytes. Offset: " + offset + " and Length: " + length + " is out of bounds for array with length: " + data.length + "!"); + bb.position(bigEndian ? BYTES_LEN_LONG_MAX - length : 0); + bb.put(data, offset, length); + bb.position(0); + double doubleValue = bb.getDouble(); + if (Double.isNaN(doubleValue)) { + throw new NumberFormatException("byte[] 0x" + bytesToHex(data) + " is a Not-a-Number (NaN) value"); } + return doubleValue; + } + + public static double parseBytesLongToDouble(List data) { + return parseBytesLongToDouble(data, 0); + } + + public static double parseBytesLongToDouble(List data, int offset) { + return parseBytesLongToDouble(data, offset, validateLength(data.size(), offset, BYTES_LEN_LONG_MAX)); + } + + public static double parseBytesLongToDouble(List data, int offset, int length) { + return parseBytesLongToDouble(data, offset, length, true); + } + + public static double parseBytesLongToDouble(List data, int offset, int length, boolean bigEndian) { + return parseBytesLongToDouble(Bytes.toArray(data), offset, length, bigEndian); + } + + public static double parseBytesLongToDouble(byte[] data) { + return parseBytesLongToDouble(data, 0); + } + + public static double parseBytesLongToDouble(byte[] data, int offset) { + return parseBytesLongToDouble(data, offset, validateLength(data.length, offset, BYTES_LEN_LONG_MAX)); + } + + public static double parseBytesLongToDouble(byte[] data, int offset, int length) { + return parseBytesLongToDouble(data, offset, length, true); + } + + public static double parseBytesLongToDouble(byte[] data, int offset, int length, boolean bigEndian) { + byte[] bytesToNumber = prepareBytesToNumber(data, offset, length, bigEndian, BYTES_LEN_LONG_MAX); + BigInteger bigInt = new BigInteger(1, bytesToNumber); + BigDecimal bigDecimalValue = new BigDecimal(bigInt); + return bigDecimalValue.doubleValue(); + } + + private static byte[] prepareBytesToNumber(byte[] data, int offset, int length, boolean bigEndian, + int bytesLenMax) { + validationNumberByLength(data, offset, length, bytesLenMax); byte[] dataBytesArray = Arrays.copyOfRange(data, offset, (offset + length)); if (!bigEndian) { ArrayUtils.reverse(dataBytesArray); @@ -931,14 +1198,15 @@ public class TbUtils { } } - private static String prepareNumberString(String value) { - if (value != null) { + private static String prepareNumberString(String value, boolean bigEndian) { + if (StringUtils.isNotBlank(value)) { value = value.trim(); value = value.replace("0x", ""); value = value.replace("0X", ""); value = value.replace(",", "."); + return bigEndian ? value : reverseHexStringByOrder(value); } - return value; + return null; } private static int isValidStringAndRadix(String valueP, int radix, String value) { @@ -1004,6 +1272,122 @@ public class TbUtils { return str.matches("^-?(0[xX])?[0-9a-fA-F]+$") ? HEX_RADIX : -1; } + public static ExecutionArrayList bytesToExecutionArrayList(ExecutionContext ctx, byte[] byteArray) { + List byteList = new ArrayList<>(); + for (byte b : byteArray) { + byteList.add(b); + } + ExecutionArrayList list = new ExecutionArrayList(byteList, ctx); + return list; + } + + public static String padStart(String str, int targetLength, char padString) { + while (str.length() < targetLength) { + str = padString + str; + } + return str; + } + + public static String padEnd(String str, int targetLength, char padString) { + while (str.length() < targetLength) { + str = str + padString; + } + return str; + } + + public static byte[] parseByteToBinaryArray(byte byteValue) { + return parseByteToBinaryArray(byteValue, BIN_LEN_MAX); + } + + public static byte[] parseByteToBinaryArray(byte byteValue, int binLength) { + return parseByteToBinaryArray(byteValue, binLength, true); + } + + /** + * bigEndian = true + * Writes the bit value to the appropriate location in the bins array, starting at the end of the array, + * to ensure proper alignment (highest bit to low end). + */ + public static byte[] parseByteToBinaryArray(byte byteValue, int binLength, boolean bigEndian) { + byte[] bins = new byte[binLength]; + for (int i = 0; i < binLength; i++) { + if(bigEndian) { + bins[binLength - 1 - i] = (byte) ((byteValue >> i) & 1); + } else { + bins[i] = (byte) ((byteValue >> i) & 1); + } + } + return bins; + } + + public static byte[] parseBytesToBinaryArray(List listValue) { + return parseBytesToBinaryArray(listValue, listValue.size() * BIN_LEN_MAX); + } + + public static byte[] parseBytesToBinaryArray(List listValue, int binLength) { + return parseBytesToBinaryArray(Bytes.toArray(listValue), binLength); + } + + public static byte[] parseBytesToBinaryArray(byte[] bytesValue) { + return parseLongToBinaryArray(parseBytesToLong(bytesValue), bytesValue.length * BIN_LEN_MAX); + } + + public static byte[] parseBytesToBinaryArray(byte[] bytesValue, int binLength) { + return parseLongToBinaryArray(parseBytesToLong(bytesValue), binLength); + } + + public static byte[] parseLongToBinaryArray(long longValue) { + return parseLongToBinaryArray(longValue, BYTES_LEN_LONG_MAX * BIN_LEN_MAX); + } + + /** + * Writes the bit value to the appropriate location in the bins array, starting at the end of the array, + * to ensure proper alignment (highest bit to low end). + */ + public static byte[] parseLongToBinaryArray(long longValue, int binsLength) { + int len = Math.min(binsLength, BYTES_LEN_LONG_MAX * BIN_LEN_MAX); + byte[] bins = new byte[len]; + for (int i = 0; i < len; i++) { + bins[len - 1 - i] = (byte) ((longValue >> i) & 1); + } + return bins; + } + + public static int parseBinaryArrayToInt(List listValue) { + return parseBinaryArrayToInt(listValue, 0); + } + + public static int parseBinaryArrayToInt(List listValue, int offset) { + return parseBinaryArrayToInt(listValue, offset, listValue.size()); + } + + public static int parseBinaryArrayToInt(List listValue, int offset, int length) { + return parseBinaryArrayToInt(Bytes.toArray(listValue), offset, length); + } + + public static int parseBinaryArrayToInt(byte[] bytesValue) { + return parseBinaryArrayToInt(bytesValue, 0); + } + + public static int parseBinaryArrayToInt(byte[] bytesValue, int offset) { + return parseBinaryArrayToInt(bytesValue, offset, bytesValue.length); + } + + public static int parseBinaryArrayToInt(byte[] bytesValue, int offset, int length) { + int result = 0; + int len = Math.min(length + offset, bytesValue.length); + for (int i = offset; i < len; i++) { + result = (result << 1) | (bytesValue[i] & 1); + } + + // For the one byte (8 bit) only If the most significant bit (sign) is set, we convert the result into a negative number + if ((bytesValue.length == BIN_LEN_MAX) + && offset == 0 && bytesValue[0] == 1) { + result -= (1 << (len - offset)); + } + return result; + } + private static byte isValidIntegerToByte(Integer val) { if (val > 255 || val < -128) { throw new NumberFormatException("The value '" + val + "' could not be correctly converted to a byte. " + @@ -1030,5 +1414,44 @@ public class TbUtils { String result = reversedHex.toString(); return isHexPref ? "0x" + result : result; } + + private static void validationNumberByLength(byte[] data, int offset, int length, int bytesLenMax) { + if (offset > data.length) { + throw new IllegalArgumentException("Offset: " + offset + " is out of bounds for array with length: " + data.length + "!"); + } + + if (offset + length > data.length) { + throw new IllegalArgumentException("Offset: " + offset + " and Length: " + length + " is out of bounds for array with length: " + data.length + "!"); + } + + if (length > bytesLenMax) { + throw new IllegalArgumentException("Length: " + length + " is too large. Maximum " + bytesLenMax + " bytes is allowed!"); + } + } + + private static int validateLength(int dataLength, int offset, int bytesLenMax) { + return (dataLength < offset) ? dataLength : Math.min((dataLength - offset), bytesLenMax); + } + + private static String formatBinary(String binaryString) { + int format = binaryString.length() < 8 ? 8 : + binaryString.length() < 16 ? 16 : + binaryString.length() < 32 ? 32 : + binaryString.length() < 64 ? 64 : 0; + return format == 0 ? binaryString : String.format("%" + format + "s", binaryString).replace(' ', '0'); + } + + private static byte[] hexToBytes(String hex) { + byte [] data = new byte[hex.length()/2]; + for (int i = 0; i < hex.length(); i += 2) { + // Extract two characters from the hex string + String byteString = hex.substring(i, i + 2); + // Parse the hex string to a byte + byte byteValue = (byte) Integer.parseInt(byteString, HEX_RADIX); + // Add the byte to the ArrayList + data[i/2] = byteValue; + } + return data; + } } diff --git a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java index 1d4e12c00e..0c22f62f1f 100644 --- a/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java +++ b/common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbUtilsTest.java @@ -30,6 +30,7 @@ import org.mvel2.execution.ExecutionArrayList; import org.mvel2.execution.ExecutionHashMap; import java.io.IOException; +import java.math.BigDecimal; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -47,7 +48,7 @@ public class TbUtilsTest { private ExecutionContext ctx; - private final float floatVal = 29.29824f; + private final Float floatVal = 29.29824f; private final float floatValRev = -5.948442E7f; @@ -225,11 +226,11 @@ public class TbUtilsTest { Assertions.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("0xFG")); - Assertions.assertEquals(java.util.Optional.of(102).get(), TbUtils.parseInt("1100110", 2)); - Assertions.assertEquals(java.util.Optional.of(-102).get(), TbUtils.parseInt("1111111111111111111111111111111111111111111111111111111110011010", 2)); - Assertions.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("1100210", 2)); - Assertions.assertEquals(java.util.Optional.of(13158).get(), TbUtils.parseInt("11001101100110", 2)); - Assertions.assertEquals(java.util.Optional.of(-13158).get(), TbUtils.parseInt("1111111111111111111111111111111111111111111111111100110010011010", 2)); + Assertions.assertEquals(java.util.Optional.of(102).get(), TbUtils.parseInt("1100110", MIN_RADIX)); + Assertions.assertEquals(java.util.Optional.of(-102).get(), TbUtils.parseInt("1111111111111111111111111111111111111111111111111111111110011010", MIN_RADIX)); + Assertions.assertThrows(NumberFormatException.class, () -> TbUtils.parseInt("1100210", MIN_RADIX)); + Assertions.assertEquals(java.util.Optional.of(13158).get(), TbUtils.parseInt("11001101100110", MIN_RADIX)); + Assertions.assertEquals(java.util.Optional.of(-13158).get(), TbUtils.parseInt("1111111111111111111111111111111111111111111111111100110010011010", MIN_RADIX)); Assertions.assertEquals(java.util.Optional.of(63).get(), TbUtils.parseInt("77", 8)); @@ -256,7 +257,7 @@ public class TbUtilsTest { @Test public void parseFloat() { - String floatValStr = "29.29824"; + String floatValStr = floatVal.toString(); Assertions.assertEquals(java.util.Optional.of(floatVal).get(), TbUtils.parseFloat(floatValStr)); String floatValHex = "41EA62CC"; Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseHexToFloat(floatValHex))); @@ -274,14 +275,113 @@ public class TbUtilsTest { } @Test - public void arseBytesToFloat() { - byte[] floatValByte = {65, -22, 98, -52}; - Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseBytesToFloat(floatValByte, 0))); - Assertions.assertEquals(0, Float.compare(floatValRev, TbUtils.parseBytesToFloat(floatValByte, 0, false))); + public void parseBytesToFloat() { + byte[] floatValByte = {0x0A}; + Assertions.assertEquals(0, Float.compare(1.4E-44f, TbUtils.parseBytesToFloat(floatValByte))); - List floatVaList = Bytes.asList(floatValByte); - Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseBytesToFloat(floatVaList, 0))); - Assertions.assertEquals(0, Float.compare(floatValRev, TbUtils.parseBytesToFloat(floatVaList, 0, false))); + floatValByte = new byte[]{65, -22, 98, -52}; + Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseBytesToFloat(floatValByte, 0))); + Assertions.assertEquals(0, Float.compare(floatValRev, TbUtils.parseBytesToFloat(floatValByte, 0, 4, false))); + + List floatValList = Bytes.asList(floatValByte); + Assertions.assertEquals(0, Float.compare(floatVal, TbUtils.parseBytesToFloat(floatValList, 0))); + Assertions.assertEquals(0, Float.compare(floatValRev, TbUtils.parseBytesToFloat(floatValList, 0, 4, false))); + + + // 1.1803216E8f == 0x4CE120E4 + floatValByte = new byte[]{0x4C, (byte) 0xE1, (byte) 0x20, (byte) 0xE4}; + float floatExpectedBe = 118.03216f; + float actualBe = TbUtils.parseBytesToFloat(floatValByte, 0, 4, true); + Assertions.assertEquals(0, Float.compare(floatExpectedBe, actualBe / 1000000)); + floatValList = Bytes.asList(floatValByte); + actualBe = TbUtils.parseBytesToFloat(floatValList, 0); + Assertions.assertEquals(0, Float.compare(floatExpectedBe, actualBe / 1000000)); + + float floatExpectedLe = 8.0821E-41f; + Assertions.assertEquals(0, Float.compare(floatExpectedLe, TbUtils.parseBytesToFloat(floatValByte, 0, 2, false))); + floatExpectedBe = 2.7579E-41f; + Assertions.assertEquals(0, Float.compare(floatExpectedBe, TbUtils.parseBytesToFloat(floatValByte, 0, 2))); + + + floatExpectedLe = 3.019557E-39f; + Assertions.assertEquals(0, Float.compare(floatExpectedLe, TbUtils.parseBytesToFloat(floatValList, 0, 3, false))); + + // 4 294 967 295L == {0xFF, 0xFF, 0xFF, 0xFF} + floatValByte = new byte[]{-1, -1, -1, -1}; + String message = "is a Not-a-Number (NaN) value"; + try { + TbUtils.parseBytesToFloat(floatValByte, 0, 4, true); + Assertions.fail("Should throw NumberFormatException"); + } catch (RuntimeException e) { + Assertions.assertTrue(e.getMessage().contains(message)); + } + + // "01752B0367FA000500010488 FFFFFFFF FFFFFFFF 33"; + String intToHexBe = "01752B0367FA000500010488FFFFFFFFFFFFFFFF33"; + floatValList = TbUtils.hexToBytes(ctx, intToHexBe); + try { + TbUtils.parseBytesToFloat(floatValList, 12, 4, false); + Assertions.fail("Should throw NumberFormatException"); + } catch (RuntimeException e) { + Assertions.assertTrue(e.getMessage().contains(message)); + } + } + + @Test + public void parseBytesIntToFloat() { + byte[] intValByte = {0x00, 0x00, 0x00, 0x0A}; + Float valueExpected = 10.0f; + Float valueActual = TbUtils.parseBytesIntToFloat(intValByte, 3, 1, true); + Assertions.assertEquals(valueExpected, valueActual); + valueActual = TbUtils.parseBytesIntToFloat(intValByte, 3, 1, false); + Assertions.assertEquals(valueExpected, valueActual); + + valueActual = TbUtils.parseBytesIntToFloat(intValByte, 2, 2, true); + Assertions.assertEquals(valueExpected, valueActual); + valueExpected = 2560.0f; + valueActual = TbUtils.parseBytesIntToFloat(intValByte, 2, 2, false); + Assertions.assertEquals(valueExpected, valueActual); + + valueExpected = 10.0f; + valueActual = TbUtils.parseBytesIntToFloat(intValByte, 0, 4, true); + Assertions.assertEquals(valueExpected, valueActual); + valueExpected = 1.6777216E8f; + valueActual = TbUtils.parseBytesIntToFloat(intValByte, 0, 4, false); + Assertions.assertEquals(valueExpected, valueActual); + + String dataAT101 = "0x01756403671B01048836BF7701F000090722050000"; + List byteAT101 = TbUtils.hexToBytes(ctx, dataAT101); + float latitudeExpected = 24.62495f; + int offset = 9; + valueActual = TbUtils.parseBytesIntToFloat(byteAT101, offset, 4, false); + Assertions.assertEquals(latitudeExpected, valueActual / 1000000); + + float longitudeExpected = 118.030576f; + valueActual = TbUtils.parseBytesIntToFloat(byteAT101, offset + 4, 4, false); + Assertions.assertEquals(longitudeExpected, valueActual / 1000000); + + valueExpected = 9.185175E8f; + valueActual = TbUtils.parseBytesIntToFloat(byteAT101, offset); + Assertions.assertEquals(valueExpected, valueActual); + // 0x36BF + valueExpected = 14015.0f; + valueActual = TbUtils.parseBytesIntToFloat(byteAT101, offset, 2); + Assertions.assertEquals(valueExpected, valueActual); + // 0xBF36 + valueExpected = 48950.0f; + valueActual = TbUtils.parseBytesIntToFloat(byteAT101, offset, 2, false); + Assertions.assertEquals(valueExpected, valueActual); + + valueExpected = 0.0f; + valueActual = TbUtils.parseBytesIntToFloat(byteAT101, byteAT101.size()); + Assertions.assertEquals(valueExpected, valueActual); + + try { + TbUtils.parseBytesIntToFloat(byteAT101, byteAT101.size() + 1); + Assertions.fail("Should throw NumberFormatException"); + } catch (RuntimeException e) { + Assertions.assertTrue(e.getMessage().contains("is out of bounds for array with length:")); + } } @Test @@ -297,9 +397,9 @@ public class TbUtilsTest { Assertions.assertEquals(java.util.Optional.of(4294967295L).get(), TbUtils.parseLong("FFFFFFFF")); Assertions.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong("0xFGFFFFFF")); - Assertions.assertEquals(java.util.Optional.of(13158L).get(), TbUtils.parseLong("11001101100110", 2)); - Assertions.assertEquals(java.util.Optional.of(-13158L).get(), TbUtils.parseLong("1111111111111111111111111111111111111111111111111100110010011010", 2)); - Assertions.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong("11001101100210", 2)); + Assertions.assertEquals(java.util.Optional.of(13158L).get(), TbUtils.parseLong("11001101100110", MIN_RADIX)); + Assertions.assertEquals(java.util.Optional.of(-13158L).get(), TbUtils.parseLong("1111111111111111111111111111111111111111111111111100110010011010", MIN_RADIX)); + Assertions.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong("11001101100210", MIN_RADIX)); Assertions.assertEquals(java.util.Optional.of(9223372036854775807L).get(), TbUtils.parseLong("777777777777777777777", 8)); Assertions.assertThrows(NumberFormatException.class, () -> TbUtils.parseLong("1787", 8)); @@ -354,13 +454,72 @@ public class TbUtilsTest { @Test public void parseBytesToDouble() { - byte[] doubleValByte = {64, -101, 4, -79, 12, -78, -107, -22}; - Assertions.assertEquals(0, Double.compare(doubleVal, TbUtils.parseBytesToDouble(doubleValByte, 0))); - Assertions.assertEquals(0, Double.compare(doubleValRev, TbUtils.parseBytesToDouble(doubleValByte, 0, false))); + byte[] doubleValByte = {0x0A}; + Assertions.assertEquals(0, Double.compare(4.9E-323, TbUtils.parseBytesToDouble(doubleValByte))); - List doubleVaList = Bytes.asList(doubleValByte); - Assertions.assertEquals(0, Double.compare(doubleVal, TbUtils.parseBytesToDouble(doubleVaList, 0))); - Assertions.assertEquals(0, Double.compare(doubleValRev, TbUtils.parseBytesToDouble(doubleVaList, 0, false))); + doubleValByte = new byte[]{64, -101, 4, -79, 12, -78, -107, -22}; + Assertions.assertEquals(0, Double.compare(doubleVal, TbUtils.parseBytesToDouble(doubleValByte, 0))); + Assertions.assertEquals(0, Double.compare(doubleValRev, TbUtils.parseBytesToDouble(doubleValByte, 0, 8, false))); + + List doubleValList = Bytes.asList(doubleValByte); + Assertions.assertEquals(0, Double.compare(doubleVal, TbUtils.parseBytesToDouble(doubleValList, 0))); + Assertions.assertEquals(0, Double.compare(doubleValRev, TbUtils.parseBytesToDouble(doubleValList, 0, 8, false))); + + doubleValByte = new byte[]{0x7F, (byte) 0xC0, (byte) 0xFF, 0x00, 0x7F, (byte) 0xC0, (byte) 0xFF, 0x00}; + double doubleExpectedBe = 2387013.651780523d; + double doubleExpectedLe = 7.234601680440024E-304d; + double actualBe = TbUtils.parseBytesToDouble(doubleValByte, 0, 8, true); + BigDecimal bigDecimal = new BigDecimal(actualBe); + // We move the decimal point to the left by 301 positions + actualBe = bigDecimal.movePointLeft(301).doubleValue(); + Assertions.assertEquals(0, Double.compare(doubleExpectedBe, actualBe)); + Assertions.assertEquals(0, Double.compare(doubleExpectedLe, TbUtils.parseBytesToDouble(doubleValByte, 0, 8, false))); + + doubleValList = Bytes.asList(doubleValByte); + actualBe = TbUtils.parseBytesToDouble(doubleValList, 0); + bigDecimal = new BigDecimal(actualBe); + actualBe = bigDecimal.movePointLeft(301).doubleValue(); + Assertions.assertEquals(0, Double.compare(doubleExpectedBe, actualBe)); + doubleExpectedLe = 26950.174646662283d; + double actualLe = TbUtils.parseBytesToDouble(doubleValList, 0, 5, false); + bigDecimal = new BigDecimal(actualLe); + actualLe = bigDecimal.movePointRight(316).doubleValue(); + Assertions.assertEquals(0, Double.compare(doubleExpectedLe, actualLe)); + + // 4 294 967 295L == {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} + doubleValByte = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1}; + String message = "is a Not-a-Number (NaN) value"; + try { + TbUtils.parseBytesToDouble(doubleValByte, 0, 8, true); + Assertions.fail("Should throw NumberFormatException"); + } catch (RuntimeException e) { + Assertions.assertTrue(e.getMessage().contains(message)); + } + } + + @Test + public void parseBytesLongToDouble() { + byte[] longValByte = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A}; + Double valueExpected = 10.0d; + Double valueActual = TbUtils.parseBytesLongToDouble(longValByte); + Assertions.assertEquals(valueExpected, valueActual); + valueActual = TbUtils.parseBytesLongToDouble(longValByte, 7, 1, true); + Assertions.assertEquals(valueExpected, valueActual); + valueActual = TbUtils.parseBytesLongToDouble(longValByte, 7, 1, false); + Assertions.assertEquals(valueExpected, valueActual); + + valueActual = TbUtils.parseBytesLongToDouble(longValByte, 6, 2, true); + Assertions.assertEquals(valueExpected, valueActual); + valueExpected = 2560.0d; + valueActual = TbUtils.parseBytesLongToDouble(longValByte, 6, 2, false); + Assertions.assertEquals(valueExpected, valueActual); + + valueExpected = 10.0d; + valueActual = TbUtils.parseBytesLongToDouble(longValByte, 0, 8, true); + Assertions.assertEquals(valueExpected, valueActual); + valueExpected = 7.2057594037927936E17d; + valueActual = TbUtils.parseBytesLongToDouble(longValByte, 0, 8, false); + Assertions.assertEquals(valueExpected, valueActual); } @Test @@ -561,9 +720,13 @@ public class TbUtilsTest { @Test public void numberToString_Test() { - Assertions.assertEquals("11001101100110", TbUtils.intLongToString(13158L, 2)); - Assertions.assertEquals("1111111111111111111111111111111111111111111111111111111110011010", TbUtils.intLongToString(-102L, 2)); - Assertions.assertEquals("1111111111111111111111111111111111111111111111111100110010011010", TbUtils.intLongToString(-13158L, 2)); + Assertions.assertEquals("00111010", TbUtils.intLongToString(58L, MIN_RADIX)); + Assertions.assertEquals("0000000010011110", TbUtils.intLongToString(158L, MIN_RADIX)); + Assertions.assertEquals("00000000000000100000001000000001", TbUtils.intLongToString(131585L, MIN_RADIX)); + Assertions.assertEquals("0111111111111111111111111111111111111111111111111111111111111111", TbUtils.intLongToString(Long.MAX_VALUE, MIN_RADIX)); + Assertions.assertEquals("1000000000000000000000000000000000000000000000000000000000000001", TbUtils.intLongToString(-Long.MAX_VALUE, MIN_RADIX)); + Assertions.assertEquals("1111111111111111111111111111111111111111111111111111111110011010", TbUtils.intLongToString(-102L, MIN_RADIX)); + Assertions.assertEquals("1111111111111111111111111111111111111111111111111100110010011010", TbUtils.intLongToString(-13158L, MIN_RADIX)); Assertions.assertEquals("777777777777777777777", TbUtils.intLongToString(Long.MAX_VALUE, 8)); Assertions.assertEquals("1000000000000000000000", TbUtils.intLongToString(Long.MIN_VALUE, 8)); Assertions.assertEquals("9223372036854775807", TbUtils.intLongToString(Long.MAX_VALUE)); @@ -603,17 +766,87 @@ public class TbUtilsTest { Assertions.assertEquals(expectedBe, actualBe); } + @Test + public void hexToBytes_Test() { + String input = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF33"; + byte[] expected = {1, 117, 43, 3, 103, -6, 0, 5, 0, 1, 4, -120, -1, -1, -1, -1, -1, -1, -1, -1, 51}; + List actual = TbUtils.hexToBytes(ctx, input); + Assertions.assertEquals(toList(expected), actual); + try { + input = "0x01752B0367FA000500010488FFFFFFFFFFFFFFFF3"; + TbUtils.hexToBytes(ctx, input); + } catch (IllegalArgumentException e) { + Assertions.assertTrue(e.getMessage().contains("Hex string must be even-length.")); + } + try { + input = "0x01752B0367KA000500010488FFFFFFFFFFFFFFFF33"; + TbUtils.hexToBytes(ctx, input); + } catch (NumberFormatException e) { + Assertions.assertTrue(e.getMessage().contains("Value: \"" + input + "\" is not numeric or hexDecimal format!")); + } + try { + input = ""; + TbUtils.hexToBytes(ctx, input); + } catch (IllegalArgumentException e) { + Assertions.assertTrue(e.getMessage().contains("Hex string must be not empty")); + } + } + @Test public void floatToHex_Test() { - Float value = 20.89f; - String expectedHex = "0x41A71EB8"; - String valueHexRev = "0xB81EA741"; + Float value = 123456789.00f; + String expectedHex = "0x4CEB79A3"; + String valueHexRev = "0xA379EB4C"; String actual = TbUtils.floatToHex(value); Assertions.assertEquals(expectedHex, actual); Float valueActual = TbUtils.parseHexToFloat(actual); Assertions.assertEquals(value, valueActual); valueActual = TbUtils.parseHexToFloat(valueHexRev, false); Assertions.assertEquals(value, valueActual); + value = 123456789.67f; + expectedHex = "0x4CEB79A3"; + valueHexRev = "0xA379EB4C"; + actual = TbUtils.floatToHex(value); + Assertions.assertEquals(expectedHex, actual); + valueActual = TbUtils.parseHexToFloat(actual); + Assertions.assertEquals(value, valueActual); + valueActual = TbUtils.parseHexToFloat(valueHexRev, false); + Assertions.assertEquals(value, valueActual); + value = 10.0f; + expectedHex = "0x41200000"; + valueHexRev = "0x00002041"; + actual = TbUtils.floatToHex(value); + Assertions.assertEquals(expectedHex, actual); + valueActual = TbUtils.parseHexToFloat(actual); + Assertions.assertEquals(value, valueActual); + valueActual = TbUtils.parseHexToFloat(valueHexRev, false); + Assertions.assertEquals(value, valueActual); + + String valueHex = "0x0000000A"; + float expectedValue = 1.4E-44f; + valueActual = TbUtils.parseHexToFloat(valueHex); + Assertions.assertEquals(expectedValue, valueActual); + actual = TbUtils.floatToHex(expectedValue); + Assertions.assertEquals(valueHex, actual); + } + + // If the length is not equal to 8 characters, we process it as an integer (eg "0x0A" for 10.0f). + @Test + public void parseHexIntLongToFloat_Test() { + Float valueExpected = 10.0f; + Float valueActual = TbUtils.parseHexIntLongToFloat("0x0A", true); + Assertions.assertEquals(valueExpected, valueActual); + valueActual = TbUtils.parseHexIntLongToFloat("0x0A", false); + Assertions.assertEquals(valueExpected, valueActual); + valueActual = TbUtils.parseHexIntLongToFloat("0x00000A", true); + Assertions.assertEquals(valueExpected, valueActual); + valueActual = TbUtils.parseHexIntLongToFloat("0x0A0000", false); + Assertions.assertEquals(valueExpected, valueActual); + valueExpected = 2570.0f; + valueActual = TbUtils.parseHexIntLongToFloat("0x000A0A", true); + Assertions.assertEquals(valueExpected, valueActual); + valueActual = TbUtils.parseHexIntLongToFloat("0x0A0A00", false); + Assertions.assertEquals(valueExpected, valueActual); } @Test @@ -626,6 +859,13 @@ public class TbUtilsTest { actual = TbUtils.doubleToHex(doubleVal, false); String expectedHexRev = "0xEA95B20CB1049B40"; Assertions.assertEquals(expectedHexRev, actual); + + String valueHex = "0x000000000000000A"; + Double expectedValue = 4.9E-323; + valueActual = TbUtils.parseHexToDouble(valueHex); + Assertions.assertEquals(expectedValue, valueActual); + actual = TbUtils.doubleToHex(expectedValue); + Assertions.assertEquals(valueHex, actual); } @Test @@ -670,6 +910,164 @@ public class TbUtilsTest { Assertions.assertEquals(-1, TbUtils.isHexadecimal("K100110")); } + @Test + public void padStart_Test() { + String binaryString = "010011"; + String expected = "00010011"; + Assertions.assertEquals(expected, TbUtils.padStart(binaryString, 8, '0')); + binaryString = "1001010011"; + expected = "1001010011"; + Assertions.assertEquals(expected, TbUtils.padStart(binaryString, 8, '0')); + binaryString = "1001010011"; + expected = "******1001010011"; + Assertions.assertEquals(expected, TbUtils.padStart(binaryString, 16, '*')); + String fullNumber = "203439900FFCD5581"; + String last4Digits = fullNumber.substring(11); + expected = "***********CD5581"; + Assertions.assertEquals(expected, TbUtils.padStart(last4Digits, fullNumber.length(), '*')); + } + + @Test + public void padEnd_Test() { + String binaryString = "010011"; + String expected = "01001100"; + Assertions.assertEquals(expected, TbUtils.padEnd(binaryString, 8, '0')); + binaryString = "1001010011"; + expected = "1001010011"; + Assertions.assertEquals(expected, TbUtils.padEnd(binaryString, 8, '0')); + binaryString = "1001010011"; + expected = "1001010011******"; + Assertions.assertEquals(expected, TbUtils.padEnd(binaryString, 16, '*')); + String fullNumber = "203439900FFCD5581"; + String last4Digits = fullNumber.substring(0, 11); + expected = "203439900FF******"; + Assertions.assertEquals(expected, TbUtils.padEnd(last4Digits, fullNumber.length(), '*')); + } + + @Test + public void parseByteToBinaryArray_Test() { + byte byteVal = 0x39; + byte[] bitArray = {0, 0, 1, 1, 1, 0, 0, 1}; + List expected = toList(bitArray); + byte[] actualArray = TbUtils.parseByteToBinaryArray(byteVal); + List actual = toList(actualArray); + Assertions.assertEquals(expected, actual); + + bitArray = new byte[]{1, 1, 1, 0, 0, 1}; + expected = toList(bitArray); + actualArray = TbUtils.parseByteToBinaryArray(byteVal, bitArray.length); + actual = toList(actualArray); + Assertions.assertEquals(expected, actual); + + bitArray = new byte[]{1, 0, 0, 1, 1, 1}; + expected = toList(bitArray); + actualArray = TbUtils.parseByteToBinaryArray(byteVal, bitArray.length, false); + actual = toList(actualArray); + Assertions.assertEquals(expected, actual); + } + + @Test + public void parseBytesToBinaryArray_Test() { + long longValue = 57L; + byte[] bytesValue = new byte[]{0x39}; // 57 + List listValue = toList(bytesValue); + byte[] bitArray = {0, 0, 1, 1, 1, 0, 0, 1}; + List expected = toList(bitArray); + byte[] actualArray = TbUtils.parseBytesToBinaryArray(listValue); + List actual = toList(actualArray); + Assertions.assertEquals(expected, actual); + + actualArray = TbUtils.parseLongToBinaryArray(longValue, listValue.size() * 8); + actual = toList(actualArray); + Assertions.assertEquals(expected, actual); + + bitArray = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1}; + expected = toList(bitArray); + actualArray = TbUtils.parseLongToBinaryArray(longValue); + actual = toList(actualArray); + Assertions.assertEquals(expected, actual); + + longValue = 52914L; + bytesValue = new byte[]{(byte) 0xCE, (byte) 0xB2}; // 52914 + listValue = toList(bytesValue); + bitArray = new byte[]{1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0}; + expected = toList(bitArray); + actualArray = TbUtils.parseBytesToBinaryArray(listValue); + actual = toList(actualArray); + Assertions.assertEquals(expected, actual); + + actualArray = TbUtils.parseLongToBinaryArray(longValue, listValue.size() * 8); + actual = toList(actualArray); + Assertions.assertEquals(expected, actual); + + bitArray = new byte[]{0, 0, 1, 0}; + expected = toList(bitArray); + actualArray = TbUtils.parseLongToBinaryArray(longValue, 4); + actual = toList(actualArray); + Assertions.assertEquals(expected, actual); + + bitArray = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0}; + expected = toList(bitArray); + actualArray = TbUtils.parseLongToBinaryArray(longValue); + actual = toList(actualArray); + Assertions.assertEquals(expected, actual); + } + + @Test + public void parseBinaryArrayToInt_Test() { + byte byteVal = (byte) 0x9F; + int expectedVolt = 31; + byte[] actualArray = TbUtils.parseByteToBinaryArray(byteVal); + int actual = TbUtils.parseBinaryArrayToInt(actualArray); + Assertions.assertEquals(byteVal, actual); + int actualVolt = TbUtils.parseBinaryArrayToInt(actualArray, 1, 7); + Assertions.assertEquals(expectedVolt, actualVolt); + boolean expectedLowVoltage = true; + boolean actualLowVoltage = actualArray[7] == 1; + Assertions.assertEquals(expectedLowVoltage, actualLowVoltage); + + byteVal = (byte) 0x39; + actualArray = TbUtils.parseByteToBinaryArray(byteVal, 6, false); + int expectedLowCurrent1Alarm = 1; // 0x39 = 00 11 10 01 (Bin0): 0 == 1 + int expectedHighCurrent1Alarm = 0; // 0x39 = 00 11 10 01 (Bin1): 0 == 0 + int expectedLowCurrent2Alarm = 0; // 0x39 = 00 11 10 01 (Bin2): 0 == 0 + int expectedHighCurrent2Alarm = 1; // 0x39 = 00 11 10 01 (Bin3): 0 == 1 + int expectedLowCurrent3Alarm = 1; // 0x39 = 00 11 10 01 (Bin4): 0 == 1 + int expectedHighCurrent3Alarm = 1; // 0x39 = 00 11 10 01 (Bin5): 0 == 1 + int actualLowCurrent1Alarm = actualArray[0]; + int actualHighCurrent1Alarm = actualArray[1]; + int actualLowCurrent2Alarm = actualArray[2]; + int actualHighCurrent2Alarm = actualArray[3]; + int actualLowCurrent3Alarm = actualArray[4]; + int actualHighCurrent3Alarm = actualArray[5]; + Assertions.assertEquals(expectedLowCurrent1Alarm, actualLowCurrent1Alarm); + Assertions.assertEquals(expectedHighCurrent1Alarm, actualHighCurrent1Alarm); + Assertions.assertEquals(expectedLowCurrent2Alarm, actualLowCurrent2Alarm); + Assertions.assertEquals(expectedHighCurrent2Alarm, actualHighCurrent2Alarm); + Assertions.assertEquals(expectedLowCurrent3Alarm, actualLowCurrent3Alarm); + Assertions.assertEquals(expectedHighCurrent3Alarm, actualHighCurrent3Alarm); + + byteVal = (byte) 0x36; + actualArray = TbUtils.parseByteToBinaryArray(byteVal); + int expectedMultiplier1 = 2; + int expectedMultiplier2 = 1; + int expectedMultiplier3 = 3; + int actualMultiplier1 = TbUtils.parseBinaryArrayToInt(actualArray, 6, 2); + int actualMultiplier2 = TbUtils.parseBinaryArrayToInt(actualArray, 4, 2); + int actualMultiplier3 = TbUtils.parseBinaryArrayToInt(actualArray, 2, 2); + Assertions.assertEquals(expectedMultiplier1, actualMultiplier1); + Assertions.assertEquals(expectedMultiplier2, actualMultiplier2); + Assertions.assertEquals(expectedMultiplier3, actualMultiplier3); + } + + @Test + public void hexToBase64_Test() { + String hex = "014A000A02202007060000014A019F03E800C81B5801014A029F030A0000000000014A032405DD05D41B5836014A049F39000000000000"; + String expected = "AUoACgIgIAcGAAABSgGfA+gAyBtYAQFKAp8DCgAAAAAAAUoDJAXdBdQbWDYBSgSfOQAAAAAAAA=="; + String actual = TbUtils.hexToBase64(hex); + Assertions.assertEquals(expected, actual); + } + private static List toList(byte[] data) { List result = new ArrayList<>(data.length); for (Byte b : data) { diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java index 84625db73e..da9500b5ea 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/CoapAdaptorUtils.java @@ -16,8 +16,8 @@ package org.thingsboard.server.transport.coap.adaptors; import org.eclipse.californium.core.coap.Request; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.gen.transport.TransportProtos; import java.util.Arrays; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java index 50dadd8e87..72f66c13a6 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/JsonCoapAdaptor.java @@ -27,10 +27,10 @@ import org.eclipse.californium.core.coap.MediaTypeRegistry; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.common.adaptor.JsonConverter; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.coap.CoapTransportResource; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java index 189620afa1..f7406345d1 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/adaptors/ProtoCoapAdaptor.java @@ -26,11 +26,11 @@ import org.eclipse.californium.core.coap.MediaTypeRegistry; import org.eclipse.californium.core.coap.Request; import org.eclipse.californium.core.coap.Response; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.adaptor.ProtoConverter; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.coap.CoapTransportResource; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/CoapClientContext.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/CoapClientContext.java index f13e58b20b..e507e76716 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/CoapClientContext.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/client/CoapClientContext.java @@ -17,8 +17,8 @@ package org.thingsboard.server.transport.coap.client; import org.eclipse.californium.core.observe.ObserveRelation; import org.eclipse.californium.core.server.resources.CoapExchange; -import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.coap.CoapSessionMsgType; diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java index 370c03c27f..2bcb5461e1 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/efento/CoapEfentoTransportResource.java @@ -28,15 +28,13 @@ import org.eclipse.californium.core.network.Exchange; import org.eclipse.californium.core.server.resources.CoapExchange; import org.eclipse.californium.core.server.resources.Resource; import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.common.adaptor.ProtoConverter; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.EfentoCoapDeviceTypeConfiguration; -import org.thingsboard.server.common.adaptor.AdaptorException; -import org.thingsboard.server.common.adaptor.ProtoConverter; -import org.thingsboard.server.common.adaptor.AdaptorException; import org.thingsboard.server.common.transport.auth.SessionInfoCreator; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.coap.ConfigProtos; diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index 58a5629dae..4825c8922b 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -21,9 +21,14 @@ import com.google.gson.JsonParser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.core.io.ByteArrayResource; import org.springframework.http.HttpHeaders; @@ -31,6 +36,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -38,6 +44,8 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.StringUtils; @@ -45,11 +53,11 @@ import org.thingsboard.server.common.data.TbTransportService; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.rpc.RpcStatus; +import org.thingsboard.server.common.msg.tools.MaxPayloadSizeExceededException; import org.thingsboard.server.common.transport.SessionMsgListener; import org.thingsboard.server.common.transport.TransportContext; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; -import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.transport.auth.SessionInfoCreator; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos; @@ -68,7 +76,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMs import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; -import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -128,6 +136,9 @@ public class DeviceApiController implements TbTransportService { private static final String ACCESS_TOKEN_PARAM_DESCRIPTION = "Your device access token."; + @Value("${transport.http.max_payload_size:65536}") + private int maxPayloadSize; + @Autowired private HttpTransportContext transportContext; @@ -265,6 +276,11 @@ public class DeviceApiController implements TbTransportService { @Operation(summary = "Reply to RPC commands (replyToCommand)", description = "Replies to server originated RPC command identified by 'requestId' parameter. The response is arbitrary JSON.\n\n" + REQUIRE_ACCESS_TOKEN) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "RPC reply to command request was sent to Core."), + @ApiResponse(responseCode = "400", description = "Invalid structure of the request."), + @ApiResponse(responseCode = "413", description = "Request payload is too large."), + }) @RequestMapping(value = "/{deviceToken}/rpc/{requestId}", method = RequestMethod.POST) public DeferredResult replyToCommand( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @@ -272,7 +288,8 @@ public class DeviceApiController implements TbTransportService { @Parameter(description = "RPC request id from the incoming RPC request", required = true , schema = @Schema(defaultValue = "123")) @PathVariable("requestId") Integer requestId, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Reply to the RPC request, JSON. For example: {\"status\":\"success\"}", required = true) - @RequestBody String json) { + @RequestBody String json, HttpServletRequest httpServletRequest) { + checkPayloadSize(httpServletRequest); DeferredResult responseWriter = new DeferredResult(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { @@ -292,12 +309,18 @@ public class DeviceApiController implements TbTransportService { "{\"result\": 4}" + MARKDOWN_CODE_BLOCK_END + REQUIRE_ACCESS_TOKEN) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "RPC request to server was sent to Rule Engine."), + @ApiResponse(responseCode = "400", description = "Invalid structure of the request."), + @ApiResponse(responseCode = "413", description = "Request payload too large."), + }) @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.POST) public DeferredResult postRpcRequest( @Parameter(description = ACCESS_TOKEN_PARAM_DESCRIPTION, required = true , schema = @Schema(defaultValue = "YOUR_DEVICE_ACCESS_TOKEN")) @PathVariable("deviceToken") String deviceToken, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "The RPC request JSON", required = true) - @RequestBody String json) { + @RequestBody String json, HttpServletRequest httpServletRequest) { + checkPayloadSize(httpServletRequest); DeferredResult responseWriter = new DeferredResult(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { @@ -420,6 +443,12 @@ public class DeviceApiController implements TbTransportService { return responseWriter; } + private void checkPayloadSize(HttpServletRequest httpServletRequest) { + if (httpServletRequest.getContentLength() > maxPayloadSize) { + throw new MaxPayloadSizeExceededException(); + } + } + private DeferredResult getOtaPackageCallback(String deviceToken, String title, String version, int size, int chunk, OtaPackageType firmwareType) { DeferredResult responseWriter = new DeferredResult<>(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), @@ -608,6 +637,20 @@ public class DeviceApiController implements TbTransportService { } + @ExceptionHandler(MaxPayloadSizeExceededException.class) + public void handle(MaxPayloadSizeExceededException exception, HttpServletRequest request, HttpServletResponse response) { + log.debug("Too large payload size. Url: {}, client ip: {}, content length: {}", request.getRequestURL(), + request.getRemoteAddr(), request.getContentLength()); + if (!response.isCommitted()) { + try { + response.setStatus(HttpStatus.PAYLOAD_TOO_LARGE.value()); + JacksonUtil.writeValue(response.getWriter(), exception.getMessage()); + } catch (IOException e) { + log.error("Can't handle exception", e); + } + } + } + private static MediaType parseMediaType(String contentType) { try { return MediaType.parseMediaType(contentType); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/TbLwM2MDtlsBootstrapCertificateVerifier.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/TbLwM2MDtlsBootstrapCertificateVerifier.java index b341b09b70..9c86b23084 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/TbLwM2MDtlsBootstrapCertificateVerifier.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/secure/TbLwM2MDtlsBootstrapCertificateVerifier.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.bootstrap.secure; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.elements.auth.RawPublicKeyIdentity; @@ -41,7 +42,6 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; -import jakarta.annotation.PostConstruct; import javax.security.auth.x500.X500Principal; import java.net.InetSocketAddress; import java.security.PublicKey; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java index 64ab50ca3b..ae737d2343 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.secure; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.californium.elements.auth.RawPublicKeyIdentity; @@ -47,7 +48,6 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2MAuthException; import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore; -import jakarta.annotation.PostConstruct; import javax.security.auth.x500.X500Principal; import java.net.InetSocketAddress; import java.security.PublicKey; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java index 1e1cc5ddce..403ec7f0a7 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/model/LwM2MModelConfigServiceImpl.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.model; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -38,7 +39,6 @@ import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogServic import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MModelConfigStore; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; -import jakarta.annotation.PreDestroy; import java.util.List; import java.util.Map; import java.util.Set; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 0b5e995ea5..f779ae167e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.transport.lwm2m.server.ota; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.leshan.core.node.codec.CodecException; @@ -57,8 +59,6 @@ import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdate import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MClientOtaInfoStore; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.ArrayList; import java.util.List; import java.util.Map; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index 0fff841685..4655a27316 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -58,9 +58,9 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttrib import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MObserveCompositeCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MCancelObserveCompositeCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MCancelObserveCompositeRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MObserveCompositeCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MObserveCompositeRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeRequest; diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java index 7d4d795738..1441e036a0 100644 --- a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java +++ b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java @@ -19,8 +19,8 @@ import org.eclipse.leshan.core.endpoint.EndpointUriUtil; import org.eclipse.leshan.core.link.Link; import org.eclipse.leshan.core.peer.IpPeer; import org.eclipse.leshan.server.registration.Registration; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.net.InetSocketAddress; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java index 9556180300..16d72ff89f 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java @@ -16,6 +16,7 @@ package org.thingsboard.server.transport.mqtt; import io.netty.handler.ssl.SslHandler; +import jakarta.annotation.PostConstruct; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -27,7 +28,6 @@ import org.thingsboard.server.common.transport.TransportContext; import org.thingsboard.server.transport.mqtt.adaptors.JsonMqttAdaptor; import org.thingsboard.server.transport.mqtt.adaptors.ProtoMqttAdaptor; -import jakarta.annotation.PostConstruct; import java.net.InetSocketAddress; import java.util.concurrent.atomic.AtomicInteger; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java index fe91657f15..ffc7e9e561 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportService.java @@ -23,6 +23,8 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.util.AttributeKey; import io.netty.util.ResourceLeakDetector; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -31,8 +33,6 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.TbTransportService; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.net.InetSocketAddress; /** diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index 8cd215f1f1..0970fe7882 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -27,11 +27,11 @@ import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.device.profile.MqttTopics; import org.thingsboard.server.common.data.ota.OtaPackageType; -import org.thingsboard.server.common.adaptor.AdaptorException; -import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionContext; diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index 9cf15f9de1..ec2aa09988 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -23,8 +23,8 @@ import io.netty.handler.codec.mqtt.MqttMessage; import io.netty.handler.codec.mqtt.MqttMessageType; import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.adaptor.AdaptorException; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; diff --git a/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java b/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java index 250c33441c..d6e91ec1fd 100644 --- a/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java +++ b/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/MqttTransportHandlerTest.java @@ -34,7 +34,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.DataConstants; @@ -68,7 +67,6 @@ import static org.hamcrest.Matchers.is; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.willDoNothing; -import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; diff --git a/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandlerTest.java b/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandlerTest.java index 670bd98af1..4a71730a5c 100644 --- a/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandlerTest.java +++ b/common/transport/mqtt/src/test/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandlerTest.java @@ -19,7 +19,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.util.ConcurrentReferenceHashMap; import org.thingsboard.server.common.data.id.CustomerId; @@ -36,13 +35,11 @@ import java.util.concurrent.ConcurrentHashMap; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.willCallRealMethod; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) public class GatewaySessionHandlerTest { diff --git a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java index 12008c10b5..3a1d440d20 100644 --- a/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java +++ b/common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/service/SnmpTransportService.java @@ -22,6 +22,8 @@ import com.google.common.util.concurrent.ListeningScheduledExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Builder; import lombok.Data; import lombok.Getter; @@ -65,8 +67,6 @@ import org.thingsboard.server.transport.snmp.SnmpTransportContext; import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; import org.thingsboard.server.transport.snmp.session.ScheduledTask; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java index b0c085b2e9..a1ed00844d 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java @@ -16,6 +16,8 @@ package org.thingsboard.server.common.transport; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Data; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -26,8 +28,6 @@ import org.thingsboard.server.common.transport.limits.TransportRateLimitService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.scheduler.SchedulerComponent; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.concurrent.ExecutorService; /** diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsConfig.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsConfig.java index c758d707fc..49630f7c02 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsConfig.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsConfig.java @@ -15,11 +15,10 @@ */ package org.thingsboard.server.common.transport.config.ssl; +import jakarta.annotation.PostConstruct; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import jakarta.annotation.PostConstruct; - @Slf4j @Data public class SslCredentialsConfig { diff --git a/common/util/src/main/java/org/thingsboard/common/util/AbstractListeningExecutor.java b/common/util/src/main/java/org/thingsboard/common/util/AbstractListeningExecutor.java index 7f8df67ce9..bb205a122e 100644 --- a/common/util/src/main/java/org/thingsboard/common/util/AbstractListeningExecutor.java +++ b/common/util/src/main/java/org/thingsboard/common/util/AbstractListeningExecutor.java @@ -18,9 +18,9 @@ package org.thingsboard.common.util; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; - import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; + import java.util.concurrent.Callable; /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetTypeFilter.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetTypeFilter.java index 805ad029f7..c6d200e10f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetTypeFilter.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetTypeFilter.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.dao.asset; +import jakarta.annotation.Nullable; import lombok.Data; -import jakarta.annotation.Nullable; import java.util.List; /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java index ad5a4ba686..82712c8a2c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.audit; -import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.id.CustomerId; diff --git a/dao/src/main/java/org/thingsboard/server/dao/audit/sink/ElasticsearchAuditLogSink.java b/dao/src/main/java/org/thingsboard/server/dao/audit/sink/ElasticsearchAuditLogSink.java index 5d143ef009..01ccbc17f1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/audit/sink/ElasticsearchAuditLogSink.java +++ b/dao/src/main/java/org/thingsboard/server/dao/audit/sink/ElasticsearchAuditLogSink.java @@ -16,6 +16,8 @@ package org.thingsboard.server.dao.audit.sink; import com.fasterxml.jackson.databind.node.ObjectNode; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; @@ -40,11 +42,8 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.id.TenantId; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.Collections; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java index 3ecff4ad53..b42fc9c205 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardInfoDao.java @@ -16,14 +16,11 @@ package org.thingsboard.server.dao.dashboard; import org.thingsboard.server.common.data.DashboardInfo; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ImageContainerDao; -import java.util.List; import java.util.UUID; /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java index 2004184cda..d396fc3cfd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewServiceImpl.java @@ -19,6 +19,7 @@ import com.google.common.base.Function; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -52,7 +53,6 @@ import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.sql.JpaExecutorService; -import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java index fd349828e2..02e1a15543 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java @@ -16,6 +16,9 @@ package org.thingsboard.server.dao.model; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.persistence.Column; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.thingsboard.common.util.JacksonUtil; @@ -24,9 +27,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.dao.DaoUtil; -import jakarta.persistence.Column; -import jakarta.persistence.Id; -import jakarta.persistence.MappedSuperclass; import java.util.Arrays; import java.util.Collections; import java.util.List; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java index ea26b60cab..7342018bd6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractWidgetTypeEntity.java @@ -15,17 +15,16 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.widget.BaseWidgetType; -import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.MappedSuperclass; import java.util.UUID; @Data diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java index 942d9cbe75..5494c8e516 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java @@ -15,16 +15,15 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.alarm.AlarmAssignee; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.id.UserId; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; - import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_EMAIL_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_FIRST_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_LAST_NAME_PROPERTY; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetProfileEntity.java index d035db07a7..ab9132e8cc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetProfileEntity.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.asset.AssetProfile; @@ -25,9 +28,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; import java.util.UUID; @Data diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceCredentialsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceCredentialsEntity.java index 34a98f1c28..0b8b882304 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceCredentialsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceCredentialsEntity.java @@ -15,6 +15,11 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.DeviceCredentialsId; @@ -25,11 +30,6 @@ import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.Table; import java.util.UUID; @Data diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceInfoEntity.java index d4bb8709cf..a2617103aa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceInfoEntity.java @@ -15,16 +15,15 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Immutable; import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; - @Data @EqualsAndHashCode(callSuper = true) @Entity diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmCompositeKey.java index e35655917c..575460032f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmCompositeKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmCompositeKey.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Transient; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.alarm.EntityAlarm; -import jakarta.persistence.Transient; import java.io.Serializable; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmEntity.java index c3b8ccbfe2..6b169be262 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EntityAlarmEntity.java @@ -15,6 +15,11 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; import lombok.Data; import org.thingsboard.server.common.data.alarm.EntityAlarm; import org.thingsboard.server.common.data.id.AlarmId; @@ -23,11 +28,6 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.ToData; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.IdClass; -import jakarta.persistence.Table; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.CREATED_TIME_PROPERTY; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ErrorEventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ErrorEventEntity.java index ee63728be0..4af7e41ade 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ErrorEventEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/ErrorEventEntity.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -22,10 +25,6 @@ import org.thingsboard.server.common.data.event.ErrorEvent; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseEntity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; - import static org.thingsboard.server.dao.model.ModelConstants.ERROR_EVENT_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ERROR_COLUMN_NAME; import static org.thingsboard.server.dao.model.ModelConstants.EVENT_METHOD_COLUMN_NAME; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java index 5cd5f59eb2..bf28a52af6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/EventEntity.java @@ -15,15 +15,15 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; import lombok.Data; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.event.Event; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.Id; -import jakarta.persistence.MappedSuperclass; import java.util.HashMap; import java.util.Map; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/LifecycleEventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/LifecycleEventEntity.java index ef7c6a5d1a..e5698f25e6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/LifecycleEventEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/LifecycleEventEntity.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -22,10 +25,6 @@ import org.thingsboard.server.common.data.event.LifecycleEvent; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseEntity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; - import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ERROR_COLUMN_NAME; import static org.thingsboard.server.dao.model.ModelConstants.EVENT_SUCCESS_COLUMN_NAME; import static org.thingsboard.server.dao.model.ModelConstants.EVENT_TYPE_COLUMN_NAME; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java index 7841a11378..884ee9cf1d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java @@ -15,6 +15,11 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.OAuth2DomainId; @@ -24,11 +29,6 @@ import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.Table; import java.util.UUID; @Data diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java index 78ec508ad0..d2dcb32a13 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.OAuth2MobileId; @@ -23,9 +26,6 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; import java.util.UUID; @Data diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java index 4983172d3e..e163d3f19c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -24,9 +27,6 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Params; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; import java.util.UUID; @Data diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java index 5b9ec291a2..96800a4d38 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/QueueStatsEntity.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.QueueStatsId; @@ -24,9 +27,6 @@ import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; import java.util.UUID; @Data diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RelationCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RelationCompositeKey.java index 79a52462c5..ca7ba87358 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RelationCompositeKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RelationCompositeKey.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Transient; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.relation.EntityRelation; -import jakarta.persistence.Transient; import java.io.Serializable; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainDebugEventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainDebugEventEntity.java index 107d35e179..23219f88e9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainDebugEventEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainDebugEventEntity.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -22,10 +25,6 @@ import org.thingsboard.server.common.data.event.RuleChainDebugEvent; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseEntity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; - import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ERROR_COLUMN_NAME; import static org.thingsboard.server.dao.model.ModelConstants.EVENT_MESSAGE_COLUMN_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RULE_CHAIN_DEBUG_EVENT_TABLE_NAME; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeDebugEventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeDebugEventEntity.java index 772727a226..44a5ee8370 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeDebugEventEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeDebugEventEntity.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -23,9 +26,6 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseEntity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.EVENT_DATA_COLUMN_NAME; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/StatisticsEventEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/StatisticsEventEntity.java index 23a8bbe796..b25563e584 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/StatisticsEventEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/StatisticsEventEntity.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @@ -22,10 +25,6 @@ import org.thingsboard.server.common.data.event.StatisticsEvent; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseEntity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; - import static org.thingsboard.server.dao.model.ModelConstants.EVENT_ERRORS_OCCURRED_COLUMN_NAME; import static org.thingsboard.server.dao.model.ModelConstants.EVENT_MESSAGES_PROCESSED_COLUMN_NAME; import static org.thingsboard.server.dao.model.ModelConstants.STATS_EVENT_TABLE_NAME; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java index f507a8661f..02922c742a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java @@ -16,24 +16,23 @@ package org.thingsboard.server.dao.model.sql; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.persistence.Column; import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; -import org.hibernate.annotations.Type; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; import org.thingsboard.server.dao.util.mapping.JsonConverter; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.EXTERNAL_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.PUBLIC_RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DESCRIPTOR_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; @@ -41,7 +40,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_IS_PUBLIC_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_PREVIEW_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.PUBLIC_RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TITLE_COLUMN; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java index 8f525916bf..7d65d70a37 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java @@ -16,7 +16,10 @@ package org.thingsboard.server.dao.model.sql; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.persistence.Column; import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.ResourceType; @@ -27,18 +30,15 @@ import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.util.mapping.JsonConverter; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.EXTERNAL_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.PUBLIC_RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DESCRIPTOR_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_ETAG_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_IS_PUBLIC_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.PUBLIC_RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TITLE_COLUMN; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserCredentialsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserCredentialsEntity.java index e7907921bf..59ba5ac4dd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserCredentialsEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/UserCredentialsEntity.java @@ -16,7 +16,10 @@ package org.thingsboard.server.dao.model.sql; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.persistence.Column; import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.UserCredentialsId; @@ -25,10 +28,6 @@ import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.dao.model.BaseEntity; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; import org.thingsboard.server.dao.util.mapping.JsonConverter; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeInfoEntity.java index 415bf26ea0..7a9e7f35bc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetTypeInfoEntity.java @@ -16,6 +16,9 @@ package org.thingsboard.server.dao.model.sql; import io.hypersistence.utils.hibernate.type.array.StringArrayType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Immutable; @@ -24,9 +27,6 @@ import org.thingsboard.server.common.data.widget.BaseWidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeInfo; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; import java.util.HashMap; import java.util.Map; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java index 1edf3bb614..b9866363e0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleEntity.java @@ -16,6 +16,9 @@ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.TenantId; @@ -24,9 +27,6 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; import java.util.UUID; @Data diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetCompositeKey.java index 7da229a79a..b8388c37ba 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetCompositeKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetCompositeKey.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Transient; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import jakarta.persistence.Transient; import java.io.Serializable; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetEntity.java index b1e988f255..fc7f46a04c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/WidgetsBundleWidgetEntity.java @@ -15,17 +15,17 @@ */ package org.thingsboard.server.dao.model.sql; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; import lombok.Data; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.widget.WidgetsBundleWidget; import org.thingsboard.server.dao.model.ToData; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.IdClass; -import jakarta.persistence.Table; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.WIDGETS_BUNDLE_WIDGET_TABLE_NAME; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/dictionary/KeyDictionaryCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/dictionary/KeyDictionaryCompositeKey.java index ca38d65aae..deae2ac05a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/dictionary/KeyDictionaryCompositeKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/dictionary/KeyDictionaryCompositeKey.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.dao.model.sqlts.dictionary; +import jakarta.persistence.Transient; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import jakarta.persistence.Transient; import java.io.Serializable; @Data diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/latest/TsKvLatestCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/latest/TsKvLatestCompositeKey.java index 58f8ee2867..08808e9356 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/latest/TsKvLatestCompositeKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/latest/TsKvLatestCompositeKey.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.dao.model.sqlts.latest; +import jakarta.persistence.Transient; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import jakarta.persistence.Transient; import java.io.Serializable; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/ts/TsKvCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/ts/TsKvCompositeKey.java index 98eed0a603..4efba65860 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/ts/TsKvCompositeKey.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/ts/TsKvCompositeKey.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.dao.model.sqlts.ts; +import jakarta.persistence.Transient; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import jakarta.persistence.Transient; import java.io.Serializable; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractAsyncDao.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractAsyncDao.java index 32cc2e6d02..0d136286b8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractAsyncDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractAsyncDao.java @@ -19,12 +19,12 @@ import com.google.common.base.Function; import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; -import org.springframework.beans.factory.annotation.Value; -import org.thingsboard.common.util.ThingsBoardExecutors; - import jakarta.annotation.Nullable; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; +import org.springframework.beans.factory.annotation.Value; +import org.thingsboard.common.util.ThingsBoardExecutors; + import java.util.concurrent.ExecutorService; /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java index d3eb176e61..31bb0c8941 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java @@ -17,19 +17,18 @@ package org.thingsboard.server.dao.nosql; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor; import org.thingsboard.server.dao.util.AsyncTaskContext; import org.thingsboard.server.dao.util.NoSqlAnyDao; -import org.thingsboard.server.cache.limits.RateLimitService; - -import jakarta.annotation.PreDestroy; /** * Created by ashvayka on 24.10.18. diff --git a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java index 8bfdd36501..cf38cf922d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java +++ b/dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateWriteExecutor.java @@ -17,19 +17,18 @@ package org.thingsboard.server.dao.nosql; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor; import org.thingsboard.server.dao.util.AsyncTaskContext; import org.thingsboard.server.dao.util.NoSqlAnyDao; -import org.thingsboard.server.cache.limits.RateLimitService; - -import jakarta.annotation.PreDestroy; /** * Created by ashvayka on 24.10.18. diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 5bb7ae163e..5846a684a8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.oauth2; +import jakarta.transaction.Transactional; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -44,7 +45,6 @@ import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import jakarta.transaction.Transactional; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 723d0e8861..bc641bb1fc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -155,6 +155,9 @@ public class BaseResourceService extends AbstractCachedEntityService referencingRuleNodes = getReferencingRuleChainNodes(tenantId, ruleChainId); Set referencingRuleChainIds = referencingRuleNodes.stream().map(RuleNode::getRuleChainId).collect(Collectors.toSet()); - if (ruleChain != null) { - if (ruleChain.isRoot()) { - throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!"); - } - if (RuleChainType.EDGE.equals(ruleChain.getType())) { - for (Edge edge : new PageDataIterable<>(link -> edgeService.findEdgesByTenantIdAndEntityId(tenantId, ruleChainId, link), DEFAULT_PAGE_SIZE)) { - if (edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) { - throw new DataValidationException("Can't delete rule chain that is root for edge [" + edge.getName() + "]. Please assign another root rule chain first to the edge!"); - } + if (ruleChain.isRoot()) { + throw new DataValidationException("Deletion of Root Tenant Rule Chain is prohibited!"); + } + if (RuleChainType.EDGE.equals(ruleChain.getType())) { + for (Edge edge : new PageDataIterable<>(link -> edgeService.findEdgesByTenantIdAndEntityId(tenantId, ruleChainId, link), DEFAULT_PAGE_SIZE)) { + if (edge.getRootRuleChainId() != null && edge.getRootRuleChainId().equals(ruleChainId)) { + throw new DataValidationException("Can't delete rule chain that is root for edge [" + edge.getName() + "]. Please assign another root rule chain first to the edge!"); } } } @@ -457,6 +458,9 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC public void deleteEntity(TenantId tenantId, EntityId id, boolean force) { if (force) { RuleChain ruleChain = findRuleChainById(tenantId, (RuleChainId) id); + if (ruleChain == null) { + return; + } checkRuleNodesAndDelete(tenantId, ruleChain, null); } else { deleteRuleChainById(tenantId, (RuleChainId) id); diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java index 40be2fbd54..a58a381bb1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java @@ -16,6 +16,11 @@ package org.thingsboard.server.dao.service; import com.google.common.collect.Iterators; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.metadata.ConstraintDescriptor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.hibernate.validator.HibernateValidator; @@ -30,11 +35,6 @@ import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.dao.exception.DataValidationException; -import jakarta.validation.ConstraintViolation; -import jakarta.validation.Validation; -import jakarta.validation.Validator; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.metadata.ConstraintDescriptor; import java.util.Collection; import java.util.Set; import java.util.stream.Collectors; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/NoXssValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/NoXssValidator.java index fd8a19a859..8b1ddfaec0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/NoXssValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/NoXssValidator.java @@ -16,6 +16,8 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; import lombok.extern.slf4j.Slf4j; import org.owasp.validator.html.AntiSamy; import org.owasp.validator.html.Policy; @@ -23,8 +25,6 @@ import org.owasp.validator.html.PolicyException; import org.owasp.validator.html.ScanException; import org.thingsboard.server.common.data.validation.NoXss; -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; import java.util.Optional; @Slf4j diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java index 252af5458a..d13f2e6ad8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java @@ -16,13 +16,12 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.validation.Length; -import jakarta.validation.ConstraintValidator; -import jakarta.validation.ConstraintValidatorContext; - @Slf4j public class StringLengthValidator implements ConstraintValidator { private int max; diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java index 2d0cdbd248..51d943ee44 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AssetDataValidator.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetDao; -import org.thingsboard.server.dao.asset.BaseAssetService; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java index 45438a9b51..38cf02047f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaPartitionedAbstractDao.java @@ -15,11 +15,10 @@ */ package org.thingsboard.server.dao.sql; -import org.thingsboard.server.dao.model.BaseEntity; -import org.thingsboard.server.dao.util.SqlDao; - import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; +import org.thingsboard.server.dao.model.BaseEntity; +import org.thingsboard.server.dao.util.SqlDao; @SqlDao public abstract class JpaPartitionedAbstractDao, D> extends JpaAbstractDao { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ScheduledLogExecutorComponent.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ScheduledLogExecutorComponent.java index 5226b5def7..484abd6a59 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/ScheduledLogExecutorComponent.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ScheduledLogExecutorComponent.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.dao.sql; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.springframework.stereotype.Component; import org.thingsboard.common.util.ThingsBoardThreadFactory; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java index eca2c769b9..bd23d1d9fe 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/AbstractComponentDescriptorInsertRepository.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.sql.component; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.Query; import lombok.extern.slf4j.Slf4j; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; @@ -25,10 +28,6 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; -import jakarta.persistence.Query; - @Slf4j public abstract class AbstractComponentDescriptorInsertRepository implements ComponentDescriptorInsertRepository { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java index c1a1c3dca0..79d47c9efe 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/component/JpaBaseComponentDescriptorDao.java @@ -32,7 +32,6 @@ import org.thingsboard.server.dao.component.ComponentDescriptorDao; import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; import org.thingsboard.server.dao.sql.JpaAbstractDao; -import java.util.Objects; import java.util.Optional; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index a1cfc52550..e76dfe9764 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -35,7 +35,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java index 63c527edfc..98c5ad7036 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -36,7 +36,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventInsertRepository.java index 5c6969fce9..faa5eb6562 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventInsertRepository.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.sql.event; +import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.jdbc.core.BatchPreparedStatementSetter; @@ -33,7 +34,6 @@ import org.thingsboard.server.common.data.event.RuleNodeDebugEvent; import org.thingsboard.server.common.data.event.StatisticsEvent; import org.thingsboard.server.dao.util.SqlDao; -import jakarta.annotation.PostConstruct; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Types; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventPartitionConfiguration.java b/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventPartitionConfiguration.java index b053e2ebc2..4be8775145 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventPartitionConfiguration.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/event/EventPartitionConfiguration.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.dao.sql.event; +import jakarta.annotation.PostConstruct; import lombok.Getter; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.event.EventType; -import jakarta.annotation.PostConstruct; import java.util.concurrent.TimeUnit; @Component diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java index df90e6430e..3d2d80e15e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRequestDao.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.sql.notification; -import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java index ac649c2db4..48a7df0f3b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.sql.notification; -import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java index ce98a46f52..cb2a577441 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.sql.notification; -import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java index 8950ab1e35..f0ca725951 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.sql.notification; -import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java index 10833b11df..707a6e7062 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java @@ -33,7 +33,6 @@ import org.thingsboard.server.dao.ota.OtaPackageInfoDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; -import java.util.Objects; import java.util.UUID; @Slf4j diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java index 05afe8b6d3..ed3bf14971 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.sql.query; -import com.fasterxml.jackson.core.JsonProcessingException; import lombok.extern.slf4j.Slf4j; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueDao.java index 799d1ff521..e4563b7d35 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/queue/JpaQueueDao.java @@ -33,7 +33,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.Objects; import java.util.UUID; @Slf4j diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index 46232c5bbb..eaf151f256 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -33,7 +33,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.Collection; -import java.util.Objects; import java.util.Optional; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java index 882c72d430..cea414a42f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java @@ -33,7 +33,6 @@ import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java index 78dd1ef4bf..26e13b1650 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/WidgetsBundleRepository.java @@ -21,7 +21,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.dao.ExportableEntityRepository; -import org.thingsboard.server.dao.model.sql.WidgetTypeInfoEntity; import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity; import java.util.List; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java index b9694fbff7..18364c6b1b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sqlts; import com.google.common.base.Function; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -29,7 +30,6 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; -import jakarta.annotation.Nullable; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/BaseAbstractSqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/BaseAbstractSqlTimeseriesDao.java index 91be4e0e2e..c7ebbd4964 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/BaseAbstractSqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/BaseAbstractSqlTimeseriesDao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sqlts; import com.google.common.base.Function; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQueryResult; @@ -25,7 +26,6 @@ import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; -import jakarta.annotation.Nullable; import java.util.List; import java.util.Objects; import java.util.Optional; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/dictionary/KeyDictionaryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/dictionary/KeyDictionaryRepository.java index f61da09779..17e24ea5e5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/dictionary/KeyDictionaryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/dictionary/KeyDictionaryRepository.java @@ -16,8 +16,8 @@ package org.thingsboard.server.dao.sqlts.dictionary; import org.springframework.data.jpa.repository.JpaRepository; -import org.thingsboard.server.dao.model.sqlts.dictionary.KeyDictionaryEntry; import org.thingsboard.server.dao.model.sqlts.dictionary.KeyDictionaryCompositeKey; +import org.thingsboard.server.dao.model.sqlts.dictionary.KeyDictionaryEntry; import java.util.Optional; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/SearchTsKvLatestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/SearchTsKvLatestRepository.java index 5e9e3369a5..b3180dae50 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/SearchTsKvLatestRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/SearchTsKvLatestRepository.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.dao.sqlts.latest; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; import org.thingsboard.server.dao.util.SqlTsLatestAnyDao; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import java.util.List; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java index 1e3ae68f4c..135ab097c9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.dao.sqlts.timescale; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvEntity; import org.thingsboard.server.dao.util.TimescaleDBTsOrTsLatestDao; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import java.util.List; import java.util.UUID; diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java index b72ac1514b..168ac4ecb2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.timeseries; import com.datastax.oss.driver.api.core.cql.Row; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import jakarta.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.kv.AggTsKvEntry; import org.thingsboard.server.common.data.kv.Aggregation; @@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntryAggWrapper; import org.thingsboard.server.dao.nosql.TbResultSet; -import jakarta.annotation.Nullable; import java.util.List; import java.util.Optional; import java.util.concurrent.Executor; diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java index 6700e3614a..6acf906875 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java @@ -28,6 +28,9 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -55,9 +58,6 @@ import org.thingsboard.server.dao.sqlts.AggregationTimeseriesDao; import org.thingsboard.server.dao.util.NoSqlTsDao; import org.thingsboard.server.dao.util.TimeUtils; -import jakarta.annotation.Nullable; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; diff --git a/dao/src/main/java/org/thingsboard/server/dao/util/JsonPathProcessingTask.java b/dao/src/main/java/org/thingsboard/server/dao/util/JsonPathProcessingTask.java index 8947a44642..ead70ce4ea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/util/JsonPathProcessingTask.java +++ b/dao/src/main/java/org/thingsboard/server/dao/util/JsonPathProcessingTask.java @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.util; import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; -import org.thingsboard.server.dao.dashboard.DashboardServiceImpl; import java.util.Arrays; import java.util.HashMap; diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java index 02d5b0f007..b54cd66499 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleDao.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.widget; -import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.page.PageData; diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/notification/channels/impl/SlackNotificationChannel.java b/monitoring/src/main/java/org/thingsboard/monitoring/notification/channels/impl/SlackNotificationChannel.java index 77b68f665d..b5357e5f26 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/notification/channels/impl/SlackNotificationChannel.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/notification/channels/impl/SlackNotificationChannel.java @@ -15,6 +15,7 @@ */ package org.thingsboard.monitoring.notification.channels.impl; +import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -23,7 +24,6 @@ import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import org.thingsboard.monitoring.notification.channels.NotificationChannel; -import jakarta.annotation.PostConstruct; import java.time.Duration; import java.util.Map; diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java index ae5cc63994..b98dcb0e82 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseHealthChecker.java @@ -15,6 +15,8 @@ */ package org.thingsboard.monitoring.service; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -29,8 +31,6 @@ import org.thingsboard.monitoring.data.MonitoredServiceKey; import org.thingsboard.monitoring.data.ServiceFailureException; import org.thingsboard.monitoring.util.TbStopWatch; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.HashMap; import java.util.Map; import java.util.UUID; diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseMonitoringService.java b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseMonitoringService.java index 5ff8c4f9a1..7df767caef 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseMonitoringService.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/service/BaseMonitoringService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.monitoring.service; +import jakarta.annotation.PostConstruct; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -29,7 +30,6 @@ import org.thingsboard.monitoring.data.MonitoredServiceKey; import org.thingsboard.monitoring.service.transport.TransportHealthChecker; import org.thingsboard.monitoring.util.TbStopWatch; -import jakarta.annotation.PostConstruct; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; diff --git a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java index e42dee834d..a16b651e15 100644 --- a/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java +++ b/netty-mqtt/src/main/java/org/thingsboard/mqtt/MqttClientConfig.java @@ -19,11 +19,11 @@ import io.netty.channel.Channel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.mqtt.MqttVersion; import io.netty.handler.ssl.SslContext; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import lombok.Getter; import lombok.Setter; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; import java.util.Random; @SuppressWarnings({"WeakerAccess", "unused"}) diff --git a/pom.xml b/pom.xml index b26187086d..5b6f30bef8 100755 --- a/pom.xml +++ b/pom.xml @@ -83,7 +83,7 @@ 3.9.2 3.25.3 1.63.0 - 1.2.1 + 1.2.2 1.18.32 1.2.5 1.2.5 @@ -105,7 +105,7 @@ org/thingsboard/server/extensions/core/plugin/telemetry/gen/**/* 8.13.2 - 0.4.2 + 0.4.5 15.4 + + + + + + + + + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.scss new file mode 100644 index 0000000000..b70fe42401 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.scss @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + height: 100%; +} + +:host ::ng-deep { + .mat-mdc-tab-body-content { + overflow: hidden !important; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.ts new file mode 100644 index 0000000000..1fca768980 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component.ts @@ -0,0 +1,116 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectionStrategy, Component, forwardRef, Input, OnDestroy, TemplateRef } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, +} from '@angular/forms'; +import { ConnectorType, ModbusBasicConfig } from '@home/components/widget/lib/gateway/gateway-widget.models'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; + +import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive'; +import { ModbusSlaveConfigComponent } from '../modbus-slave-config/modbus-slave-config.component'; +import { ModbusMasterTableComponent } from '../modbus-master-table/modbus-master-table.component'; + +@Component({ + selector: 'tb-modbus-basic-config', + templateUrl: './modbus-basic-config.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ModbusBasicConfigComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => ModbusBasicConfigComponent), + multi: true + } + ], + standalone: true, + imports: [ + CommonModule, + SharedModule, + ModbusSlaveConfigComponent, + ModbusMasterTableComponent, + EllipsisChipListDirective, + ], + styleUrls: ['./modbus-basic-config.component.scss'], +}) + +export class ModbusBasicConfigComponent implements ControlValueAccessor, Validator, OnDestroy { + + @Input() generalTabContent: TemplateRef; + + basicFormGroup: FormGroup; + + onChange: (value: ModbusBasicConfig) => void; + onTouched: () => void; + + private destroy$ = new Subject(); + + constructor(private fb: FormBuilder) { + this.basicFormGroup = this.fb.group({ + master: [], + slave: [], + }); + + this.basicFormGroup.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(value => { + this.onChange(value); + this.onTouched(); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnChange(fn: (value: ModbusBasicConfig) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + writeValue(basicConfig: ModbusBasicConfig): void { + const editedBase = { + slave: basicConfig.slave ?? {}, + master: basicConfig.master ?? {}, + }; + + this.basicFormGroup.setValue(editedBase, {emitEvent: false}); + } + + validate(): ValidationErrors | null { + return this.basicFormGroup.valid ? null : { + basicFormGroup: {valid: false} + }; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html new file mode 100644 index 0000000000..02d2d1491d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.html @@ -0,0 +1,180 @@ + +
+
+
{{ panelTitle | translate }}{{' (' + keysListFormArray.controls.length + ')'}}
+
+
+
+ + + + +
+ + {{ keyControl.get('tag').value }}{{ '-' }}{{ keyControl.get('value').value }} + + {{ keyControl.get('tag').value }} +
+
+
+ +
+
gateway.platform-side
+
+
+ gateway.key +
+
+ + + + warning + + +
+
+
+
+
gateway.connector-side
+
+
+ gateway.type +
+
+ + + {{ type }} + + +
+
+
+
gateway.function-code
+
+ + + + {{ ModbusFunctionCodeTranslationsMap.get(code) | translate }} + + + +
+
+
+
gateway.objects-count
+
+ + + +
+
+
+
gateway.address
+
+ + + + warning + + +
+
+
+
gateway.value
+
+ + + + warning + + +
+
+
+
+
+
+
+ +
+
+
+ +
+
+ +
+ {{ noKeysText }} +
+
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.scss new file mode 100644 index 0000000000..0e9f9a432f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.scss @@ -0,0 +1,44 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +:host { + .tb-modbus-keys-panel { + width: 77vw; + max-width: 700px; + + .title-container { + max-width: 11vw; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap + } + + .key-panel { + height: 500px; + overflow: auto; + } + + .tb-form-panel { + .mat-mdc-icon-button { + width: 56px; + height: 56px; + padding: 16px; + color: rgba(0, 0, 0, 0.54); + } + } + } +} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts new file mode 100644 index 0000000000..5b312f52c8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-data-keys-panel/modbus-data-keys-panel.component.ts @@ -0,0 +1,208 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { + AbstractControl, + FormArray, + FormGroup, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { + ModbusDataType, + ModbusFunctionCodeTranslationsMap, + ModbusObjectCountByDataType, + ModbusValue, + ModbusValueKey, + noLeadTrailSpacesRegex, +} from '@home/components/widget/lib/gateway/gateway-widget.models'; +import { CommonModule } from '@angular/common'; +import { SharedModule } from '@shared/shared.module'; +import { GatewayHelpLinkPipe } from '@home/components/widget/lib/gateway/pipes/gateway-help-link.pipe'; +import { generateSecret } from '@core/utils'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; + +@Component({ + selector: 'tb-modbus-data-keys-panel', + templateUrl: './modbus-data-keys-panel.component.html', + styleUrls: ['./modbus-data-keys-panel.component.scss'], + standalone: true, + imports: [ + CommonModule, + SharedModule, + GatewayHelpLinkPipe, + ] +}) +export class ModbusDataKeysPanelComponent implements OnInit, OnDestroy { + + @coerceBoolean() + @Input() isMaster = false; + @Input() panelTitle: string; + @Input() addKeyTitle: string; + @Input() deleteKeyTitle: string; + @Input() noKeysText: string; + @Input() keysType: ModbusValueKey; + @Input() values: ModbusValue[]; + @Input() popover: TbPopoverComponent; + + @Output() keysDataApplied = new EventEmitter>(); + + keysListFormArray: FormArray; + modbusDataTypes = Object.values(ModbusDataType); + withFunctionCode = true; + functionCodesMap = new Map(); + defaultFunctionCodes = []; + + readonly editableDataTypes = [ModbusDataType.BYTES, ModbusDataType.BITS, ModbusDataType.STRING]; + readonly ModbusFunctionCodeTranslationsMap = ModbusFunctionCodeTranslationsMap; + + private destroy$ = new Subject(); + + private readonly defaultReadFunctionCodes = [3, 4]; + private readonly defaultWriteFunctionCodes = [5, 6, 15, 16]; + private readonly stringAttrUpdatesWriteFunctionCodes = [6, 16]; + + constructor(private fb: UntypedFormBuilder) {} + + ngOnInit(): void { + this.withFunctionCode = !this.isMaster || (this.keysType !== ModbusValueKey.ATTRIBUTES && this.keysType !== ModbusValueKey.TIMESERIES); + this.keysListFormArray = this.prepareKeysFormArray(this.values); + this.defaultFunctionCodes = this.getDefaultFunctionCodes(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + trackByControlId(_: number, keyControl: AbstractControl): string { + return keyControl.value.id; + } + + addKey(): void { + const dataKeyFormGroup = this.fb.group({ + tag: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + value: [{value: '', disabled: !this.isMaster}, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + type: [ModbusDataType.BYTES, [Validators.required]], + address: [null, [Validators.required]], + objectsCount: [1, [Validators.required]], + functionCode: [{ value: this.getDefaultFunctionCodes()[0], disabled: !this.withFunctionCode }, [Validators.required]], + id: [{value: generateSecret(5), disabled: true}], + }); + this.observeKeyDataType(dataKeyFormGroup); + + this.keysListFormArray.push(dataKeyFormGroup); + } + + deleteKey($event: Event, index: number): void { + if ($event) { + $event.stopPropagation(); + } + this.keysListFormArray.removeAt(index); + this.keysListFormArray.markAsDirty(); + } + + cancel(): void { + this.popover.hide(); + } + + applyKeysData(): void { + this.keysDataApplied.emit(this.keysListFormArray.value); + } + + private prepareKeysFormArray(values: ModbusValue[]): UntypedFormArray { + const keysControlGroups: Array = []; + + if (values) { + values.forEach(value => { + const dataKeyFormGroup = this.createDataKeyFormGroup(value); + this.observeKeyDataType(dataKeyFormGroup); + this.functionCodesMap.set(dataKeyFormGroup.get('id').value, this.getFunctionCodes(value.type)); + + keysControlGroups.push(dataKeyFormGroup); + }); + } + + return this.fb.array(keysControlGroups); + } + + private createDataKeyFormGroup(modbusValue: ModbusValue): FormGroup { + const { tag, value, type, address, objectsCount, functionCode } = modbusValue; + + return this.fb.group({ + tag: [tag, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + value: [{ value, disabled: !this.isMaster }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + type: [type, [Validators.required]], + address: [address, [Validators.required]], + objectsCount: [objectsCount, [Validators.required]], + functionCode: [{ value: functionCode, disabled: !this.withFunctionCode }, [Validators.required]], + id: [{ value: generateSecret(5), disabled: true }], + }); + } + + private observeKeyDataType(keyFormGroup: FormGroup): void { + keyFormGroup.get('type').valueChanges.pipe(takeUntil(this.destroy$)).subscribe(dataType => { + if (!this.editableDataTypes.includes(dataType)) { + keyFormGroup.get('objectsCount').patchValue(ModbusObjectCountByDataType[dataType], {emitEvent: false}); + } + this.updateFunctionCodes(keyFormGroup, dataType); + }); + } + + private updateFunctionCodes(keyFormGroup: FormGroup, dataType: ModbusDataType): void { + const functionCodes = this.getFunctionCodes(dataType); + this.functionCodesMap.set(keyFormGroup.get('id').value, functionCodes); + if (!functionCodes.includes(keyFormGroup.get('functionCode').value)) { + keyFormGroup.get('functionCode').patchValue(functionCodes[0], {emitEvent: false}); + } + } + + private getFunctionCodes(dataType: ModbusDataType): number[] { + if (this.keysType === ModbusValueKey.ATTRIBUTES_UPDATES) { + return dataType === ModbusDataType.STRING + ? this.stringAttrUpdatesWriteFunctionCodes + : this.defaultWriteFunctionCodes; + } + + const functionCodes = [...this.defaultReadFunctionCodes]; + if (dataType === ModbusDataType.BITS) { + const bitsFunctionCodes = [1, 2]; + functionCodes.push(...bitsFunctionCodes); + functionCodes.sort(); + } + if (this.keysType === ModbusValueKey.RPC_REQUESTS) { + functionCodes.push(...this.defaultWriteFunctionCodes); + } + + return functionCodes; + } + + private getDefaultFunctionCodes(): number[] { + if (this.keysType === ModbusValueKey.ATTRIBUTES_UPDATES) { + return this.defaultWriteFunctionCodes; + } + if (this.keysType === ModbusValueKey.RPC_REQUESTS) { + return [...this.defaultReadFunctionCodes, ...this.defaultWriteFunctionCodes]; + } + return this.defaultReadFunctionCodes; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.html new file mode 100644 index 0000000000..e16aa0c51f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.html @@ -0,0 +1,131 @@ + +
+
+ +
+
+ {{ 'gateway.servers-slaves' | translate}} +
+ + + +
+
+ +
+ + +   + + + +
+
+
+ + + + {{ 'gateway.name' | translate }} + + + {{ mapping['name'] }} + + + + + {{ 'gateway.client-communication-type' | translate }} + + + {{ ModbusProtocolLabelsMap.get(mapping['type']) }} + + + + + + +
+ + +
+
+ + + + + +
+
+
+ + +
+
+ +
+
+ + widget.no-data-found + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.scss new file mode 100644 index 0000000000..e9a5d3ebcd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.scss @@ -0,0 +1,90 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@import '../scss/constants'; + +:host { + width: 100%; + height: 100%; + display: block; + + .tb-master-table { + + .tb-master-table-content { + width: 100%; + height: 100%; + background: #fff; + overflow: hidden; + + .mat-toolbar-tools{ + min-height: auto; + } + + .title-container{ + overflow: hidden; + } + + .tb-master-table-title { + padding-right: 20px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .table-container { + overflow: auto; + + .mat-mdc-table { + table-layout: fixed; + min-width: 450px; + + .table-value-column { + padding: 0 12px; + width: 38%; + } + } + } + } + } + + .no-data-found { + height: calc(100% - 120px); + } + + @media #{$mat-xs} { + .mat-toolbar { + height: auto; + min-height: 100px; + + .tb-master-table-title{ + padding-bottom: 5px; + width: 100%; + } + } + } +} + +:host ::ng-deep { + mat-cell.tb-value-cell { + cursor: pointer; + + .mat-icon { + height: 24px; + width: 24px; + font-size: 24px; + color: #757575 + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.ts new file mode 100644 index 0000000000..2cb66f6062 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-master-table/modbus-master-table.component.ts @@ -0,0 +1,228 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + forwardRef, + OnDestroy, + OnInit, + ViewChild, +} from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { MatDialog } from '@angular/material/dialog'; +import { DialogService } from '@core/services/dialog.service'; +import { Subject } from 'rxjs'; +import { debounceTime, distinctUntilChanged, take, takeUntil } from 'rxjs/operators'; +import { + ControlValueAccessor, + FormArray, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormGroup, + ValidationErrors, + Validator, +} from '@angular/forms'; +import { + ModbusMasterConfig, + ModbusProtocolLabelsMap, + SlaveConfig +} from '@home/components/widget/lib/gateway/gateway-widget.models'; +import { isDefinedAndNotNull, isUndefinedOrNull } from '@core/utils'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { ModbusSlaveDialogComponent } from '../modbus-slave-dialog/modbus-slave-dialog.component'; +import { TbTableDatasource } from '@shared/components/table/table-datasource.abstract'; + +@Component({ + selector: 'tb-modbus-master-table', + templateUrl: './modbus-master-table.component.html', + styleUrls: ['./modbus-master-table.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ModbusMasterTableComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => ModbusMasterTableComponent), + multi: true + } + ], + standalone: true, + imports: [CommonModule, SharedModule] +}) +export class ModbusMasterTableComponent implements ControlValueAccessor, Validator, AfterViewInit, OnInit, OnDestroy { + + @ViewChild('searchInput') searchInputField: ElementRef; + + textSearchMode = false; + dataSource: SlavesDatasource; + masterFormGroup: UntypedFormGroup; + textSearch = this.fb.control('', {nonNullable: true}); + + readonly ModbusProtocolLabelsMap = ModbusProtocolLabelsMap; + + private onChange: (value: ModbusMasterConfig) => void = () => {}; + private onTouched: () => void = () => {}; + + private destroy$ = new Subject(); + + constructor( + public translate: TranslateService, + public dialog: MatDialog, + private dialogService: DialogService, + private fb: FormBuilder, + private cdr: ChangeDetectorRef, + ) { + this.masterFormGroup = this.fb.group({ slaves: this.fb.array([]) }); + this.dataSource = new SlavesDatasource(); + } + + get slaves(): FormArray { + return this.masterFormGroup.get('slaves') as FormArray; + } + + ngOnInit(): void { + this.masterFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { + this.updateTableData(value.slaves); + this.onChange(value); + this.onTouched(); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + ngAfterViewInit(): void { + this.textSearch.valueChanges.pipe( + debounceTime(150), + distinctUntilChanged((prev, current) => (prev ?? '') === current.trim()), + takeUntil(this.destroy$) + ).subscribe(text => this.updateTableData(this.slaves.value, text.trim())); + } + + registerOnChange(fn: (value: ModbusMasterConfig) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + writeValue(master: ModbusMasterConfig): void { + this.slaves.clear(); + this.pushDataAsFormArrays(master.slaves); + } + + validate(): ValidationErrors | null { + return this.slaves.controls.length ? null : { + slavesFormGroup: {valid: false} + }; + } + + enterFilterMode(): void { + this.textSearchMode = true; + this.cdr.detectChanges(); + const searchInput = this.searchInputField.nativeElement; + searchInput.focus(); + searchInput.setSelectionRange(0, 0); + } + + exitFilterMode(): void { + this.updateTableData(this.slaves.value); + this.textSearchMode = false; + this.textSearch.reset(); + } + + manageSlave($event: Event, index?: number): void { + if ($event) { + $event.stopPropagation(); + } + const withIndex = isDefinedAndNotNull(index); + const value = withIndex ? this.slaves.at(index).value : {}; + this.dialog.open(ModbusSlaveDialogComponent, { + disableClose: true, + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], + data: { + value, + buttonTitle: withIndex ? 'action.add' : 'action.apply' + } + }).afterClosed() + .pipe(take(1), takeUntil(this.destroy$)) + .subscribe(res => { + if (res) { + if (withIndex) { + this.slaves.at(index).patchValue(res); + } else { + this.slaves.push(this.fb.control(res)); + } + this.masterFormGroup.markAsDirty(); + } + }); + } + + deleteMapping($event: Event, index: number): void { + if ($event) { + $event.stopPropagation(); + } + this.dialogService.confirm( + this.translate.instant('gateway.delete-slave-title'), + '', + this.translate.instant('action.no'), + this.translate.instant('action.yes'), + true + ).pipe(take(1), takeUntil(this.destroy$)).subscribe((result) => { + if (result) { + this.slaves.removeAt(index); + this.masterFormGroup.markAsDirty(); + } + }); + } + + private updateTableData(data: SlaveConfig[], textSearch?: string): void { + if (textSearch) { + data = data.filter(item => + Object.values(item).some(value => + value.toString().toLowerCase().includes(textSearch.toLowerCase()) + ) + ); + } + this.dataSource.loadData(data); + } + + private pushDataAsFormArrays(slaves: SlaveConfig[]): void { + if (slaves?.length) { + slaves.forEach((slave: SlaveConfig) => this.slaves.push(this.fb.control(slave))); + } + } +} + +export class SlavesDatasource extends TbTableDatasource { + constructor() { + super(); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.html new file mode 100644 index 0000000000..66db8c5018 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.html @@ -0,0 +1,62 @@ + +
+
{{ 'gateway.hints.path-in-os' | translate }}
+
+
gateway.client-cert-path
+
+ + + +
+
+
+
gateway.private-key-path
+
+ + + +
+
+
+
gateway.password
+
+ + +
+ +
+
+
+
+
+
gateway.server-hostname
+
+ + + +
+
+
+ + + {{ 'gateway.request-client-certificate' | translate }} + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.ts new file mode 100644 index 0000000000..bc40727b55 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-security-config/modbus-security-config.component.ts @@ -0,0 +1,163 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + forwardRef, + Input, + OnChanges, + OnDestroy +} from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormGroup, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { + ModbusSecurity, + noLeadTrailSpacesRegex, +} from '@home/components/widget/lib/gateway/gateway-widget.models'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-modbus-security-config', + templateUrl: './modbus-security-config.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ModbusSecurityConfigComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => ModbusSecurityConfigComponent), + multi: true + } + ], + standalone: true, + imports: [ + CommonModule, + SharedModule, + ] +}) +export class ModbusSecurityConfigComponent implements ControlValueAccessor, Validator, OnChanges, OnDestroy { + + @coerceBoolean() + @Input() isMaster = false; + + securityConfigFormGroup: UntypedFormGroup; + + private disabled = false; + + private onChange: (value: ModbusSecurity) => void; + private onTouched: () => void; + + private destroy$ = new Subject(); + + constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) { + this.securityConfigFormGroup = this.fb.group({ + certfile: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], + keyfile: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], + password: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], + server_hostname: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], + reqclicert: [{value: false, disabled: true}], + }); + + this.observeValueChanges(); + } + + ngOnChanges(): void { + this.updateMasterEnabling(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnChange(fn: (value: ModbusSecurity) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (this.disabled) { + this.securityConfigFormGroup.disable({emitEvent: false}); + } else { + this.securityConfigFormGroup.enable({emitEvent: false}); + } + this.updateMasterEnabling(); + this.cdr.markForCheck(); + } + + validate(): ValidationErrors | null { + return this.securityConfigFormGroup.valid ? null : { + securityConfigFormGroup: { valid: false } + }; + } + + writeValue(securityConfig: ModbusSecurity): void { + const { certfile, password, keyfile, server_hostname } = securityConfig; + const securityState = { + certfile: certfile ?? '', + password: password ?? '', + keyfile: keyfile ?? '', + server_hostname: server_hostname ?? '', + reqclicert: !!securityConfig.reqclicert, + }; + + this.securityConfigFormGroup.reset(securityState, {emitEvent: false}); + } + + private updateMasterEnabling(): void { + if (this.isMaster) { + if (!this.disabled) { + this.securityConfigFormGroup.get('reqclicert').enable({emitEvent: false}); + } + this.securityConfigFormGroup.get('server_hostname').disable({emitEvent: false}); + } else { + if (!this.disabled) { + this.securityConfigFormGroup.get('server_hostname').enable({emitEvent: false}); + } + this.securityConfigFormGroup.get('reqclicert').disable({emitEvent: false}); + } + } + + private observeValueChanges(): void { + this.securityConfigFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value: ModbusSecurity) => { + this.onChange(value); + this.onTouched(); + }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.html new file mode 100644 index 0000000000..d37c8cc9ee --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.html @@ -0,0 +1,263 @@ + +
+
+
{{ 'gateway.hints.modbus-server' | translate }}
+
+ + + {{ 'gateway.enable' | translate }} + + +
+
+
+
+
gateway.server-slave-config
+ + {{ ModbusProtocolLabelsMap.get(type) }} + +
+
+
+
gateway.host
+
+ + + + warning + + +
+
+
+
gateway.port
+
+ + + + warning + + +
+
+ +
+
gateway.port
+
+ + + + warning + + +
+
+
+
+
+ gateway.method +
+
+ + + {{ ModbusMethodLabelsMap.get(method) }} + + +
+
+
+
+
gateway.unit-id
+
+ + + + warning + + +
+
+
+
gateway.device-name
+
+ + + + warning + + +
+
+
+
gateway.device-profile
+
+ + + + warning + + +
+
+
+
gateway.poll-period
+
+ + + +
+
+
+
gateway.baudrate
+
+ + + {{ rate }} + + +
+
+
+ + + +
gateway.advanced-connection-settings
+
+
+
+
+
gateway.byte-order
+
+ + + {{ order }} + + +
+
+
+ + + + + + {{ 'gateway.tls-connection' | translate }} + + + + + + +
+ +
+
gateway.vendor-name
+
+ + + +
+
+
+
gateway.product-code
+
+ + + +
+
+
+
gateway.vendor-url
+
+ + + +
+
+
+
gateway.product-name
+
+ + + +
+
+
+
gateway.model-name
+
+ + + +
+
+
+
+
+
+
+
gateway.values
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.scss new file mode 100644 index 0000000000..a464832202 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.scss @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +$server-config-header-height: 132px; + +:host { + .slave-content { + height: calc(100% - #{$server-config-header-height}); + overflow: auto; + } + + .slave-container { + display: inherit; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.ts new file mode 100644 index 0000000000..2a8488a2d4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-config/modbus-slave-config.component.ts @@ -0,0 +1,287 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectionStrategy, Component, forwardRef, OnDestroy } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormControl, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormGroup, + ValidationErrors, + Validator, + Validators, +} from '@angular/forms'; +import { + ModbusBaudrates, + ModbusMethodLabelsMap, + ModbusMethodType, + ModbusOrderType, + ModbusProtocolLabelsMap, + ModbusProtocolType, + ModbusRegisterValues, + ModbusSerialMethodType, + ModbusSlave, + noLeadTrailSpacesRegex, + PortLimits, + SlaveConfig, +} from '@home/components/widget/lib/gateway/gateway-widget.models'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { Subject } from 'rxjs'; +import { startWith, takeUntil } from 'rxjs/operators'; +import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe'; +import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component'; +import { ModbusValuesComponent, } from '../modbus-values/modbus-values.component'; +import { isEqual } from '@core/utils'; + +@Component({ + selector: 'tb-modbus-slave-config', + templateUrl: './modbus-slave-config.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ModbusSlaveConfigComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => ModbusSlaveConfigComponent), + multi: true + } + ], + standalone: true, + imports: [ + CommonModule, + SharedModule, + ModbusValuesComponent, + ModbusSecurityConfigComponent, + GatewayPortTooltipPipe, + ], + styleUrls: ['./modbus-slave-config.component.scss'], +}) +export class ModbusSlaveConfigComponent implements ControlValueAccessor, Validator, OnDestroy { + + slaveConfigFormGroup: UntypedFormGroup; + showSecurityControl: FormControl; + ModbusProtocolLabelsMap = ModbusProtocolLabelsMap; + ModbusMethodLabelsMap = ModbusMethodLabelsMap; + portLimits = PortLimits; + + readonly modbusProtocolTypes = Object.values(ModbusProtocolType); + readonly modbusMethodTypes = Object.values(ModbusMethodType); + readonly modbusSerialMethodTypes = Object.values(ModbusSerialMethodType); + readonly modbusOrderType = Object.values(ModbusOrderType); + readonly ModbusProtocolType = ModbusProtocolType; + readonly modbusBaudrates = ModbusBaudrates; + + private readonly serialSpecificControlKeys = ['serialPort', 'baudrate']; + private readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host']; + + private onChange: (value: SlaveConfig) => void; + private onTouched: () => void; + + private destroy$ = new Subject(); + + constructor(private fb: FormBuilder) { + this.showSecurityControl = this.fb.control(false); + this.slaveConfigFormGroup = this.fb.group({ + type: [ModbusProtocolType.TCP], + host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]], + serialPort: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + method: [ModbusMethodType.SOCKET], + unitId: [0, [Validators.required]], + baudrate: [this.modbusBaudrates[0]], + deviceName: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + deviceType: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + pollPeriod: [5000], + sendDataToThingsBoard: [false], + byteOrder:[ModbusOrderType.BIG], + security: [], + identity: this.fb.group({ + vendorName: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], + productCode: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], + vendorUrl: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], + productName: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], + modelName: ['', [Validators.pattern(noLeadTrailSpacesRegex)]], + }), + values: [], + }); + + this.observeValueChanges(); + this.observeTypeChange(); + this.observeFormEnable(); + this.observeShowSecurity(); + } + + get isSlaveEnabled(): boolean { + return this.slaveConfigFormGroup.get('sendDataToThingsBoard').value; + } + + get protocolType(): ModbusProtocolType { + return this.slaveConfigFormGroup.get('type').value; + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnChange(fn: (value: SlaveConfig) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + validate(): ValidationErrors | null { + return this.slaveConfigFormGroup.valid ? null : { + slaveConfigFormGroup: { valid: false } + }; + } + + writeValue(slaveConfig: ModbusSlave): void { + this.showSecurityControl.patchValue(!!slaveConfig.security && !isEqual(slaveConfig.security, {})); + this.updateSlaveConfig(slaveConfig); + this.updateFormEnableState(slaveConfig.sendDataToThingsBoard); + } + + private observeValueChanges(): void { + this.slaveConfigFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value: SlaveConfig) => { + if (value.type === ModbusProtocolType.Serial) { + value.port = value.serialPort; + delete value.serialPort; + } + this.onChange(value); + this.onTouched(); + }); + } + + private observeTypeChange(): void { + this.slaveConfigFormGroup.get('type').valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(type => { + this.updateFormEnableState(this.isSlaveEnabled); + this.updateMethodType(type); + }); + } + + private updateMethodType(type: ModbusProtocolType): void { + if (this.slaveConfigFormGroup.get('method').value !== ModbusMethodType.RTU) { + this.slaveConfigFormGroup.get('method').patchValue( + type === ModbusProtocolType.Serial + ? ModbusSerialMethodType.ASCII + : ModbusMethodType.SOCKET, + {emitEvent: false} + ); + } + } + + private observeFormEnable(): void { + this.slaveConfigFormGroup.get('sendDataToThingsBoard').valueChanges + .pipe(startWith(this.isSlaveEnabled), takeUntil(this.destroy$)) + .subscribe(value => this.updateFormEnableState(value)); + } + + private updateFormEnableState(enabled: boolean): void { + if (enabled) { + this.slaveConfigFormGroup.enable({emitEvent: false}); + this.showSecurityControl.enable({emitEvent: false}); + } else { + this.slaveConfigFormGroup.disable({emitEvent: false}); + this.showSecurityControl.disable({emitEvent: false}); + this.slaveConfigFormGroup.get('sendDataToThingsBoard').enable({emitEvent: false}); + } + this.updateEnablingByProtocol(this.protocolType); + this.updateSecurityEnable(this.showSecurityControl.value); + } + + private observeShowSecurity(): void { + this.showSecurityControl.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(value => this.updateSecurityEnable(value)); + } + + private updateSecurityEnable(securityEnabled: boolean): void { + if (securityEnabled && this.isSlaveEnabled && this.protocolType !== ModbusProtocolType.Serial) { + this.slaveConfigFormGroup.get('security').enable({emitEvent: false}); + } else { + this.slaveConfigFormGroup.get('security').disable({emitEvent: false}); + } + } + + private updateEnablingByProtocol(type: ModbusProtocolType): void { + const enableKeys = type === ModbusProtocolType.Serial ? this.serialSpecificControlKeys : this.tcpUdpSpecificControlKeys; + const disableKeys = type === ModbusProtocolType.Serial ? this.tcpUdpSpecificControlKeys : this.serialSpecificControlKeys; + + if (this.isSlaveEnabled) { + enableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({ emitEvent: false })); + } + + disableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({ emitEvent: false })); + } + + private updateSlaveConfig(slaveConfig: ModbusSlave): void { + const { + type = ModbusProtocolType.TCP, + method = ModbusMethodType.RTU, + unitId = 0, + deviceName = '', + deviceType = '', + pollPeriod = 5000, + sendDataToThingsBoard = false, + byteOrder = ModbusOrderType.BIG, + security = {}, + identity = { + vendorName: '', + productCode: '', + vendorUrl: '', + productName: '', + modelName: '', + }, + values = {} as ModbusRegisterValues, + baudrate = this.modbusBaudrates[0], + host = '', + port = null, + } = slaveConfig; + + const slaveState: ModbusSlave = { + type, + method, + unitId, + deviceName, + deviceType, + pollPeriod, + sendDataToThingsBoard: !!sendDataToThingsBoard, + byteOrder, + security, + identity, + values, + baudrate, + host: type === ModbusProtocolType.Serial ? '' : host, + port: type === ModbusProtocolType.Serial ? null : port, + serialPort: (type === ModbusProtocolType.Serial ? port : '') as string, + }; + + this.slaveConfigFormGroup.setValue(slaveState, { emitEvent: false }); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.html new file mode 100644 index 0000000000..1e47479f03 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.html @@ -0,0 +1,362 @@ + +
+ +

{{ 'gateway.server-slave' | translate }}

+ +
+ +
+
+
+
gateway.name
+
+ + + + warning + + +
+
+
+
+
+
gateway.server-connection
+ + {{ ModbusProtocolLabelsMap.get(type) }} + +
+
+
+
gateway.host
+
+ + + + warning + + +
+
+
+
gateway.port
+
+ + + + warning + + +
+
+ +
+
gateway.port
+
+ + + + warning + + +
+
+
+
+
+ gateway.method +
+
+ + + {{ ModbusMethodLabelsMap.get(method) }} + + +
+
+
+ +
+
gateway.baudrate
+
+ + + {{ rate }} + + +
+
+
+
gateway.bytesize
+
+ + + {{ size }} + + +
+
+
+
gateway.stopbits
+
+ + + +
+
+
+
gateway.parity
+
+ + + {{ ModbusParityLabelsMap.get(parity) }} + + +
+
+
+ + + {{ 'gateway.strict' | translate }} + + +
+
+
+
gateway.unit-id
+
+ + + + warning + + +
+
+
+
gateway.device-name
+
+ + + + warning + + +
+
+
+
gateway.device-profile
+
+ + + + warning + + +
+
+
+ + + {{ 'gateway.send-data-on-change' | translate }} + + +
+
+ + + +
gateway.advanced-connection-settings
+
+
+
+
+
gateway.connection-timeout
+
+ + + +
+
+
+
gateway.byte-order
+
+ + + {{ order }} + + +
+
+
+
gateway.word-order
+
+ + + {{ order }} + + +
+
+
+ + + + + + {{ 'gateway.tls-connection' | translate }} + + + + + + +
+
+ + + {{ 'gateway.retries' | translate }} + + +
+
+ + + {{ 'gateway.retries-on-empty' | translate }} + + +
+
+ + + {{ 'gateway.retries-on-invalid' | translate }} + + +
+
+
gateway.poll-period
+
+ + + +
+
+
+
gateway.connect-attempt-time
+
+ + + +
+
+
+
gateway.connect-attempt-count
+
+ + + +
+
+
+
gateway.wait-after-failed-attempts
+
+ + + +
+
+
+
+
+
+ +
+
+
+
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.scss new file mode 100644 index 0000000000..8cea184957 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.scss @@ -0,0 +1,21 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .slaves-config-container { + width: 80vw; + max-width: 900px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.ts new file mode 100644 index 0000000000..a00a488aa0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-slave-dialog/modbus-slave-dialog.component.ts @@ -0,0 +1,243 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { ChangeDetectionStrategy, Component, forwardRef, Inject, OnDestroy } from '@angular/core'; +import { + FormBuilder, + FormControl, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormGroup, + Validators, +} from '@angular/forms'; +import { + ModbusBaudrates, + ModbusByteSizes, + ModbusMethodLabelsMap, + ModbusMethodType, + ModbusOrderType, + ModbusParity, + ModbusParityLabelsMap, + ModbusProtocolLabelsMap, + ModbusProtocolType, + ModbusSerialMethodType, + ModbusSlaveInfo, + noLeadTrailSpacesRegex, + PortLimits, + SlaveConfig, +} from '@home/components/widget/lib/gateway/gateway-widget.models'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { Subject } from 'rxjs'; +import { ModbusValuesComponent } from '../modbus-values/modbus-values.component'; +import { ModbusSecurityConfigComponent } from '../modbus-security-config/modbus-security-config.component'; +import { DialogComponent } from '@shared/components/dialog.component'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { Router } from '@angular/router'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { GatewayPortTooltipPipe } from '@home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe'; +import { takeUntil } from 'rxjs/operators'; +import { isEqual } from '@core/utils'; + +@Component({ + selector: 'tb-modbus-slave-dialog', + templateUrl: './modbus-slave-dialog.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ModbusSlaveDialogComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => ModbusSlaveDialogComponent), + multi: true + } + ], + standalone: true, + imports: [ + CommonModule, + SharedModule, + ModbusValuesComponent, + ModbusSecurityConfigComponent, + GatewayPortTooltipPipe, + ], + styleUrls: ['./modbus-slave-dialog.component.scss'], +}) +export class ModbusSlaveDialogComponent extends DialogComponent implements OnDestroy { + + slaveConfigFormGroup: UntypedFormGroup; + showSecurityControl: FormControl; + portLimits = PortLimits; + + readonly modbusProtocolTypes = Object.values(ModbusProtocolType); + readonly modbusMethodTypes = Object.values(ModbusMethodType); + readonly modbusSerialMethodTypes = Object.values(ModbusSerialMethodType); + readonly modbusParities = Object.values(ModbusParity); + readonly modbusByteSizes = ModbusByteSizes; + readonly modbusBaudrates = ModbusBaudrates; + readonly modbusOrderType = Object.values(ModbusOrderType); + readonly ModbusProtocolType = ModbusProtocolType; + readonly ModbusParityLabelsMap = ModbusParityLabelsMap; + readonly ModbusProtocolLabelsMap = ModbusProtocolLabelsMap; + readonly ModbusMethodLabelsMap = ModbusMethodLabelsMap; + readonly modbusHelpLink = + 'https://thingsboard.io/docs/iot-gateway/config/modbus/#section-master-description-and-configuration-parameters'; + + private readonly serialSpecificControlKeys = ['serialPort', 'baudrate', 'stopbits', 'bytesize', 'parity', 'strict']; + private readonly tcpUdpSpecificControlKeys = ['port', 'security', 'host', 'wordOrder']; + + private destroy$ = new Subject(); + + constructor( + private fb: FormBuilder, + protected store: Store, + protected router: Router, + @Inject(MAT_DIALOG_DATA) public data: ModbusSlaveInfo, + public dialogRef: MatDialogRef, + ) { + super(store, router, dialogRef); + + this.showSecurityControl = this.fb.control(false); + this.initializeSlaveFormGroup(); + this.updateSlaveFormGroup(); + this.updateControlsEnabling(this.data.value.type); + this.observeTypeChange(); + this.observeShowSecurity(); + this.showSecurityControl.patchValue(!!this.data.value.security && !isEqual(this.data.value.security, {})); + } + + get protocolType(): ModbusProtocolType { + return this.slaveConfigFormGroup.get('type').value; + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + cancel(): void { + this.dialogRef.close(null); + } + + add(): void { + if (!this.slaveConfigFormGroup.valid) { + return; + } + + const { values, type, serialPort, ...rest } = this.slaveConfigFormGroup.value; + const slaveResult = { ...rest, type, ...values }; + + if (type === ModbusProtocolType.Serial) { + slaveResult.port = serialPort; + } + + this.dialogRef.close(slaveResult); + } + + private initializeSlaveFormGroup(): void { + this.slaveConfigFormGroup = this.fb.group({ + name: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + type: [ModbusProtocolType.TCP], + host: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + port: [null, [Validators.required, Validators.min(PortLimits.MIN), Validators.max(PortLimits.MAX)]], + serialPort: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + method: [ModbusMethodType.SOCKET, [Validators.required]], + baudrate: [this.modbusBaudrates[0]], + stopbits: [1], + bytesize: [ModbusByteSizes[0]], + parity: [ModbusParity.None], + strict: [true], + unitId: [0, [Validators.required]], + deviceName: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + deviceType: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + sendDataOnlyOnChange: [false], + timeout: [35], + byteOrder: [ModbusOrderType.BIG], + wordOrder: [ModbusOrderType.BIG], + retries: [true], + retryOnEmpty: [true], + retryOnInvalid: [true], + pollPeriod: [5000], + connectAttemptTimeMs: [5000], + connectAttemptCount: [5], + waitAfterFailedAttemptsMs: [300000], + values: [{}], + security: [{}], + }); + } + + private updateSlaveFormGroup(): void { + this.slaveConfigFormGroup.patchValue({ + ...this.data.value, + port: this.data.value.type === ModbusProtocolType.Serial ? null : this.data.value.port, + serialPort: this.data.value.type === ModbusProtocolType.Serial ? this.data.value.port : '', + values: { + attributes: this.data.value.attributes ?? [], + timeseries: this.data.value.timeseries ?? [], + attributeUpdates: this.data.value.attributeUpdates ?? [], + rpc: this.data.value.rpc ?? [], + } + }); + } + + private observeTypeChange(): void { + this.slaveConfigFormGroup.get('type').valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(type => { + this.updateControlsEnabling(type); + this.updateMethodType(type); + }); + } + + private updateMethodType(type: ModbusProtocolType): void { + if (this.slaveConfigFormGroup.get('method').value !== ModbusMethodType.RTU) { + this.slaveConfigFormGroup.get('method').patchValue( + type === ModbusProtocolType.Serial + ? ModbusSerialMethodType.ASCII + : ModbusMethodType.SOCKET, + {emitEvent: false} + ); + } + } + + private updateControlsEnabling(type: ModbusProtocolType): void { + const [enableKeys, disableKeys] = type === ModbusProtocolType.Serial + ? [this.serialSpecificControlKeys, this.tcpUdpSpecificControlKeys] + : [this.tcpUdpSpecificControlKeys, this.serialSpecificControlKeys]; + + enableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.enable({ emitEvent: false })); + disableKeys.forEach(key => this.slaveConfigFormGroup.get(key)?.disable({ emitEvent: false })); + + this.updateSecurityEnabling(this.showSecurityControl.value); + } + + private observeShowSecurity(): void { + this.showSecurityControl.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(value => this.updateSecurityEnabling(value)); + } + + private updateSecurityEnabling(isEnabled: boolean): void { + if (isEnabled && this.protocolType !== ModbusProtocolType.Serial) { + this.slaveConfigFormGroup.get('security').enable({emitEvent: false}); + } else { + this.slaveConfigFormGroup.get('security').disable({emitEvent: false}); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.html new file mode 100644 index 0000000000..a6b4382638 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.html @@ -0,0 +1,121 @@ + + + +
+ +
+
+ + + + +
+ +
+
+
+
+ + +
+
gateway.attributes
+
+ + + {{ attribute.tag }} + + + + + + +
+
+
+
gateway.timeseries
+
+ + + {{ telemetry.tag }} + + + + + + +
+
+
+
gateway.attribute-updates
+
+ + + {{ attributeUpdate.tag }} + + + + + + +
+
+
+
gateway.rpc-requests
+
+ + + {{ rpcRequest.tag }} + + + + + + +
+
+
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.scss new file mode 100644 index 0000000000..2d0782e6a5 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.scss @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +:host { + .mat-mdc-tab-body-wrapper { + min-height: 320px; + } +} + +::ng-deep .mdc-evolution-chip-set__chips { + align-items: center; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.ts new file mode 100644 index 0000000000..6bfc07c130 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-values/modbus-values.component.ts @@ -0,0 +1,236 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + forwardRef, + Input, + OnDestroy, + OnInit, + Renderer2, + ViewContainerRef +} from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, +} from '@angular/forms'; +import { + ModbusKeysAddKeyTranslationsMap, + ModbusKeysDeleteKeyTranslationsMap, + ModbusKeysNoKeysTextTranslationsMap, + ModbusKeysPanelTitleTranslationsMap, + ModbusRegisterTranslationsMap, + ModbusRegisterType, + ModbusRegisterValues, + ModbusValue, + ModbusValueKey, + ModbusValues, + ModbusValuesState, +} from '@home/components/widget/lib/gateway/gateway-widget.models'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; +import { takeUntil } from 'rxjs/operators'; +import { Subject } from 'rxjs'; +import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { ModbusDataKeysPanelComponent } from '../modbus-data-keys-panel/modbus-data-keys-panel.component'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-modbus-values', + templateUrl: './modbus-values.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => ModbusValuesComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => ModbusValuesComponent), + multi: true + } + ], + standalone: true, + imports: [ + CommonModule, + SharedModule, + EllipsisChipListDirective, + ], + styleUrls: ['./modbus-values.component.scss'] +}) + +export class ModbusValuesComponent implements ControlValueAccessor, Validator, OnInit, OnDestroy { + + @coerceBoolean() + @Input() singleMode = false; + + disabled = false; + modbusRegisterTypes: ModbusRegisterType[] = Object.values(ModbusRegisterType); + modbusValueKeys = Object.values(ModbusValueKey); + ModbusValuesTranslationsMap = ModbusRegisterTranslationsMap; + ModbusValueKey = ModbusValueKey; + valuesFormGroup: FormGroup; + + private onChange: (value: ModbusValuesState) => void; + private onTouched: () => void; + + private destroy$ = new Subject(); + + constructor(private fb: FormBuilder, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private cdr: ChangeDetectorRef, + ) {} + + ngOnInit() { + this.initializeValuesFormGroup(); + this.observeValuesChanges(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnChange(fn: (value: ModbusValuesState) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + writeValue(values: ModbusValuesState): void { + if (this.singleMode) { + this.valuesFormGroup.setValue(this.getSingleRegisterState(values as ModbusValues), { emitEvent: false }); + } else { + const { holding_registers, coils_initializer, input_registers, discrete_inputs } = values as ModbusRegisterValues; + this.valuesFormGroup.setValue({ + holding_registers: this.getSingleRegisterState(holding_registers), + coils_initializer: this.getSingleRegisterState(coils_initializer), + input_registers: this.getSingleRegisterState(input_registers), + discrete_inputs: this.getSingleRegisterState(discrete_inputs), + }, { emitEvent: false }); + } + this.cdr.markForCheck(); + } + + validate(): ValidationErrors | null { + return this.valuesFormGroup.valid ? null : { + valuesFormGroup: {valid: false} + }; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + this.cdr.markForCheck(); + } + + getValueGroup(valueKey: ModbusValueKey, register?: ModbusRegisterType): FormGroup { + return register + ? this.valuesFormGroup.get(register).get(valueKey) as FormGroup + : this.valuesFormGroup.get(valueKey) as FormGroup; + } + + manageKeys($event: Event, matButton: MatButton, keysType: ModbusValueKey, register?: ModbusRegisterType): void { + $event.stopPropagation(); + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + return; + } + + const keysControl = this.getValueGroup(keysType, register); + const ctx = { + values: keysControl.value, + isMaster: !this.singleMode, + keysType, + panelTitle: ModbusKeysPanelTitleTranslationsMap.get(keysType), + addKeyTitle: ModbusKeysAddKeyTranslationsMap.get(keysType), + deleteKeyTitle: ModbusKeysDeleteKeyTranslationsMap.get(keysType), + noKeysText: ModbusKeysNoKeysTextTranslationsMap.get(keysType) + }; + const dataKeysPanelPopover = this.popoverService.displayPopover( + trigger, + this.renderer, + this.viewContainerRef, + ModbusDataKeysPanelComponent, + 'leftBottom', + false, + null, + ctx, + {}, + {}, + {}, + true + ); + dataKeysPanelPopover.tbComponentRef.instance.popover = dataKeysPanelPopover; + dataKeysPanelPopover.tbComponentRef.instance.keysDataApplied.pipe(takeUntil(this.destroy$)).subscribe((keysData: ModbusValue[]) => { + dataKeysPanelPopover.hide(); + keysControl.patchValue(keysData); + keysControl.markAsDirty(); + this.cdr.markForCheck(); + }); + } + + private initializeValuesFormGroup(): void { + const getValuesFormGroup = () => this.fb.group(this.modbusValueKeys.reduce((acc, key) => { + acc[key] = this.fb.control([[], []]); + return acc; + }, {})); + + if (this.singleMode) { + this.valuesFormGroup = getValuesFormGroup(); + } else { + this.valuesFormGroup = this.fb.group( + this.modbusRegisterTypes.reduce((registersAcc, register) => { + registersAcc[register] = getValuesFormGroup(); + return registersAcc; + }, {}) + ); + } + } + + + private observeValuesChanges(): void { + this.valuesFormGroup.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(value => { + this.onChange(value); + this.onTouched(); + }); + } + + private getSingleRegisterState(values: ModbusValues): ModbusValues { + return { + attributes: values?.attributes ?? [], + timeseries: values?.timeseries ?? [], + attributeUpdates: values?.attributeUpdates ?? [], + rpc: values?.rpc ?? [], + }; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt-basic-config/mqtt-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt-basic-config/mqtt-basic-config.component.ts index 650ecef68a..8ae9fc6163 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt-basic-config/mqtt-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/mqtt-basic-config/mqtt-basic-config.component.ts @@ -25,22 +25,28 @@ import { Validator, } from '@angular/forms'; import { - ConnectorBaseConfig, MappingType, + MQTTBasicConfig, RequestMappingData, RequestType, } from '@home/components/widget/lib/gateway/gateway-widget.models'; import { SharedModule } from '@shared/shared.module'; import { CommonModule } from '@angular/common'; -import { - BrokerConfigControlComponent, - MappingTableComponent, - SecurityConfigComponent, - WorkersConfigControlComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/public-api'; import { takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; import { isObject } from 'lodash'; +import { + SecurityConfigComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component'; +import { + WorkersConfigControlComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component'; +import { + BrokerConfigControlComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/broker-config-control/broker-config-control.component'; +import { + MappingTableComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component'; @Component({ selector: 'tb-mqtt-basic-config', @@ -112,17 +118,18 @@ export class MqttBasicConfigComponent implements ControlValueAccessor, Validator this.onTouched = fn; } - writeValue(basicConfig: ConnectorBaseConfig): void { + writeValue(basicConfig: MQTTBasicConfig): void { + const { broker, dataMapping = [], requestsMapping } = basicConfig; const editedBase = { - workers: { - maxNumberOfWorkers: basicConfig.broker?.maxNumberOfWorkers, - maxMessageNumberPerWorker: basicConfig.broker?.maxMessageNumberPerWorker, - }, - dataMapping: basicConfig.dataMapping || [], - broker: basicConfig.broker || {}, - requestsMapping: Array.isArray(basicConfig.requestsMapping) - ? basicConfig.requestsMapping - : this.getRequestDataArray(basicConfig.requestsMapping), + workers: broker && (broker.maxNumberOfWorkers || broker.maxMessageNumberPerWorker) ? { + maxNumberOfWorkers: broker.maxNumberOfWorkers, + maxMessageNumberPerWorker: broker.maxMessageNumberPerWorker, + } : {}, + dataMapping: dataMapping || [], + broker: broker || {}, + requestsMapping: Array.isArray(requestsMapping) + ? requestsMapping + : this.getRequestDataArray(requestsMapping), }; this.basicFormGroup.setValue(editedBase, {emitEvent: false}); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.html new file mode 100644 index 0000000000..4c992c0e21 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.html @@ -0,0 +1,125 @@ + +
+
+
gateway.server-url
+
+ + + + warning + + +
+
+
+
+
{{ 'gateway.timeout' | translate }}
+
+
+ + + + warning + + +
+
+
+
gateway.security
+
+ + + {{ version.name }} + + +
+
+
+
+
{{ 'gateway.scan-period' | translate }}
+
+
+ + + + warning + + +
+
+
+
+
{{ 'gateway.sub-check-period' | translate }}
+
+
+ + + + warning + + +
+
+
+ + +
{{ 'gateway.enable-subscription' | translate }}
+
+
+
+
+ + + {{ 'gateway.show-map' | translate }} + + +
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/server-config/server-config.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.scss similarity index 82% rename from ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/server-config/server-config.component.scss rename to ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.scss index 64e886ffef..416f368279 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/server-config/server-config.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.scss @@ -17,12 +17,4 @@ width: 100%; height: 100%; display: block; - - .server-conf-field-title { - min-width: 250px; - width: 30%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/server-config/server-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.ts similarity index 71% rename from ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/server-config/server-config.component.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.ts index 98a36182a6..eddad255a3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/server-config/server-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component.ts @@ -33,24 +33,27 @@ import { } from '@home/components/widget/lib/gateway/gateway-widget.models'; import { SharedModule } from '@shared/shared.module'; import { CommonModule } from '@angular/common'; -import { SecurityConfigComponent } from '@home/components/widget/lib/gateway/connectors-configuration/public-api'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { TruncateWithTooltipDirective } from '@shared/directives/truncate-with-tooltip.directive'; +import { + SecurityConfigComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component'; @Component({ - selector: 'tb-server-config', - templateUrl: './server-config.component.html', - styleUrls: ['./server-config.component.scss'], + selector: 'tb-opc-server-config', + templateUrl: './opc-server-config.component.html', + styleUrls: ['./opc-server-config.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, providers: [ { provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => ServerConfigComponent), + useExisting: forwardRef(() => OpcServerConfigComponent), multi: true }, { provide: NG_VALIDATORS, - useExisting: forwardRef(() => ServerConfigComponent), + useExisting: forwardRef(() => OpcServerConfigComponent), multi: true } ], @@ -59,9 +62,10 @@ import { takeUntil } from 'rxjs/operators'; CommonModule, SharedModule, SecurityConfigComponent, + TruncateWithTooltipDirective, ] }) -export class ServerConfigComponent implements ControlValueAccessor, Validator, OnDestroy { +export class OpcServerConfigComponent implements ControlValueAccessor, Validator, OnDestroy { securityPolicyTypes = SecurityPolicyTypes; serverConfigFormGroup: UntypedFormGroup; @@ -112,6 +116,16 @@ export class ServerConfigComponent implements ControlValueAccessor, Validator, O } writeValue(serverConfig: ServerConfig): void { - this.serverConfigFormGroup.patchValue(serverConfig, {emitEvent: false}); + const { timeoutInMillis, scanPeriodInMillis, enableSubscriptions, subCheckPeriodInMillis, showMap, security } = serverConfig; + const serverConfigState = { + ...serverConfig, + timeoutInMillis: timeoutInMillis || 1000, + scanPeriodInMillis: scanPeriodInMillis || 1000, + enableSubscriptions: enableSubscriptions || true, + subCheckPeriodInMillis: subCheckPeriodInMillis || 10, + showMap: showMap || false, + security: security || SecurityPolicy.BASIC128, + }; + this.serverConfigFormGroup.reset(serverConfigState, {emitEvent: false}); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.html index 1089fd114c..5f8b25af9d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.html @@ -20,7 +20,7 @@ - +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.ts index 2c1dd7dd2b..39198c9696 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component.ts @@ -25,21 +25,29 @@ import { Validator, } from '@angular/forms'; import { - ConnectorBaseConfig, ConnectorType, MappingType, + OPCBasicConfig, } from '@home/components/widget/lib/gateway/gateway-widget.models'; import { SharedModule } from '@shared/shared.module'; import { CommonModule } from '@angular/common'; -import { - BrokerConfigControlComponent, - MappingTableComponent, - SecurityConfigComponent, - ServerConfigComponent, - WorkersConfigControlComponent -} from '@home/components/widget/lib/gateway/connectors-configuration/public-api'; import { takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; +import { + SecurityConfigComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component'; +import { + WorkersConfigControlComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component'; +import { + BrokerConfigControlComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/broker-config-control/broker-config-control.component'; +import { + MappingTableComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component'; +import { + OpcServerConfigComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component'; @Component({ selector: 'tb-opc-ua-basic-config', @@ -65,7 +73,7 @@ import { Subject } from 'rxjs'; WorkersConfigControlComponent, BrokerConfigControlComponent, MappingTableComponent, - ServerConfigComponent, + OpcServerConfigComponent, ], styleUrls: ['./opc-ua-basic-config.component.scss'] }) @@ -109,7 +117,7 @@ export class OpcUaBasicConfigComponent implements ControlValueAccessor, Validato this.onTouched = fn; } - writeValue(basicConfig: ConnectorBaseConfig): void { + writeValue(basicConfig: OPCBasicConfig): void { const editedBase = { server: basicConfig.server || {}, mapping: basicConfig.mapping || [], diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/public-api.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/public-api.ts deleted file mode 100644 index 5e185ddf0a..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/public-api.ts +++ /dev/null @@ -1,26 +0,0 @@ -/// -/// Copyright © 2016-2024 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -export * from './mapping-table/mapping-table.component'; -export * from './device-info-table/device-info-table.component'; -export * from './security-config/security-config.component'; -export * from './server-config/server-config.component'; -export * from './mapping-data-keys-panel/mapping-data-keys-panel.component'; -export * from './type-value-panel/type-value-panel.component'; -export * from './broker-config-control/broker-config-control.component'; -export * from './workers-config-control/workers-config-control.component'; -export * from './opc-ua-basic-config/opc-ua-basic-config.component'; -export * from './mqtt-basic-config/mqtt-basic-config.component'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.ts index 38a66daafc..18d1a96e38 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/security-config/security-config.component.ts @@ -16,6 +16,7 @@ import { ChangeDetectionStrategy, + ChangeDetectorRef, Component, forwardRef, Input, @@ -87,7 +88,7 @@ export class SecurityConfigComponent implements ControlValueAccessor, OnInit, On private destroy$ = new Subject(); - constructor(private fb: FormBuilder) {} + constructor(private fb: FormBuilder, private cdr: ChangeDetectorRef) {} ngOnInit(): void { this.securityFormGroup = this.fb.group({ @@ -127,6 +128,7 @@ export class SecurityConfigComponent implements ControlValueAccessor, OnInit, On } this.securityFormGroup.reset(securityInfo, {emitEvent: false}); } + this.cdr.markForCheck(); } validate(): ValidationErrors | null { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/server-config/server-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/server-config/server-config.component.html deleted file mode 100644 index 43b15de06c..0000000000 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/server-config/server-config.component.html +++ /dev/null @@ -1,127 +0,0 @@ - -
-
-
-
gateway.server-url
-
- - - - warning - - -
-
-
-
- gateway.timeout -
-
- - - - warning - - -
-
-
-
gateway.security
-
- - - {{ version.name }} - - -
-
-
-
- gateway.scan-period -
-
- - - - warning - - -
-
-
-
- gateway.sub-check-period -
-
- - - - warning - - -
-
-
-
- - - {{ 'gateway.enable-subscription' | translate }} - - -
-
- - - {{ 'gateway.show-map' | translate }} - - -
- - -
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component.html index 37f7b1422e..0a3bd4e6f3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component.html @@ -18,8 +18,8 @@
- gateway.max-number-of-workers + tb-hint-tooltip-icon="{{ 'gateway.max-number-of-workers-hint' | translate }}"> +
{{ 'gateway.max-number-of-workers' | translate }}
@@ -40,8 +40,8 @@
- gateway.max-messages-queue-for-worker + tb-hint-tooltip-icon="{{ 'gateway.max-messages-queue-for-worker-hint' | translate }}"> +
{{ 'gateway.max-messages-queue-for-worker' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component.ts index 9a37bdb07c..ad13b921bc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component.ts @@ -25,7 +25,9 @@ import { FormBuilder, NG_VALIDATORS, NG_VALUE_ACCESSOR, - UntypedFormGroup, ValidationErrors, Validator, + UntypedFormGroup, + ValidationErrors, + Validator, Validators } from '@angular/forms'; import { SharedModule } from '@shared/shared.module'; @@ -33,6 +35,7 @@ import { CommonModule } from '@angular/common'; import { WorkersConfig } from '@home/components/widget/lib/gateway/gateway-widget.models'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { TruncateWithTooltipDirective } from '@shared/directives/truncate-with-tooltip.directive'; @Component({ selector: 'tb-workers-config-control', @@ -42,6 +45,7 @@ import { takeUntil } from 'rxjs/operators'; imports: [ CommonModule, SharedModule, + TruncateWithTooltipDirective, ], providers: [ { @@ -91,7 +95,11 @@ export class WorkersConfigControlComponent implements OnDestroy, ControlValueAcc } writeValue(workersConfig: WorkersConfig): void { - this.workersConfigFormGroup.patchValue(workersConfig, {emitEvent: false}); + const { maxNumberOfWorkers, maxMessageNumberPerWorker } = workersConfig; + this.workersConfigFormGroup.reset({ + maxNumberOfWorkers: maxNumberOfWorkers || 100, + maxMessageNumberPerWorker: maxMessageNumberPerWorker || 10, + }, {emitEvent: false}); } validate(): ValidationErrors | null { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts index 7cdfbf5808..5a1b5d2a1b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts @@ -33,7 +33,7 @@ import { } from '@home/components/widget/lib/gateway/gateway-widget.models'; import { Subject } from 'rxjs'; import { ResourcesService } from '@core/services/resources.service'; -import { takeUntil, tap } from "rxjs/operators"; +import { takeUntil, tap } from 'rxjs/operators'; @Component({ selector: 'tb-add-connector-dialog', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.html index 35ac90f671..17c002115c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.html @@ -19,7 +19,7 @@

{{ MappingTypeTranslationsMap.get(this.data?.mappingType) | translate}}

-
+
-
- gateway.mqtt-qos +
+ {{ 'gateway.mqtt-qos' | translate }}
@@ -140,8 +140,8 @@
- gateway.extension + tb-hint-tooltip-icon="{{ 'gateway.extension-hint' | translate }}"> + {{ 'gateway.extension' | translate }}
@@ -369,8 +369,8 @@
- gateway.device-name-filter + tb-hint-tooltip-icon="{{ 'gateway.device-name-filter-hint' | translate }}"> + {{ 'gateway.device-name-filter' | translate }}
@@ -388,8 +388,8 @@
-
- gateway.attribute-filter +
+ {{ 'gateway.attribute-filter' | translate }}
@@ -472,8 +472,8 @@
-
- gateway.device-name-filter +
+ {{ 'gateway.device-name-filter' | translate }}
@@ -491,8 +491,8 @@
-
- gateway.method-filter +
+ {{ 'gateway.method-filter' | translate }}
@@ -580,8 +580,8 @@
-
- gateway.response-topic-Qos +
+ {{ 'gateway.response-topic-Qos' | translate }}
@@ -618,8 +618,8 @@
-
- gateway.device-node +
+ {{ 'gateway.device-node' | translate }}
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss index 190422eeb0..4212e8f834 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss @@ -47,6 +47,7 @@ .mdc-evolution-chip-set__chips { justify-content: flex-end; align-items: center; + flex-wrap: nowrap; } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.ts index 3812fa161e..9e5a43bc17 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.ts @@ -24,10 +24,15 @@ import { Router } from '@angular/router'; import { Attribute, AttributesUpdate, + ConnectorMapping, + ConnectorMappingFormValue, + ConverterMappingFormValue, ConvertorType, ConvertorTypeTranslationsMap, DataConversionTranslationsMap, + DeviceConnectorMapping, DeviceInfoType, + HelpLinkByMappingTypeMap, MappingHintTranslationsMap, MappingInfo, MappingKeysAddKeyTranslationsMap, @@ -37,11 +42,11 @@ import { MappingKeysType, MappingType, MappingTypeTranslationsMap, - MappingValue, noLeadTrailSpacesRegex, OPCUaSourceTypes, QualityTypes, QualityTypeTranslationsMap, + RequestMappingFormValue, RequestType, RequestTypesTranslationsMap, RpcMethod, @@ -55,14 +60,16 @@ import { startWith, takeUntil } from 'rxjs/operators'; import { MatButton } from '@angular/material/button'; import { TbPopoverService } from '@shared/components/popover.service'; import { TranslateService } from '@ngx-translate/core'; -import { MappingDataKeysPanelComponent } from '@home/components/widget/lib/gateway/connectors-configuration/public-api'; +import { + MappingDataKeysPanelComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component'; @Component({ selector: 'tb-mapping-dialog', templateUrl: './mapping-dialog.component.html', styleUrls: ['./mapping-dialog.component.scss'] }) -export class MappingDialogComponent extends DialogComponent implements OnDestroy { +export class MappingDialogComponent extends DialogComponent implements OnDestroy { mappingForm: UntypedFormGroup; @@ -97,6 +104,8 @@ export class MappingDialogComponent extends DialogComponent(); @@ -104,7 +113,7 @@ export class MappingDialogComponent extends DialogComponent, protected router: Router, @Inject(MAT_DIALOG_DATA) public data: MappingInfo, - public dialogRef: MatDialogRef, + public dialogRef: MatDialogRef, private fb: FormBuilder, private popoverService: TbPopoverService, private renderer: Renderer2, @@ -187,10 +196,6 @@ export class MappingDialogComponent extends DialogComponent

{{ 'gateway.connectors' | translate }}

- +
{{ 'gateway.statistics.command' | translate }} @@ -43,13 +48,16 @@ matSort [matSortActive]="pageLink.sortOrder.property" [matSortDirection]="pageLink.sortDirection()" matSortDisableClear> - {{ 'widgets.gateway.created-time' | translate }} + {{ 'widgets.gateway.created-time' | translate }} + - {{row[0]| date:'yyyy-MM-dd HH:mm:ss' }} + {{ row[0]| date:'yyyy-MM-dd HH:mm:ss' }} - {{ 'widgets.gateway.message' | translate }} + {{ 'widgets.gateway.message' | translate }} + {{ row[1] }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss index cd7722d12d..9f719807ca 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.scss @@ -28,6 +28,7 @@ height: 100%; margin-right: 35px; padding: 15px; + gap: 22px; } @media only screen and (max-width: 750px) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts index a3cb79e125..b17b8ef28a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-statistics.component.ts @@ -32,6 +32,7 @@ import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { MatTableDataSource } from '@angular/material/table'; import { MatSort } from '@angular/material/sort'; import { NULL_UUID } from '@shared/models/id/has-uuid'; +import { deepClone } from '@core/utils'; @Component({ selector: 'tb-gateway-statistics', @@ -135,6 +136,11 @@ export class GatewayStatisticsComponent implements AfterViewInit { } } + public navigateToStatistics() { + const params = deepClone(this.ctx.stateController.getStateParams()); + this.ctx.stateController.openState('configuration', params); + } + public sortData() { this.dataSource.sortData(this.dataSource.data, this.sort); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts index 9805008503..8df3994ea2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts @@ -174,13 +174,25 @@ export interface ConnectorSecurity { export type ConnectorMapping = DeviceConnectorMapping | RequestMappingData | ConverterConnectorMapping; -export interface ConnectorBaseConfig { - mapping?: DeviceConnectorMapping[]; - dataMapping?: ConverterConnectorMapping[]; - requestsMapping?: Record | RequestMappingData[]; - server?: ServerConfig; - broker?: BrokerConfig; - workers?: WorkersConfig; +export type ConnectorMappingFormValue = DeviceConnectorMapping | RequestMappingFormValue | ConverterMappingFormValue; + +export type ConnectorBaseConfig = MQTTBasicConfig | OPCBasicConfig | ModbusBasicConfig; + +export interface MQTTBasicConfig { + dataMapping: ConverterConnectorMapping[]; + requestsMapping: Record | RequestMappingData[]; + broker: BrokerConfig; + workers: WorkersConfig; +} + +export interface OPCBasicConfig { + mapping: DeviceConnectorMapping[]; + server: ServerConfig; +} + +export interface ModbusBasicConfig { + master: ModbusMasterConfig; + slave: ModbusSlave; } export interface WorkersConfig { @@ -223,7 +235,7 @@ export interface AttributesUpdate { value: string; } -interface Converter { +export interface Converter { type: ConvertorType; deviceNameJsonExpression: string; deviceTypeJsonExpression: string; @@ -239,14 +251,20 @@ export interface ConverterConnectorMapping { converter: Converter; } +export type ConverterMappingFormValue = Omit & { + converter: { + type: ConvertorType; + } & Record; +}; + export interface DeviceConnectorMapping { deviceNodePattern: string; deviceNodeSource: string; deviceInfo: DeviceInfo; - attributes: Attribute[]; - timeseries: Timeseries[]; - rpc_methods: RpcMethod[]; - attributes_updates: AttributesUpdate[]; + attributes?: Attribute[]; + timeseries?: Timeseries[]; + rpc_methods?: RpcMethod[]; + attributes_updates?: AttributesUpdate[]; } export enum ConnectorType { @@ -475,6 +493,11 @@ export interface MappingInfo { buttonTitle: string; } +export interface ModbusSlaveInfo { + value: SlaveConfig; + buttonTitle: string; +} + export enum ConnectorConfigurationModes { BASIC = 'basic', ADVANCED = 'advanced' @@ -540,6 +563,14 @@ export const MappingHintTranslationsMap = new Map( ] ); +export const HelpLinkByMappingTypeMap = new Map( + [ + [MappingType.DATA, 'https://thingsboard.io/docs/iot-gateway/config/mqtt/#section-mapping'], + [MappingType.OPCUA, 'https://thingsboard.io/docs/iot-gateway/config/opc-ua/#section-mapping'], + [MappingType.REQUESTS, 'https://thingsboard.io/docs/iot-gateway/config/mqtt/#section-mapping'] + ] +); + export const QualityTypes = [0, 1 ,2]; export const QualityTypeTranslationsMap = new Map( @@ -597,6 +628,10 @@ export interface RequestMappingData { requestValue: RequestDataItem; } +export type RequestMappingFormValue = Omit & { + requestValue: Record; +}; + export interface RequestDataItem { type: string; details: string; @@ -739,3 +774,259 @@ export const SecurityPolicyTypes = [ { value: SecurityPolicy.BASIC256, name: 'Basic256' }, { value: SecurityPolicy.BASIC256SHA, name: 'Basic256SHA256' } ]; + +export enum ModbusProtocolType { + TCP = 'tcp', + UDP = 'udp', + Serial = 'serial', +} + +export const ModbusProtocolLabelsMap = new Map( + [ + [ModbusProtocolType.TCP, 'TCP'], + [ModbusProtocolType.UDP, 'UDP'], + [ModbusProtocolType.Serial, 'Serial'], + ] +); + +export enum ModbusMethodType { + SOCKET = 'socket', + RTU = 'rtu', +} + +export enum ModbusSerialMethodType { + RTU = 'rtu', + ASCII = 'ascii', +} + +export const ModbusMethodLabelsMap = new Map( + [ + [ModbusMethodType.SOCKET, 'Socket'], + [ModbusMethodType.RTU, 'RTU'], + [ModbusSerialMethodType.ASCII, 'ASCII'], + ] +); + +export const ModbusByteSizes = [5, 6, 7 ,8]; + +export enum ModbusParity { + Even = 'E', + Odd = 'O', + None = 'N' +} + +export const ModbusParityLabelsMap = new Map( + [ + [ModbusParity.Even, 'Even'], + [ModbusParity.Odd, 'Odd'], + [ModbusParity.None, 'None'], + ] +); + +export enum ModbusOrderType { + BIG = 'BIG', + LITTLE = 'LITTLE', +} + +export enum ModbusRegisterType { + HoldingRegisters = 'holding_registers', + CoilsInitializer = 'coils_initializer', + InputRegisters = 'input_registers', + DiscreteInputs = 'discrete_inputs' +} + +export const ModbusRegisterTranslationsMap = new Map( + [ + [ModbusRegisterType.HoldingRegisters, 'gateway.holding_registers'], + [ModbusRegisterType.CoilsInitializer, 'gateway.coils_initializer'], + [ModbusRegisterType.InputRegisters, 'gateway.input_registers'], + [ModbusRegisterType.DiscreteInputs, 'gateway.discrete_inputs'] + ] +); + +export enum ModbusDataType { + STRING = 'string', + BYTES = 'bytes', + BITS = 'bits', + INT8 = '8int', + UINT8 = '8uint', + FLOAT8 = '8float', + INT16 = '16int', + UINT16 = '16uint', + FLOAT16 = '16float', + INT32 = '32int', + UINT32 = '32uint', + FLOAT32 = '32float', + INT64 = '64int', + UINT64 = '64uint', + FLOAT64 = '64float' +} + +export enum ModbusObjectCountByDataType { + '8int' = 1, + '8uint' = 1, + '8float' = 1, + '16int' = 1, + '16uint' = 1, + '16float' = 1, + '32int' = 2, + '32uint' = 2, + '32float' = 2, + '64int' = 4, + '64uint' = 4, + '64float' = 4, +} + +export enum ModbusValueKey { + ATTRIBUTES = 'attributes', + TIMESERIES = 'timeseries', + ATTRIBUTES_UPDATES = 'attributeUpdates', + RPC_REQUESTS = 'rpc', +} + +export const ModbusKeysPanelTitleTranslationsMap = new Map( + [ + [ModbusValueKey.ATTRIBUTES, 'gateway.attributes'], + [ModbusValueKey.TIMESERIES, 'gateway.timeseries'], + [ModbusValueKey.ATTRIBUTES_UPDATES, 'gateway.attribute-updates'], + [ModbusValueKey.RPC_REQUESTS, 'gateway.rpc-requests'] + ] +); + +export const ModbusKeysAddKeyTranslationsMap = new Map( + [ + [ModbusValueKey.ATTRIBUTES, 'gateway.add-attribute'], + [ModbusValueKey.TIMESERIES, 'gateway.add-timeseries'], + [ModbusValueKey.ATTRIBUTES_UPDATES, 'gateway.add-attribute-update'], + [ModbusValueKey.RPC_REQUESTS, 'gateway.add-rpc-request'] + ] +); + +export const ModbusKeysDeleteKeyTranslationsMap = new Map( + [ + [ModbusValueKey.ATTRIBUTES, 'gateway.delete-attribute'], + [ModbusValueKey.TIMESERIES, 'gateway.delete-timeseries'], + [ModbusValueKey.ATTRIBUTES_UPDATES, 'gateway.delete-attribute-update'], + [ModbusValueKey.RPC_REQUESTS, 'gateway.delete-rpc-request'] + ] +); + +export const ModbusKeysNoKeysTextTranslationsMap = new Map( + [ + [ModbusValueKey.ATTRIBUTES, 'gateway.no-attributes'], + [ModbusValueKey.TIMESERIES, 'gateway.no-timeseries'], + [ModbusValueKey.ATTRIBUTES_UPDATES, 'gateway.no-attribute-updates'], + [ModbusValueKey.RPC_REQUESTS, 'gateway.no-rpc-requests'] + ] +); + +export const ModbusFunctionCodeTranslationsMap = new Map( + [ + [1, 'gateway.read-coils'], + [2, 'gateway.read-discrete-inputs'], + [3, 'gateway.read-multiple-holding-registers'], + [4, 'gateway.read-input-registers'], + [5, 'gateway.write-coil'], + [6, 'gateway.write-register'], + [15, 'gateway.write-coils'], + [16, 'gateway.write-registers'], + ] +); + +export interface ModbusMasterConfig { + slaves: SlaveConfig[]; +} + +export interface SlaveConfig { + name: string; + host?: string; + port: string | number; + serialPort?: string; + type: ModbusProtocolType; + method: ModbusMethodType; + timeout: number; + byteOrder: ModbusOrderType; + wordOrder: ModbusOrderType; + retries: boolean; + retryOnEmpty: boolean; + retryOnInvalid: boolean; + pollPeriod: number; + unitId: number; + deviceName: string; + deviceType: string; + sendDataOnlyOnChange: boolean; + connectAttemptTimeMs: number; + connectAttemptCount: number; + waitAfterFailedAttemptsMs: number; + attributes: ModbusValue[]; + timeseries: ModbusValue[]; + attributeUpdates: ModbusValue[]; + rpc: ModbusValue[]; + security?: ModbusSecurity; + baudrate?: number; + stopbits?: number; + bytesize?: number; + parity?: ModbusParity; + strict?: boolean; +} + +export interface ModbusValue { + tag: string; + type: ModbusDataType; + functionCode?: number; + objectsCount: number; + address: number; + value?: string; +} + +export interface ModbusSecurity { + certfile?: string; + keyfile?: string; + password?: string; + server_hostname?: string; + reqclicert?: boolean; +} + +export interface ModbusSlave { + host?: string; + type: ModbusProtocolType; + method: ModbusMethodType; + unitId: number; + serialPort?: string; + baudrate?: number; + deviceName: string; + deviceType: string; + pollPeriod: number; + sendDataToThingsBoard: boolean; + byteOrder: ModbusOrderType; + identity: ModbusIdentity; + values: ModbusValuesState; + port: string | number; + security: ModbusSecurity; +} + +export type ModbusValuesState = ModbusRegisterValues | ModbusValues; + +export interface ModbusRegisterValues { + holding_registers: ModbusValues; + coils_initializer: ModbusValues; + input_registers: ModbusValues; + discrete_inputs: ModbusValues; +} + +export interface ModbusValues { + attributes: ModbusValue[]; + timeseries: ModbusValue[]; + attributeUpdates: ModbusValue[]; + rpc: ModbusValue[]; +} + +export interface ModbusIdentity { + vendorName?: string; + productCode?: string; + vendorUrl?: string; + productName?: string; + modelName?: string; +} + +export const ModbusBaudrates = [4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600]; diff --git a/ui-ngx/src/app/modules/home/pipes/gateway-help-link/gateway-help-link.pipe.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-help-link.pipe.ts similarity index 100% rename from ui-ngx/src/app/modules/home/pipes/gateway-help-link/gateway-help-link.pipe.ts rename to ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-help-link.pipe.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe.ts new file mode 100644 index 0000000000..fcf5766c05 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/pipes/gateway-port-tooltip.pipe.ts @@ -0,0 +1,42 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { Pipe, PipeTransform } from '@angular/core'; +import { PortLimits } from '@home/components/widget/lib/gateway/gateway-widget.models'; +import { AbstractControl } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; + +@Pipe({ + name: 'getGatewayPortTooltip', + standalone: true, +}) +export class GatewayPortTooltipPipe implements PipeTransform { + + constructor(private translate: TranslateService) {} + + transform(portControl: AbstractControl): string { + if (portControl.hasError('required')) { + return this.translate.instant('gateway.port-required'); + } + if (portControl.hasError('min') || portControl.hasError('max')) { + return this.translate.instant('gateway.port-limits-error', { + min: PortLimits.MIN, + max: PortLimits.MAX, + }); + } + return ''; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html index 7ec7562d31..72b5d8a7f9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.html @@ -48,7 +48,7 @@ {{ 'widgets.recent-dashboards.name' | translate }} - {{ lastVisitedDashboard.title }} + {{ lastVisitedDashboard.title }} @@ -78,7 +78,7 @@ class="star" [ngClass]="{'starred': dashboard.starred}">{{ dashboard.starred ? 'star' : 'star_border' }}
@@ -87,6 +87,7 @@ subscriptSizing="dynamic" appearance="outline" [useIdValue]="false" + [customerId]="customerId" label="" placeholder="{{ 'dashboard.select-dashboard' | translate }}" [(ngModel)]="starredDashboardValue" (ngModelChange)="onStarDashboard($event)"> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts index 69b2230e25..9ec876ffd6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/home-page/recent-dashboards-widget.component.ts @@ -21,7 +21,8 @@ import { Input, OnDestroy, OnInit, - QueryList, ViewChild, + QueryList, + ViewChild, ViewChildren } from '@angular/core'; import { PageComponent } from '@shared/components/page.component'; @@ -29,11 +30,12 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { Authority } from '@shared/models/authority.enum'; import { BehaviorSubject, Observable, of } from 'rxjs'; -import { getCurrentAuthUser } from '@core/auth/auth.selectors'; +import { getCurrentAuthState, getCurrentAuthUser } from '@core/auth/auth.selectors'; import { WidgetContext } from '@home/models/widget-component.models'; import { AbstractUserDashboardInfo, - LastVisitedDashboardInfo, StarredDashboardInfo, + LastVisitedDashboardInfo, + StarredDashboardInfo, UserDashboardAction, UserDashboardsInfo } from '@shared/models/user-settings.models'; @@ -77,6 +79,8 @@ export class RecentDashboardsWidgetComponent extends PageComponent implements On hasDashboardsAccess = true; dirty = false; + public customerId: string; + private isFullscreenMode = getCurrentAuthState(this.store).forceFullscreen; constructor(protected store: Store, private cd: ChangeDetectorRef, @@ -85,6 +89,9 @@ export class RecentDashboardsWidgetComponent extends PageComponent implements On } ngOnInit() { + if (this.authUser.authority === Authority.CUSTOMER_USER) { + this.customerId = this.authUser.customerId; + } this.hasDashboardsAccess = [Authority.TENANT_ADMIN, Authority.CUSTOMER_USER].includes(this.authUser.authority); if (this.hasDashboardsAccess) { this.reload(); @@ -110,6 +117,11 @@ export class RecentDashboardsWidgetComponent extends PageComponent implements On ); } + public createDashboardUrl(id: string): string { + const baseUrl = this.isFullscreenMode ? '/dashboard/' : '/dashboards/'; + return baseUrl + id; + } + toggleValueChange(value: 'last' | 'starred') { this.toggleValue = value; if (this.dirty) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts index 00cc3f24f9..12e2a1065b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/power-button-widget.models.ts @@ -24,7 +24,7 @@ import { SetValueSettings, ValueToDataType } from '@shared/models/action-widget-settings.models'; -import { Circle, Effect, Element, G, Gradient, Runner, Svg, Text, Timeline } from '@svgdotjs/svg.js'; +import { Circle, Effect, Element, G, Gradient, Path, Runner, Svg, Text, Timeline } from '@svgdotjs/svg.js'; import '@svgdotjs/svg.filter.js'; import tinycolor from 'tinycolor2'; import { WidgetContext } from '@home/models/widget-component.models'; @@ -35,7 +35,10 @@ export enum PowerButtonLayout { outlined = 'outlined', default_volume = 'default_volume', simplified_volume = 'simplified_volume', - outlined_volume = 'outlined_volume' + outlined_volume = 'outlined_volume', + default_icon = 'default_icon', + simplified_icon = 'simplified_icon', + outlined_icon = 'outlined_icon' } export const powerButtonLayouts = Object.keys(PowerButtonLayout) as PowerButtonLayout[]; @@ -47,7 +50,10 @@ export const powerButtonLayoutTranslations = new Map( [PowerButtonLayout.outlined, 'widgets.power-button.layout-outlined'], [PowerButtonLayout.default_volume, 'widgets.power-button.layout-default-volume'], [PowerButtonLayout.simplified_volume, 'widgets.power-button.layout-simplified-volume'], - [PowerButtonLayout.outlined_volume, 'widgets.power-button.layout-outlined-volume'] + [PowerButtonLayout.outlined_volume, 'widgets.power-button.layout-outlined-volume'], + [PowerButtonLayout.default_icon, 'widgets.power-button.layout-default-icon'], + [PowerButtonLayout.simplified_icon, 'widgets.power-button.layout-simplified-icon'], + [PowerButtonLayout.outlined_icon, 'widgets.power-button.layout-outlined-icon'] ] ); @@ -58,7 +64,10 @@ export const powerButtonLayoutImages = new Map( [PowerButtonLayout.outlined, 'assets/widget/power-button/outlined-layout.svg'], [PowerButtonLayout.default_volume, 'assets/widget/power-button/default-volume-layout.svg'], [PowerButtonLayout.simplified_volume, 'assets/widget/power-button/simplified-volume-layout.svg'], - [PowerButtonLayout.outlined_volume, 'assets/widget/power-button/outlined-volume-layout.svg'] + [PowerButtonLayout.outlined_volume, 'assets/widget/power-button/outlined-volume-layout.svg'], + [PowerButtonLayout.default_icon, 'assets/widget/power-button/default-icon-layout.svg'], + [PowerButtonLayout.simplified_icon, 'assets/widget/power-button/simplified-icon-layout.svg'], + [PowerButtonLayout.outlined_icon, 'assets/widget/power-button/outlined-icon-layout.svg'] ] ); @@ -219,6 +228,21 @@ export const powerButtonShapeSize = 110; const cx = powerButtonShapeSize / 2; const cy = powerButtonShapeSize / 2; +const powerCircle = 'M13 4.67063C13 3.65748 12.0377 2.91866 11.0946 3.28889C4.59815 5.83928 0 12.1545 0 19.5412C0 29.1835 ' + + '7.83502 37.0001 17.5 37.0001C27.165 37.0001 35 29.1835 35 19.5412C35 12.1545 30.4019 5.83928 23.9054 3.28889C22.9623 2.91866 22 ' + + '3.65748 22 4.67063C22 5.33931 22.434 5.92434 23.0519 6.17991C28.3077 8.35375 32 13.5209 32 19.5412C32 27.52 25.5148 34.0001 17.5 ' + + '34.0001C9.48521 34.0001 3 27.52 3 19.5412C3 13.5209 6.69234 8.35374 11.9481 6.17991C12.566 5.92434 13 5.33931 13 4.67063Z'; +const powerLine = 'M16.5 1C16.5 0.447716 16.9477 0 17.5 0C18.0523 0 18.5 0.447715 18.5 1V17C18.5 ' + + '17.5523 18.0523 18 17.5 18C16.9477 18 16.5 17.5523 16.5 17V1Z'; + +const powerCircleStroke = 'M12 2.95698C12 1.45236 10.4775 0.438524 9.19424 1.22416C3.68417 4.59764 0 10.7283 0 17.7316C0 ' + + '28.3732 8.50659 37 19 37C29.4934 37 38 28.3732 38 17.7316C38 10.7283 34.3158 4.59764 28.8058 1.22416C27.5225 ' + + '0.438524 26 1.45236 26 2.95698C26 3.73878 26.4365 4.44718 27.0911 4.87461C31.2354 7.58066 34 12.3083 34 ' + + '17.7316C34 26.2172 27.2316 33 19 33C10.7684 33 4 26.2172 4 17.7316C4 12.3084 6.76462 7.58066 10.9089 ' + + '4.87461C11.5635 4.44718 12 3.73878 12 2.95698Z'; +const powerLineStroke = 'M0 2.5C0 1.11929 1.11929 0 2.5 0C3.88071 0 5 1.11929 5 2.5V15.5C5 16.8807 3.88071 18 ' + + '2.5 18C1.11929 18 0 16.8807 0 15.5V2.5Z'; + const powerButtonAnimation = (element: Element): Runner => element.animate(200, 0, 'now'); export abstract class PowerButtonShape { @@ -242,6 +266,12 @@ export abstract class PowerButtonShape { return new SimplifiedVolumePowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); case PowerButtonLayout.outlined_volume: return new OutlinedVolumePowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); + case PowerButtonLayout.default_icon: + return new DefaultIconPowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); + case PowerButtonLayout.simplified_icon: + return new SimplifiedIconPowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); + case PowerButtonLayout.outlined_icon: + return new OutlinedIconPowerButtonShape(ctx, svgShape, settings, value, disabled, onClick); } } @@ -705,6 +735,31 @@ class DefaultVolumePowerButtonShape extends PowerButtonShape { private pressedTimeline: Timeline; private centerGroup: G; + + protected drawOffCenter(centerGroup: G) { + this.offLabelShape = this.createOffLabel('400').addTo(centerGroup); + } + + protected drawOnCenter() { + this.onLabelShape = this.createOnLabel('400'); + } + + protected addOnCenterToMask(onCircleShape: Circle) { + this.createMask(onCircleShape,[this.onLabelShape]); + } + + protected addOnCenterTimeLine(pressedTimeline: Timeline) { + this.onLabelShape.timeline(pressedTimeline); + } + + protected drawOffCenterColor(mainColor: PowerButtonColor) { + this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } + + protected onCenterAnimation(scale: number) { + powerButtonAnimation(this.onLabelShape).transform({scale, origin: {x: cx, y: cy}}); + } + protected drawShape() { this.outerBorder = this.svgShape.circle(powerButtonShapeSize).center(cx, cy) .fill({opacity: 0}).stroke({width: 0}); @@ -723,15 +778,15 @@ class DefaultVolumePowerButtonShape extends PowerButtonShape { add.stop(1, '#FFFFFF', 1); }).from(0.832, 0.1188).to(0.268, 0.92); this.centerGroup = this.svgShape.group(); - this.offLabelShape = this.createOffLabel('400').addTo(this.centerGroup); + this.drawOffCenter(this.centerGroup); this.onCircleShape = this.svgShape.circle(powerButtonShapeSize - 24).center(cx, cy); - this.onLabelShape = this.createOnLabel('400'); - this.createMask(this.onCircleShape, [this.onLabelShape]); + this.drawOnCenter(); + this.addOnCenterToMask(this.onCircleShape); this.innerShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 24, cx, cy, 3, 0.3); this.pressedTimeline = new Timeline(); this.centerGroup.timeline(this.pressedTimeline); - this.onLabelShape.timeline(this.pressedTimeline); + this.addOnCenterTimeLine(this.pressedTimeline); this.innerShadow.timeline(this.pressedTimeline); } @@ -751,7 +806,7 @@ class DefaultVolumePowerButtonShape extends PowerButtonShape { this.innerBorder.fill(this.innerBorderGradient); this.innerBorder.attr({ 'fill-opacity': 1 }); } - this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.drawOffCenterColor(mainColor); this.onCircleShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); } @@ -776,14 +831,14 @@ class DefaultVolumePowerButtonShape extends PowerButtonShape { this.innerShadow.show(); const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale, origin: {x: cx, y: cy}}); + this.onCenterAnimation(pressedScale); this.innerShadow.animate(6, 0.6); } protected onPressEnd() { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); + this.onCenterAnimation(1); this.innerShadow.animateRestore().after(() => { if (this.disabled) { this.innerShadow.hide(); @@ -793,6 +848,42 @@ class DefaultVolumePowerButtonShape extends PowerButtonShape { } +class DefaultIconPowerButtonShape extends DefaultVolumePowerButtonShape { + private offPowerSymbolCircle: Path; + private offPowerSymbolLine: Path; + private onPowerSymbolCircle: Path; + private onPowerSymbolLine: Path; + + protected drawOffCenter(centerGroup: G) { + this.offPowerSymbolCircle = this.svgShape.path(powerCircle).center(cx, cy).addTo(centerGroup); + this.offPowerSymbolLine = this.svgShape.path(powerLine).center(cx, cy-12).addTo(centerGroup); + } + + protected drawOnCenter() { + this.onPowerSymbolCircle = this.svgShape.path(powerCircle).center(cx, cy); + this.onPowerSymbolLine = this.svgShape.path(powerLine).center(cx, cy-12); + } + + protected addOnCenterToMask(onCircleShape: Circle) { + this.createMask(onCircleShape, [this.onPowerSymbolCircle, this.onPowerSymbolLine]); + } + + protected addOnCenterTimeLine(pressedTimeline: Timeline) { + this.onPowerSymbolCircle.timeline(pressedTimeline); + this.onPowerSymbolLine.timeline(pressedTimeline); + } + + protected drawOffCenterColor(mainColor: PowerButtonColor) { + this.offPowerSymbolCircle.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.offPowerSymbolLine.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } + + protected onCenterAnimation(scale: number) { + powerButtonAnimation(this.onPowerSymbolCircle).transform({scale, origin: {x: cx, y: cy}}); + powerButtonAnimation(this.onPowerSymbolLine).transform({scale, origin: {x: cx, y: cy}}); + } +} + class SimplifiedVolumePowerButtonShape extends PowerButtonShape { private outerBorder: Circle; @@ -805,6 +896,10 @@ class SimplifiedVolumePowerButtonShape extends PowerButtonShape { private centerGroup: G; private onCenterGroup: G; + protected drawCenterGroup(centerGroup: G, onCenterGroup: G) { + this.offLabelShape = this.createOffLabel().addTo(centerGroup); + this.onLabelShape = this.createOnLabel().addTo(onCenterGroup); + } protected drawShape() { this.outerBorder = this.svgShape.circle(powerButtonShapeSize).center(cx, cy) @@ -812,9 +907,8 @@ class SimplifiedVolumePowerButtonShape extends PowerButtonShape { this.outerBorderMask = this.svgShape.circle(powerButtonShapeSize - 4).center(cx, cy); this.createMask(this.outerBorder, [this.outerBorderMask]); this.centerGroup = this.svgShape.group(); - this.offLabelShape = this.createOffLabel().addTo(this.centerGroup); this.onCenterGroup = this.svgShape.group(); - this.onLabelShape = this.createOnLabel().addTo(this.onCenterGroup); + this.drawCenterGroup(this.centerGroup, this.onCenterGroup); this.innerShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 4, cx, cy, 3, 0.3); this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 4, cx, cy, 0, 0); this.pressedTimeline = new Timeline(); @@ -867,6 +961,27 @@ class SimplifiedVolumePowerButtonShape extends PowerButtonShape { } } +class SimplifiedIconPowerButtonShape extends SimplifiedVolumePowerButtonShape { + private offPowerSymbolCircle: Path; + private offPowerSymbolLine: Path; + private onPowerSymbolCircle: Path; + private onPowerSymbolLine: Path; + + protected drawCenterGroup(centerGroup: G, onCenterGroup: G) { + this.offPowerSymbolCircle = this.svgShape.path(powerCircle).center(cx, cy).addTo(centerGroup); + this.offPowerSymbolLine = this.svgShape.path(powerLine).center(cx, cy-12).addTo(centerGroup); + this.onPowerSymbolCircle = this.svgShape.path(powerCircle).center(cx, cy).addTo(onCenterGroup); + this.onPowerSymbolLine = this.svgShape.path(powerLine).center(cx, cy-12).addTo(onCenterGroup); + } + + protected drawColorState(mainColor: PowerButtonColor) { + this.offPowerSymbolCircle.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.offPowerSymbolLine.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.onPowerSymbolCircle.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.onPowerSymbolLine.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } +} + class OutlinedVolumePowerButtonShape extends PowerButtonShape { private outerBorder: Circle; private outerBorderMask: Circle; @@ -881,6 +996,30 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { private centerGroup: G; private onCenterGroup: G; + protected drawOffCenter(centerGroup: G) { + this.offLabelShape = this.createOffLabel('800').addTo(centerGroup); + } + + protected drawOnCenter() { + this.onLabelShape = this.createOnLabel('800'); + } + + protected addOnCenterToMask(onCircleShape: Circle) { + this.createMask(onCircleShape,[this.onLabelShape]); + } + + protected addOnCenterTimeLine(pressedTimeline: Timeline) { + this.onLabelShape.timeline(pressedTimeline); + } + + protected drawOffCenterColor(mainColor: PowerButtonColor) { + this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } + + protected onCenterAnimation(scale: number) { + powerButtonAnimation(this.onLabelShape).transform({scale, origin: {x: cx, y: cy}}); + } + protected drawShape() { this.outerBorder = this.svgShape.circle(powerButtonShapeSize).center(cx, cy) .fill({opacity: 0}).stroke({width: 0}); @@ -895,19 +1034,19 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { this.innerBorderMask = this.svgShape.circle(powerButtonShapeSize - 30).center(cx, cy); this.createMask(this.innerBorder, [this.innerBorderMask]); this.centerGroup = this.svgShape.group(); - this.offLabelShape = this.createOffLabel('800').addTo(this.centerGroup); + this.drawOffCenter(this.centerGroup); this.onCenterGroup = this.svgShape.group(); this.onCircleShape = this.svgShape.circle(powerButtonShapeSize - 30).center(cx, cy) .addTo(this.onCenterGroup); - this.onLabelShape = this.createOnLabel('800'); - this.createMask(this.onCircleShape, [this.onLabelShape]); + this.drawOnCenter(); + this.addOnCenterToMask(this.onCircleShape); this.pressedShadow = new InnerShadowCircle(this.svgShape, powerButtonShapeSize - 30, cx, cy, 0, 0); this.backgroundShape.addClass('tb-small-shadow'); this.pressedTimeline = new Timeline(); this.centerGroup.timeline(this.pressedTimeline); this.onCenterGroup.timeline(this.pressedTimeline); - this.onLabelShape.timeline(this.pressedTimeline); + this.addOnCenterTimeLine(this.pressedTimeline); this.pressedShadow.timeline(this.pressedTimeline); } @@ -919,7 +1058,7 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { this.outerBorder.attr({ 'fill-opacity': 1 }); } this.innerBorder.attr({fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); - this.offLabelShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.drawOffCenterColor(mainColor); this.onCircleShape.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); } @@ -940,7 +1079,7 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { const pressedScale = 0.75; powerButtonAnimation(this.centerGroup).transform({scale: pressedScale}); powerButtonAnimation(this.onCenterGroup).transform({scale: 0.98}); - powerButtonAnimation(this.onLabelShape).transform({scale: pressedScale / 0.98, origin: {x: cx, y: cy}}); + this.onCenterAnimation(pressedScale / 0.98); this.pressedShadow.animate(6, 0.6); } @@ -948,8 +1087,44 @@ class OutlinedVolumePowerButtonShape extends PowerButtonShape { this.pressedTimeline.finish(); powerButtonAnimation(this.centerGroup).transform({scale: 1}); powerButtonAnimation(this.onCenterGroup).transform({scale: 1}); - powerButtonAnimation(this.onLabelShape).transform({scale: 1, origin: {x: cx, y: cy}}); + this.onCenterAnimation(1); this.pressedShadow.animateRestore(); } } + +class OutlinedIconPowerButtonShape extends OutlinedVolumePowerButtonShape { + private offPowerSymbolCircle: Path; + private offPowerSymbolLine: Path; + private onPowerSymbolCircle: Path; + private onPowerSymbolLine: Path; + + protected drawOffCenter(centerGroup: G) { + this.offPowerSymbolCircle = this.svgShape.path(powerCircleStroke).center(cx, cy).addTo(centerGroup); + this.offPowerSymbolLine = this.svgShape.path(powerLineStroke).center(cx, cy-12).addTo(centerGroup); + } + + protected drawOnCenter() { + this.onPowerSymbolCircle = this.svgShape.path(powerCircleStroke).center(cx, cy); + this.onPowerSymbolLine = this.svgShape.path(powerLineStroke).center(cx, cy-12); + } + + protected addOnCenterToMask(onCircleShape: Circle) { + this.createMask(onCircleShape, [this.onPowerSymbolCircle, this.onPowerSymbolLine]); + } + + protected addOnCenterTimeLine(pressedTimeline: Timeline) { + this.onPowerSymbolCircle.timeline(pressedTimeline); + this.onPowerSymbolLine.timeline(pressedTimeline); + } + + protected drawOffCenterColor(mainColor: PowerButtonColor) { + this.offPowerSymbolCircle.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + this.offPowerSymbolLine.attr({ fill: mainColor.hex, 'fill-opacity': mainColor.opacity}); + } + + protected onCenterAnimation(scale: number) { + powerButtonAnimation(this.onPowerSymbolCircle).transform({scale, origin: {x: cx, y: cy}}); + powerButtonAnimation(this.onPowerSymbolLine).transform({scale, origin: {x: cx, y: cy}}); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/unread-notification-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/unread-notification-widget-settings.component.html index 5f452f3508..f59c85b3ca 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/unread-notification-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/cards/unread-notification-widget-settings.component.html @@ -41,7 +41,9 @@
- {{ 'widgets.notification.counter' | translate }} +
+ {{ 'widgets.notification.counter' | translate }} +
@@ -49,7 +51,7 @@
, private fb: UntypedFormBuilder) { super(store); @@ -78,4 +80,8 @@ export class UnreadNotificationWidgetSettingsComponent extends WidgetSettingsCom } } + private _countPreviewFn(): string { + return this.unreadNotificationWidgetSettingsForm.get('maxNotificationDisplay').value?.toString() || '6'; + } + } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html index 8965b462b1..e7ca744685 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html @@ -79,7 +79,7 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.html index d79e84d98e..e56fc1ec00 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.html @@ -18,7 +18,7 @@
-
{{ title | translate }}
+
{{ titleText | translate }}
{{ chartFillTypeTranslationMap.get(type) | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.ts index 718c4cce02..bf02937221 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/chart-fill-settings.component.ts @@ -55,7 +55,7 @@ export class ChartFillSettingsComponent implements OnInit, ControlValueAccessor disabled: boolean; @Input() - title = 'widgets.chart.fill'; + titleText = 'widgets.chart.fill'; @Input() fillNoneTitle = 'widgets.chart.fill-type-none'; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html index 62cacf0730..173d2102d6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.html @@ -65,6 +65,14 @@
widgets.gauge.max-value-short
+ + warning +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts index 5a4289a8d2..5de1b77b18 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/gauge/digital-gauge-widget-settings.component.ts @@ -16,7 +16,14 @@ import { Datasource, WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; import { Component } from '@angular/core'; -import { AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { + AbstractControl, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormGroup, ValidationErrors, + ValidatorFn, + Validators +} from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { GaugeType } from '@home/components/widget/lib/canvas-digital-gauge'; @@ -167,7 +174,7 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent donutStartAngle: [settings.donutStartAngle, []], showMinMax: [settings.showMinMax, []], minValue: [settings.minValue, []], - maxValue: [settings.maxValue, []], + maxValue: [settings.maxValue, [this.maxValueValidation()]], minMaxFont: [settings.minMaxFont, []], minMaxColor: [settings.minMaxFont.color, []], @@ -206,6 +213,18 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent }); } + private maxValueValidation(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value: string = control.value; + if (value) { + if (value < control.parent?.get('minValue').value) { + return {maxValue: true}; + } + } + return null; + }; + } + protected prepareOutputSettings(settings) { const barColor: ColorSettings = this.digitalGaugeWidgetSettingsForm.get('barColor').value; @@ -230,10 +249,15 @@ export class DigitalGaugeWidgetSettingsComponent extends WidgetSettingsComponent } protected validatorTriggers(): string[] { - return ['gaugeType', 'showTitle', 'showUnitTitle', 'showValue', 'showMinMax', 'showTimestamp', 'showTicks', 'animation']; + return ['gaugeType', 'showTitle', 'showUnitTitle', 'showValue', 'showMinMax', 'showTimestamp', 'showTicks', 'animation', 'minValue']; } - protected updateValidators(emitEvent: boolean) { + protected updateValidators(emitEvent: boolean, trigger: string) { + if (trigger === 'minValue') { + this.digitalGaugeWidgetSettingsForm.get('maxValue').updateValueAndValidity({emitEvent: true}); + this.digitalGaugeWidgetSettingsForm.get('maxValue').markAsTouched({onlySelf: true}); + return; + } const gaugeType: GaugeType = this.digitalGaugeWidgetSettingsForm.get('gaugeType').value; const showTitle: boolean = this.digitalGaugeWidgetSettingsForm.get('showTitle').value; const showUnitTitle: boolean = this.digitalGaugeWidgetSettingsForm.get('showUnitTitle').value; diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index 1d28f139c0..51d82dece9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -90,9 +90,6 @@ import { ToggleButtonWidgetComponent } from '@home/components/widget/lib/button/ import { TimeSeriesChartWidgetComponent } from '@home/components/widget/lib/chart/time-series-chart-widget.component'; import { AddConnectorDialogComponent } from '@home/components/widget/lib/gateway/dialog/add-connector-dialog.component'; import { MappingDialogComponent } from '@home/components/widget/lib/gateway/dialog/mapping-dialog.component'; -import { - EllipsisChipListDirective -} from '@home/components/widget/lib/gateway/connectors-configuration/ellipsis-chip-list.directive'; import { StatusWidgetComponent } from '@home/components/widget/lib/indicator/status-widget.component'; import { LatestChartComponent } from '@home/components/widget/lib/chart/latest-chart.component'; import { PieChartWidgetComponent } from '@home/components/widget/lib/chart/pie-chart-widget.component'; @@ -112,18 +109,38 @@ import { import { NotificationTypeFilterPanelComponent } from '@home/components/widget/lib/cards/notification-type-filter-panel.component'; -import { GatewayHelpLinkPipe } from '@home/pipes/public-api'; +import { GatewayHelpLinkPipe } from '@home/components/widget/lib/gateway/pipes/gateway-help-link.pipe'; +import { EllipsisChipListDirective } from '@shared/directives/ellipsis-chip-list.directive'; import { - BrokerConfigControlComponent, - DeviceInfoTableComponent, - MappingDataKeysPanelComponent, - MappingTableComponent, - MqttBasicConfigComponent, - OpcUaBasicConfigComponent, - ServerConfigComponent, - TypeValuePanelComponent, - WorkersConfigControlComponent, -} from '@home/components/widget/lib/gateway/connectors-configuration/public-api'; + BrokerConfigControlComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/broker-config-control/broker-config-control.component'; +import { + WorkersConfigControlComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/workers-config-control/workers-config-control.component'; +import { + OpcServerConfigComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/opc-server-config/opc-server-config.component'; +import { + MqttBasicConfigComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/mqtt-basic-config/mqtt-basic-config.component'; +import { + MappingTableComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-table/mapping-table.component'; +import { + OpcUaBasicConfigComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/opc-ua-basic-config/opc-ua-basic-config.component'; +import { + ModbusBasicConfigComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/modbus/modbus-basic-config/modbus-basic-config.component'; +import { + DeviceInfoTableComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/device-info-table/device-info-table.component'; +import { + MappingDataKeysPanelComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/mapping-data-keys-panel/mapping-data-keys-panel.component'; +import { + TypeValuePanelComponent +} from '@home/components/widget/lib/gateway/connectors-configuration/type-value-panel/type-value-panel.component'; @NgModule({ declarations: [ @@ -163,7 +180,6 @@ import { GatewayConfigurationComponent, GatewayRemoteConfigurationDialogComponent, GatewayServiceRPCConnectorTemplateDialogComponent, - EllipsisChipListDirective, ValueCardWidgetComponent, AggregatedValueCardWidgetComponent, CountWidgetComponent, @@ -192,7 +208,8 @@ import { LabelCardWidgetComponent, LabelValueCardWidgetComponent, UnreadNotificationWidgetComponent, - NotificationTypeFilterPanelComponent], + NotificationTypeFilterPanelComponent + ], imports: [ CommonModule, SharedModule, @@ -203,11 +220,13 @@ import { GatewayHelpLinkPipe, BrokerConfigControlComponent, WorkersConfigControlComponent, - ServerConfigComponent, + OpcServerConfigComponent, MqttBasicConfigComponent, MappingTableComponent, OpcUaBasicConfigComponent, - KeyValueIsNotEmptyPipe + KeyValueIsNotEmptyPipe, + ModbusBasicConfigComponent, + EllipsisChipListDirective, ], exports: [ EntitiesTableWidgetComponent, diff --git a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts index a79fa89242..bae0389e61 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget.component.ts @@ -487,6 +487,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI } if (!this.widgetContext.inited && this.isReady()) { this.widgetContext.inited = true; + this.widgetContext.destroyed = false; this.dashboardWidget.updateWidgetParams(); this.widgetContext.detectContainerChanges(); if (this.cafs.init) { diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index 581fded240..fbda78a128 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -449,6 +449,8 @@ export class WidgetContext { labelPattern.destroy(); } this.labelPatterns.clear(); + this.width = undefined; + this.height = undefined; this.destroyed = true; } diff --git a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html index 76625326b4..5c3d9da081 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html @@ -58,7 +58,7 @@ resource.title - + {{ 'resource.title-required' | translate }} @@ -68,7 +68,7 @@ impleme ngOnInit() { super.ngOnInit(); - this.entityForm.get('resourceType').valueChanges.pipe( - startWith(ResourceType.JS_MODULE), - filter(() => this.isAdd), - takeUntil(this.destroy$) - ).subscribe((type) => { - if (type === this.resourceType.LWM2M_MODEL) { - this.entityForm.get('title').disable({emitEvent: false}); - this.entityForm.patchValue({title: ''}, {emitEvent: false}); - } else { - this.entityForm.get('title').enable({emitEvent: false}); - } - this.entityForm.patchValue({ - data: null, - fileName: null - }, {emitEvent: false}); - }); + if (this.isAdd) { + this.observeResourceTypeChange(); + } } ngOnDestroy() { @@ -96,7 +83,7 @@ export class ResourcesLibraryComponent extends EntityComponent impleme title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], resourceType: [entity?.resourceType ? entity.resourceType : ResourceType.JS_MODULE, Validators.required], fileName: [entity ? entity.fileName : null, Validators.required], - data: [entity ? entity.data : null, Validators.required] + data: [entity ? entity.data : null, this.isAdd ? [Validators.required] : []] }); } @@ -105,7 +92,6 @@ export class ResourcesLibraryComponent extends EntityComponent impleme this.entityForm.get('resourceType').disable({emitEvent: false}); if (entity.resourceType !== ResourceType.JS_MODULE) { this.entityForm.get('fileName').disable({emitEvent: false}); - this.entityForm.get('data').disable({emitEvent: false}); } } this.entityForm.patchValue({ @@ -153,4 +139,24 @@ export class ResourcesLibraryComponent extends EntityComponent impleme horizontalPosition: 'right' })); } + + private observeResourceTypeChange(): void { + this.entityForm.get('resourceType').valueChanges.pipe( + startWith(ResourceType.JS_MODULE), + takeUntil(this.destroy$) + ).subscribe((type: ResourceType) => this.onResourceTypeChange(type)); + } + + private onResourceTypeChange(type: ResourceType): void { + if (type === this.resourceType.LWM2M_MODEL) { + this.entityForm.get('title').disable({emitEvent: false}); + this.entityForm.patchValue({title: ''}, {emitEvent: false}); + } else { + this.entityForm.get('title').enable({emitEvent: false}); + } + this.entityForm.patchValue({ + data: null, + fileName: null + }, {emitEvent: false}); + } } diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html index fc71dbedcb..aa928687ad 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.html @@ -35,8 +35,7 @@
+ [isAdd]="isTransportTypeChanged">
@@ -65,8 +64,7 @@
+ formControlName="configuration">
diff --git a/ui-ngx/src/app/modules/home/pipes/public-api.ts b/ui-ngx/src/app/modules/home/pipes/public-api.ts deleted file mode 100644 index 1cb3ce312a..0000000000 --- a/ui-ngx/src/app/modules/home/pipes/public-api.ts +++ /dev/null @@ -1,17 +0,0 @@ -/// -/// Copyright © 2016-2024 The Thingsboard Authors -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// - -export * from './gateway-help-link/gateway-help-link.pipe'; diff --git a/ui-ngx/src/app/shared/components/entity/entity-list.component.ts b/ui-ngx/src/app/shared/components/entity/entity-list.component.ts index 4e33944f29..a4d7c9bab0 100644 --- a/ui-ngx/src/app/shared/components/entity/entity-list.component.ts +++ b/ui-ngx/src/app/shared/components/entity/entity-list.component.ts @@ -25,11 +25,17 @@ import { SimpleChanges, ViewChild } from '@angular/core'; -import { ControlValueAccessor, UntypedFormBuilder, UntypedFormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + ValidationErrors, + Validators +} from '@angular/forms'; import { Observable } from 'rxjs'; import { filter, map, mergeMap, share, tap } from 'rxjs/operators'; -import { Store } from '@ngrx/store'; -import { AppState } from '@app/core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { EntityType } from '@shared/models/entity-type.models'; import { BaseData } from '@shared/models/base-data'; @@ -49,6 +55,11 @@ import { SubscriptSizing } from '@angular/material/form-field'; provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EntityListComponent), multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => EntityListComponent), + multi: true } ] }) @@ -56,7 +67,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV entityListFormGroup: UntypedFormGroup; - modelValue: Array | null; + private modelValue: Array | null; @Input() entityType: EntityType; @@ -108,17 +119,16 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV private propagateChange = (v: any) => { }; - constructor(private store: Store, - public translate: TranslateService, + constructor(public translate: TranslateService, private entityService: EntityService, private fb: UntypedFormBuilder) { this.entityListFormGroup = this.fb.group({ - entities: [this.entities, this.required ? [Validators.required] : []], + entities: [this.entities], entity: [null] }); } - updateValidators() { + private updateValidators() { this.entityListFormGroup.get('entities').setValidators(this.required ? [Validators.required] : []); this.entityListFormGroup.get('entities').updateValueAndValidity(); } @@ -189,7 +199,13 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV this.dirty = true; } - reset() { + validate(): ValidationErrors | null { + return this.entityListFormGroup.valid ? null : { + entities: {valid: false} + }; + } + + private reset() { this.entities = []; this.entityListFormGroup.get('entities').setValue(this.entities); this.modelValue = null; @@ -201,7 +217,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV this.dirty = true; } - add(entity: BaseData): void { + private add(entity: BaseData): void { if (!this.modelValue || this.modelValue.indexOf(entity.id.id) === -1) { if (!this.modelValue) { this.modelValue = []; @@ -214,7 +230,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV this.clear(); } - remove(entity: BaseData) { + public remove(entity: BaseData) { let index = this.entities.indexOf(entity); if (index >= 0) { this.entities.splice(index, 1); @@ -229,11 +245,11 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV } } - displayEntityFn(entity?: BaseData): string | undefined { + public displayEntityFn(entity?: BaseData): string | undefined { return entity ? entity.name : undefined; } - fetchEntities(searchText?: string): Observable>> { + private fetchEntities(searchText?: string): Observable>> { this.searchText = searchText; return this.entityService.getEntitiesByNameFilter(this.entityType, searchText, @@ -241,14 +257,14 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV map((data) => data ? data : [])); } - onFocus() { + public onFocus() { if (this.dirty) { this.entityListFormGroup.get('entity').updateValueAndValidity({onlySelf: true, emitEvent: true}); this.dirty = false; } } - clear(value: string = '') { + private clear(value: string = '') { this.entityInput.nativeElement.value = value; this.entityListFormGroup.get('entity').patchValue(value, {emitEvent: true}); setTimeout(() => { @@ -257,8 +273,7 @@ export class EntityListComponent implements ControlValueAccessor, OnInit, AfterV }, 0); } - textIsNotEmpty(text: string): boolean { + public textIsNotEmpty(text: string): boolean { return (text && text.length > 0); } - } diff --git a/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.html b/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.html index 32b66fd2b7..f2413f0ad7 100644 --- a/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.html +++ b/ui-ngx/src/app/shared/components/hint-tooltip-icon.component.html @@ -15,7 +15,7 @@ limitations under the License. --> - + implements DataSource { + + protected dataSubject = new BehaviorSubject>([]); + + connect(): Observable> { + return this.dataSubject.asObservable(); + } + + disconnect(): void { + this.dataSubject.complete(); + } + + loadData(data: Array): void { + this.dataSubject.next(data); + } + + isEmpty(): Observable { + return this.dataSubject.pipe( + map((data: T[]) => !data.length) + ); + } + + total(): Observable { + return this.dataSubject.pipe( + map((data: T[]) => data.length) + ); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/ellipsis-chip-list.directive.ts b/ui-ngx/src/app/shared/directives/ellipsis-chip-list.directive.ts similarity index 90% rename from ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/ellipsis-chip-list.directive.ts rename to ui-ngx/src/app/shared/directives/ellipsis-chip-list.directive.ts index aa4738d707..c320a2f78a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/ellipsis-chip-list.directive.ts +++ b/ui-ngx/src/app/shared/directives/ellipsis-chip-list.directive.ts @@ -20,7 +20,7 @@ import { Inject, Input, OnDestroy, - Renderer2 + Renderer2, } from '@angular/core'; import { isEqual } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; @@ -30,13 +30,15 @@ import { takeUntil } from 'rxjs/operators'; @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector - selector: '[tb-ellipsis-chip-list]' + selector: '[tb-ellipsis-chip-list]', + standalone: true, }) export class EllipsisChipListDirective implements OnDestroy { chipsValue: string[]; private destroy$ = new Subject(); + private intersectionObserver: IntersectionObserver; @Input('tb-ellipsis-chip-list') set chips(value: string[]) { @@ -59,6 +61,19 @@ export class EllipsisChipListDirective implements OnDestroy { ).subscribe(() => { this.adjustChips(); }); + this.observeIntersection(); + } + + private observeIntersection(): void { + this.intersectionObserver = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (entry.isIntersecting) { + this.adjustChips(); + } + }); + }); + + this.intersectionObserver.observe(this.el.nativeElement); } private adjustChips(): void { @@ -124,5 +139,6 @@ export class EllipsisChipListDirective implements OnDestroy { ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); + this.intersectionObserver.disconnect(); } } diff --git a/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts b/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts new file mode 100644 index 0000000000..1281b86040 --- /dev/null +++ b/ui-ngx/src/app/shared/directives/truncate-with-tooltip.directive.ts @@ -0,0 +1,116 @@ +/// +/// Copyright © 2016-2024 The Thingsboard Authors +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// + +import { + AfterViewInit, + Directive, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, +} from '@angular/core'; +import { fromEvent, Subject } from 'rxjs'; +import { filter, takeUntil, tap } from 'rxjs/operators'; +import { MatTooltip, TooltipPosition } from '@angular/material/tooltip'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Directive({ + standalone: true, + selector: '[tbTruncateWithTooltip]', + providers: [MatTooltip], +}) +export class TruncateWithTooltipDirective implements OnInit, AfterViewInit, OnDestroy { + + @Input('tbTruncateWithTooltip') + text: string; + + @Input() + @coerceBoolean() + tooltipEnabled = true; + + @Input() + position: TooltipPosition = 'above'; + + private destroy$ = new Subject(); + + constructor( + private elementRef: ElementRef, + private renderer: Renderer2, + private tooltip: MatTooltip + ) {} + + ngOnInit(): void { + this.observeMouseEvents(); + this.applyTruncationStyles(); + } + + ngAfterViewInit(): void { + if (!this.text) { + this.text = this.elementRef.nativeElement.innerText; + } + + this.tooltip.position = this.position; + } + + ngOnDestroy(): void { + if (this.tooltip._isTooltipVisible()) { + this.hideTooltip(); + } + this.destroy$.next(); + this.destroy$.complete(); + } + + private observeMouseEvents(): void { + fromEvent(this.elementRef.nativeElement, 'mouseenter') + .pipe( + filter(() => this.tooltipEnabled), + filter(() => this.isOverflown(this.elementRef.nativeElement)), + tap(() => this.showTooltip()), + takeUntil(this.destroy$), + ) + .subscribe(); + fromEvent(this.elementRef.nativeElement, 'mouseleave') + .pipe( + filter(() => this.tooltipEnabled), + filter(() => this.tooltip._isTooltipVisible()), + tap(() => this.hideTooltip()), + takeUntil(this.destroy$), + ) + .subscribe(); + } + + private applyTruncationStyles(): void { + this.renderer.setStyle(this.elementRef.nativeElement, 'white-space', 'nowrap'); + this.renderer.setStyle(this.elementRef.nativeElement, 'overflow', 'hidden'); + this.renderer.setStyle(this.elementRef.nativeElement, 'text-overflow', 'ellipsis'); + } + + private isOverflown(element: HTMLElement): boolean { + return element.clientWidth < element.scrollWidth; + } + + private showTooltip(): void { + this.tooltip.message = this.text; + + this.renderer.setAttribute(this.elementRef.nativeElement, 'matTooltip', this.text); + this.tooltip.show(); + } + + private hideTooltip(): void { + this.tooltip.hide(); + } +} diff --git a/ui-ngx/src/app/shared/pipe/key-value-not-empty.pipe.ts b/ui-ngx/src/app/shared/pipe/key-value-not-empty.pipe.ts index 94eeced50e..08afba9d81 100644 --- a/ui-ngx/src/app/shared/pipe/key-value-not-empty.pipe.ts +++ b/ui-ngx/src/app/shared/pipe/key-value-not-empty.pipe.ts @@ -62,6 +62,6 @@ export class KeyValueIsNotEmptyPipe implements PipeTransform { } private makeKeyValuePair(key: string, value: unknown): KeyValue { - return {key: key, value: value}; + return {key, value}; } } diff --git a/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md b/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md index 6779128c89..0542e592d3 100644 --- a/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md +++ b/ui-ngx/src/assets/help/en_US/rulenode/common_node_fields_templatization.md @@ -1,4 +1,4 @@ Fields templatization feature allows you to process the incoming messages with dynamic configuration by substitution of templates specified in the configuration fields with values from message or message metadata. -For more detailed information, please refer to the ThingsBoard [documentation](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/templatization/) +For more detailed information, please refer to the ThingsBoard [documentation{:target="_blank"}](${siteBaseUrl}/docs${docPlatformPrefix}/user-guide/templatization/) diff --git a/ui-ngx/src/assets/help/en_US/rulenode/node-templatization-doc.md b/ui-ngx/src/assets/help/en_US/rulenode/node-templatization-doc.md new file mode 100644 index 0000000000..9e21a0bcb5 --- /dev/null +++ b/ui-ngx/src/assets/help/en_US/rulenode/node-templatization-doc.md @@ -0,0 +1,83 @@ +# Rule nodes fields templatization + +Templatization is the process of using predefined templates to dynamically insert or substitute values into text. +These templates serve as placeholders for variables that can be filled in later with actual data. + +In the context of rule engine, templates are used to extract data from incoming messages during runtime. +This is particularly helpful in the rule node configuration, where templatization allows for dynamic configuration by replacing static values in the configuration fields with real-time values from the incoming messages. +This enables more flexible and automated handling of data, making it easier to perform conditional operations based on varying inputs. + +## Syntax + +Templates start with a dollar sign (`$`), followed by brackets with a key name inside. +Square brackets (`[]`) are used for message keys, while curly brackets (`{}`) are used for message metadata keys. +For example: +- `$[messageKey]` - will extract value of `messageKey` from incoming message. +- `${metadataKey}` - will extract value of `metadataKey` from incoming message metadata. + +In the example above, `messageKey` and `metadataKey` represent any key name that may exist within the message or its metadata. + +## Example + +Let's review an example. First JSON is message, second is message metadata: + +```json +{ + "temperature": 26.5, + "humidity": 75.2, + "soilMoisture": 28.9, + "windSpeed": 26.2, + "location": "riverside" +} +``` +```json +{ + "deviceType": "weather_sensor", + "deviceName": "weather1", + "ts": "1685379440000" +} +``` + +Assume, we detected an unusually high wind speed and want to send this telemetry reading to some external REST API. +Every reading needs to be associated with specific device and location - this information is available only in real-time. +We can use templates extract necessary data and to construct URL for sending data: + +`example-base-url.com/report-reading?location=$[location]&deviceName=${deviceName}` + +This template will be resolved to: + +`example-base-url.com/report-reading?location=riverside&deviceName=weather1` + +Templates are ideal for scenarios where the specific values aren't known at the time of configuration but will become available at runtime. + +## Notes + +- Templates can be combined with regular text. For example: "Fuel tanks are filled to `$[fuelLevel]`%". +- You can access nested keys in JSON object using dot notation: `$[object.key]`. +- If specified key is missing or value associated with that key is an object or an array, then template string will be returned unchanged. + +To illustrate written above let's review an example. Here's content of a message: +```json +{ + "number": 123.45, + "string": "text", + "boolean": true, + "array": [1, 2, 3], + "object": { + "property": "propertyValue" + }, + "null": null +} +``` +Here's a table with comparison between templates and extracted values: + +| **Template** | **Extracted value** | +|--------------------|---------------------| +| $[number] | 123.45 | +| $[string] | text | +| $[boolean] | true | +| $[array] | $[array] | +| $[object] | $[object] | +| $[object.property] | propertyValue | +| $[null] | null | +| $[doesNotExist] | $[doesNotExist] | diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 33c8d25652..14eb272910 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2755,19 +2755,27 @@ "function": "Function" }, "gateway": { + "address": "Address", + "address-required": "Address required", "add-entry": "Add configuration", "add-attribute": "Add attribute", "add-attribute-update": "Add attribute update", "add-key": "Add key", "add-timeseries": "Add time series", "add-mapping": "Add mapping", + "add-slave": "Add Slave", "arguments": "Arguments", "add-rpc-method": "Add method", + "add-rpc-request": "Add request", "add-value": "Add argument", + "baudrate": "Baudrate", + "bytesize": "Bytesize", "delete-value": "Delete value", "delete-rpc-method": "Delete method", - "delete-attribute-update": "Add attribute update", + "delete-rpc-request": "Delete request", + "delete-attribute-update": "Delete attribute update", "advanced": "Advanced", + "advanced-connection-settings": "Advanced connection settings", "attributes": "Attributes", "attribute-updates": "Attribute updates", "attribute-filter": "Attribute filter", @@ -2777,6 +2785,8 @@ "attribute-name-expression-required": "Attribute name expression required.", "attribute-name-expression-hint": "Hint for Attribute name expression", "basic": "Basic", + "byte-order": "Byte order", + "word-order": "Word order", "broker": { "connection": "Connection to broker", "name": "Broker name", @@ -2811,6 +2821,9 @@ "connectors-table-actions": "Actions", "connectors-table-key": "Key", "connectors-table-class": "Class", + "connection-timeout": "Connection timeout (s)", + "connect-attempt-time": "Connect attempt time (ms)", + "connect-attempt-count": "Connect attempt count", "copy-username": "Copy username", "copy-password": "Copy password", "copy-client-id": "Copy client ID", @@ -2839,7 +2852,8 @@ "device-name-filter-hint": "This field supports Regular expressions to filter incoming data by device name.", "device-name-filter-required": "Device name filter is required.", "details": "Details", - "delete-mapping-title": "Delete mapping ?", + "delete-mapping-title": "Delete mapping?", + "delete-slave-title": "Delete slave?", "download-configuration-file": "Download configuration file", "download-docker-compose": "Download docker-compose.yml for your gateway", "enable-remote-logging": "Enable remote logging", @@ -2858,6 +2872,7 @@ "configuration-delete-dialog-confirm": "Turn Off", "connector-duplicate-name": "Connector with such name already exists.", "connector-side": "Connector side", + "client-communication-type": "Client communication type", "payload-type": "Payload type", "platform-side": "Platform side", "JSON": "JSON", @@ -2883,8 +2898,11 @@ "device-node-hint": "Path or identifier for device node on OPC UA server. Relative paths from it for attributes and time series can be used.", "device-name": "Device name", "device-profile": "Device profile", + "device-name-required": "Device name required", + "device-profile-required": "Device profile required", "download-tip": "Download configuration file", "drop-file": "Drop file here or", + "enable": "Enable", "enable-subscription": "Enable subscription", "extension": "Extension", "extension-hint": "Put your converter classname in the field. Custom converter with such class should be in extension/mqtt folder.", @@ -2895,6 +2913,7 @@ "fill-connector-defaults-hint": "This property allows to fill connector configuration with default values on it's creation.", "from-device-request-settings": "Input request parsing", "from-device-request-settings-hint": "These fields support JSONPath expressions to extract a name from incoming message.", + "function-code": "Function code", "to-device-response-settings": "Output request processing", "to-device-response-settings-hint": "For these fields you can use the following variables and they will be replaced with actual values: ${deviceName}, ${attributeKey}, ${attributeValue}", "gateway": "Gateway", @@ -2933,8 +2952,13 @@ "inactivity-timeout-seconds-required": "Inactivity timeout is required", "inactivity-timeout-seconds-min": "Inactivity timeout can not be less then 1", "inactivity-timeout-seconds-pattern": "Inactivity timeout is not valid", + "unit-id": "Unit ID", "host": "Host", "host-required": "Host is required.", + "holding_registers": "Holding registers", + "coils_initializer": "Coils initializer", + "input_registers": "Input registers", + "discrete_inputs": "Discrete inputs", "json-parse": "Not valid JSON.", "json-required": "Field cannot be empty.", "JSONPath-hint": "This field supports constants and JSONPath expressions.", @@ -2968,12 +2992,14 @@ "max-messages-queue-for-worker": "Max messages queue per worker", "max-messages-queue-for-worker-hint": "Maximal messages count that will be in the queue \nfor each converter worker.", "max-messages-queue-for-worker-required": "Max messages queue per worker is required.", + "method": "Method", "method-name": "Method name", "method-required": "Method name is required.", "min-pack-send-delay": "Min pack send delay (in ms)", "min-pack-send-delay-required": "Min pack send delay is required", "min-pack-send-delay-min": "Min pack send delay can not be less then 0", "mode": "Mode", + "model-name": "Model name", "mqtt-version": "MQTT version", "name": "Name", "name-required": "Name is required.", @@ -2987,17 +3013,22 @@ "no-keys": "No keys", "no-value": "No arguments", "no-rpc-methods": "No RPC methods", + "no-rpc-requests": "No RPC requests", "path-hint": "The path is local to the gateway file system", "path-logs": "Path to log files", "path-logs-required": "Path is required.", "password": "Password", "password-required": "Password is required.", "permit-without-calls": "Keep alive permit without calls", + "poll-period": "Poll period (ms)", "port": "Port", "port-required": "Port is required.", "port-limits-error": "Port should be number from {{min}} to {{max}}.", "private-key-path": "Path to private key file", "path-to-private-key-required": "Path to private key file is required.", + "parity": "Parity", + "product-code": "Product code", + "product-name": "Product name", "raw": "Raw", "retain": "Retain", "retain-hint": "This flag tells the broker to store the message for a topic\nand ensures any new client subscribing to that topic\nwill receive the stored message.", @@ -3006,6 +3037,9 @@ "remove-entry": "Remove configuration", "remote-shell": "Remote shell", "remote-configuration": "Remote Configuration", + "retries": "Retries", + "retries-on-empty": "Retries on empty", + "retries-on-invalid": "Retries on invalid", "rpc": { "title": "{{type}} Connector RPC parameters", "templates-title": "Connector RPC Templates", @@ -3097,6 +3131,7 @@ "json-value-invalid": "JSON value has an invalid format" }, "rpc-methods": "RPC methods", + "rpc-requests": "RPC requests", "request" : { "connect-request": "Connect request", "disconnect-request": "Disconnect request", @@ -3108,6 +3143,7 @@ "requests-mapping": "Requests mapping", "requests-mapping-hint": "MQTT Connector requests allows you to connect, disconnect, process attribute requests from the device, handle attribute updates on the server and RPC processing configuration.", "request-topic-expression": "Request topic expression", + "request-client-certificate": "Request client certificate", "request-topic-expression-required": "Request topic expression is required.", "response-timeout": "Response timeout (ms)", "response-timeout-required": "Response timeout is required.", @@ -3118,7 +3154,10 @@ "response-topic-expression-required": "Response topic expression is required.", "response-value-expression": "Response value expression", "response-value-expression-required": "Response value expression is required.", + "vendor-name": "Vendor name", + "vendor-url": "Vendor URL", "value": "Value", + "values": "Values", "value-required": "Value is required.", "value-expression": "Value expression", "value-expression-required": "Value expression is required.", @@ -3141,17 +3180,28 @@ }, "select-connector": "Select connector to display config", "send-change-data": "Send data only on change", + "send-data-TB": "Send data to ThingsBoard", + "send-data-on-change": "Send data only on change", "send-change-data-hint": "The values will be saved to the database only if they are different from the corresponding values in the previous converted message. This functionality applies to both attributes and time series in the converter output.", "server": "Server", + "server-hostname": "Server hostname", + "server-slave": "Server (Slave)", + "servers-slaves": "Servers (Slaves)", "server-port": "Server port", "server-url": "Server endpoint url", + "server-connection": "Server Connection", + "server-config": "Server configuration", + "server-slave-config": "Server (Slave) configuration", "server-url-required": "Server endpoint url is required.", + "stopbits": "Stopbits", + "strict": "Strict", "set": "Set", "show-map": "Show map", "statistics": { "statistic": "Statistic", "statistics": "Statistics", "statistic-commands-empty": "No configured statistic keys found. You can configure them in \"Statistics\" tab in general configuration.", + "statistics-button": "Go to configuration", "commands": "Commands", "send-period": "Statistic send period (in sec)", "send-period-required": "Statistic send period is required", @@ -3237,6 +3287,8 @@ "topic-required": "Topic filter is required.", "tls-path-ca-certificate": "Path to CA certificate on gateway", "tls-path-client-certificate": "Path to client certificate on gateway", + "tls-connection": "TLS Connection", + "master-connections": "Master Connections", "method-filter": "Method filter", "method-filter-hint": "Regular expression to filter incoming RPC method from platform.", "method-filter-required": "Method filter is required.", @@ -3256,13 +3308,25 @@ "at-least-once": "1 - At least once", "exactly-once": "2 - Exactly once" }, + "objects-count": "Objects count", + "wait-after-failed-attempts": "Wait after failed attempts (ms)", "tls-path-private-key": "Path to private key on gateway", "toggle-fullscreen": "Toggle fullscreen", "transformer-json-config": "Configuration JSON*", "update-config": "Add/update configuration JSON", "username": "Username", "username-required": "Username is required.", + "unit-id-required": "Unit ID is required.", + "read-coils": "Read Coils", + "read-discrete-inputs": "Read Discrete Inputs", + "read-multiple-holding-registers": "Read Multiple Holding Register", + "read-input-registers": "Read Input Registers", + "write-coil": "Write Coil", + "write-coils": "Write Coils", + "write-register": "Write Register", + "write-registers": "Write Registers", "hints": { + "modbus-server": "Starting with version 3.0, Gateway can run as a Modbus slave.", "remote-configuration": "Enables remote configuration and management of the gateway", "remote-shell": "Enables remote control of the operating system with the gateway from the Remote Shell widget", "host": "Hostname or IP address of platform server", @@ -3301,7 +3365,9 @@ "grpc-max-pings-without-data": "Maximum number of keepalive ping messages that the server can send without receiving any data before it considers the connection dead.", "grpc-min-ping-interval-without-data": "Minimum amount of time the server should wait between sending keepalive ping messages when there is no data being sent or received.", "permit-without-calls": "Allow server to keep the GRPC connection alive even when there are no active RPC calls.", + "path-in-os": "Path in gateway os.", "memory": "Your data will be stored in the in-memory queue, it is a fastest but no persistence guarantee.", + "framer-type": "Type of framer.", "file": "Your data will be stored in separated files and will be saved even after the gateway restart.", "sqlite": "Your data will be stored in file based database. And will be saved even after the gateway restart.", "opcua-timeout": "Timeout in seconds for connecting to OPC-UA server.", @@ -5663,6 +5729,9 @@ "layout-default-volume": "Default.Volume", "layout-simplified-volume": "Simplified.Volume", "layout-outlined-volume": "Outlined.Volume", + "layout-default-icon": "Default.Icon", + "layout-simplified-icon": "Simplified.Icon", + "layout-outlined-icon": "Outlined.Icon", "main": "Main", "background": "Background", "power-on-colors": "Power 'On' colors", @@ -6279,7 +6348,8 @@ "min-and-max-value": "Min and max value", "min-and-max-label": "Min and max label", "font": "Font", - "tick-width-and-color": "Tick width and color" + "tick-width-and-color": "Tick width and color", + "min-max-validation-text": "Max value must be bigger than min value" }, "gpio": { "pin": "Pin", @@ -7046,6 +7116,7 @@ "notification": { "max-notification-display": "Maximum notifications to display", "counter": "Counter", + "counter-hint": "Counter will be displayed if \"Widget title\" is enabled", "icon": "Icon", "counter-value": "Value", "counter-color": "Color", diff --git a/ui-ngx/src/assets/metadata/connector-default-configs/modbus.json b/ui-ngx/src/assets/metadata/connector-default-configs/modbus.json index 437a6abd95..441f63ab5f 100644 --- a/ui-ngx/src/assets/metadata/connector-default-configs/modbus.json +++ b/ui-ngx/src/assets/metadata/connector-default-configs/modbus.json @@ -2,6 +2,7 @@ "master": { "slaves": [ { + "name": "Slave 1", "host": "127.0.0.1", "port": 5021, "type": "tcp", @@ -15,6 +16,7 @@ "pollPeriod": 5000, "unitId": 1, "deviceName": "Temp Sensor", + "deviceType": "default", "sendDataOnlyOnChange": true, "connectAttemptTimeMs": 5000, "connectAttemptCount": 5, @@ -185,64 +187,60 @@ "wordOrder": "LITTLE", "unitId": 0, "values": { - "holding_registers": [ - { - "attributes": [ - { - "address": 1, - "type": "string", - "tag": "sm", - "objectsCount": 1, - "value": "ON" - } - ], - "timeseries": [ - { - "address": 2, - "type": "int", - "tag": "smm", - "objectsCount": 1, - "value": "12334" - } - ], - "attributeUpdates": [ - { - "tag": "shared_attribute_write", - "type": "32int", - "functionCode": 6, - "objectsCount": 2, - "address": 29, - "value": 1243 - } - ], - "rpc": [ - { - "tag": "setValue", - "type": "bits", - "functionCode": 5, - "objectsCount": 1, - "address": 31, - "value": 22 - } - ] + "holding_registers": { + "attributes": [ + { + "address": 1, + "type": "string", + "tag": "sm", + "objectsCount": 1, + "value": "ON" + } + ], + "timeseries": [ + { + "address": 2, + "type": "8int", + "tag": "smm", + "objectsCount": 1, + "value": "12334" + } + ], + "attributeUpdates": [ + { + "tag": "shared_attribute_write", + "type": "32int", + "functionCode": 6, + "objectsCount": 2, + "address": 29, + "value": 1243 + } + ], + "rpc": [ + { + "tag": "setValue", + "type": "bits", + "functionCode": 5, + "objectsCount": 1, + "address": 31, + "value": 22 + } + ] + }, + "coils_initializer": { + "attributes": [ + { + "address": 5, + "type": "string", + "tag": "sm", + "objectsCount": 1, + "value": "12" + } + ], + "timeseries": [], + "attributeUpdates": [], + "rpc": [] } - ], - "coils_initializer": [ - { - "attributes": [ - { - "address": 5, - "type": "string", - "tag": "sm", - "objectsCount": 1, - "value": "12" - } - ], - "timeseries": [], - "attributeUpdates": [], - "rpc": [] - } - ] } } } diff --git a/ui-ngx/src/assets/widget/power-button/default-icon-layout.svg b/ui-ngx/src/assets/widget/power-button/default-icon-layout.svg new file mode 100644 index 0000000000..9da9ba8535 --- /dev/null +++ b/ui-ngx/src/assets/widget/power-button/default-icon-layout.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/power-button/outlined-icon-layout.svg b/ui-ngx/src/assets/widget/power-button/outlined-icon-layout.svg new file mode 100644 index 0000000000..b884cd06b0 --- /dev/null +++ b/ui-ngx/src/assets/widget/power-button/outlined-icon-layout.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-ngx/src/assets/widget/power-button/simplified-icon-layout.svg b/ui-ngx/src/assets/widget/power-button/simplified-icon-layout.svg new file mode 100644 index 0000000000..be3596da29 --- /dev/null +++ b/ui-ngx/src/assets/widget/power-button/simplified-icon-layout.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +