Browse Source

Revert "Develop/3.0 (#2760)" (#2762)

This reverts commit 72c7002f61.
pull/2785/head
Igor Kulikov 6 years ago
committed by GitHub
parent
commit
cdec671b3c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      application/pom.xml
  2. 39
      application/src/main/data/json/demo/dashboards/gateways.json
  3. 62
      application/src/main/data/json/demo/dashboards/thermostats.json
  4. 4
      application/src/main/data/json/system/widget_bundles/alarm_widgets.json
  5. 24
      application/src/main/data/json/system/widget_bundles/cards.json
  6. 2
      application/src/main/data/json/system/widget_bundles/charts.json
  7. 34
      application/src/main/data/json/system/widget_bundles/control_widgets.json
  8. 4
      application/src/main/data/json/system/widget_bundles/date.json
  9. 14
      application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json
  10. 58
      application/src/main/data/json/system/widget_bundles/gateway_widgets.json
  11. 48
      application/src/main/data/json/system/widget_bundles/gpio_widgets.json
  12. 158
      application/src/main/data/json/system/widget_bundles/input_widgets.json
  13. 40
      application/src/main/data/json/system/widget_bundles/maps.json
  14. 73
      application/src/main/data/json/system/widget_bundles/maps__deprecated_.json
  15. 4
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  16. 4
      application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
  17. 2
      application/src/main/java/org/thingsboard/server/config/WebConfig.java
  18. 21
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  19. 95
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  20. 58
      application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
  21. 72
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  22. 17
      application/src/main/java/org/thingsboard/server/controller/CustomerController.java
  23. 49
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  24. 94
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  25. 99
      application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
  26. 34
      application/src/main/java/org/thingsboard/server/controller/EventController.java
  27. 18
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  28. 17
      application/src/main/java/org/thingsboard/server/controller/TenantController.java
  29. 30
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  30. 17
      application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java
  31. 127
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  32. 8
      application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java
  33. 314
      application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
  34. 28
      application/src/main/java/org/thingsboard/server/service/install/CassandraEntityDatabaseSchemaService.java
  35. 2
      application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java
  36. 4
      application/src/main/java/org/thingsboard/server/service/install/DatabaseSchemaService.java
  37. 18
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  38. 13
      application/src/main/java/org/thingsboard/server/service/install/SqlAbstractDatabaseSchemaService.java
  39. 2
      application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java
  40. 102
      application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java
  41. 329
      application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraEntitiesToSqlMigrateService.java
  42. 172
      application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlColumn.java
  43. 64
      application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlColumnData.java
  44. 28
      application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlColumnType.java
  45. 40
      application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlEventTsColumn.java
  46. 304
      application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlTable.java
  47. 6
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  48. 12
      application/src/main/java/org/thingsboard/server/service/install/update/PaginatedUpdater.java
  49. 7
      application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java
  50. 4
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java
  51. 12
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  52. 7
      application/src/main/resources/thingsboard.yml
  53. 39
      application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
  54. 6
      application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java
  55. 130
      application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
  56. 22
      application/src/test/java/org/thingsboard/server/controller/BaseAuditLogControllerTest.java
  57. 34
      application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
  58. 49
      application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java
  59. 116
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
  60. 87
      application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
  61. 40
      application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java
  62. 68
      application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
  63. 26
      application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java
  64. 47
      application/src/test/java/org/thingsboard/server/controller/ControllerNoSqlTestSuite.java
  65. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/AdminControllerNoSqlTest.java
  66. 27
      application/src/test/java/org/thingsboard/server/controller/nosql/AssetControllerNoSqlTest.java
  67. 23
      application/src/test/java/org/thingsboard/server/controller/nosql/AuditLogControllerNoSqlTest.java
  68. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/AuthControllerNoSqlTest.java
  69. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/ComponentDescriptorControllerNoSqlTest.java
  70. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/CustomerControllerNoSqlTest.java
  71. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/DashboardControllerNoSqlTest.java
  72. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/DeviceControllerNoSqlTest.java
  73. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java
  74. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/TenantControllerNoSqlTest.java
  75. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/UserControllerNoSqlTest.java
  76. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/WidgetTypeControllerNoSqlTest.java
  77. 26
      application/src/test/java/org/thingsboard/server/controller/nosql/WidgetsBundleControllerNoSqlTest.java
  78. 11
      application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java
  79. 6
      application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java
  80. 50
      application/src/test/java/org/thingsboard/server/rules/RuleEngineNoSqlTestSuite.java
  81. 7
      application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java
  82. 16
      application/src/test/java/org/thingsboard/server/rules/flow/nosql/RuleEngineFlowNoSqlIntegrationTest.java
  83. 5
      application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java
  84. 25
      application/src/test/java/org/thingsboard/server/rules/lifecycle/nosql/RuleEngineLifecycleNoSqlIntegrationTest.java
  85. 4
      application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java
  86. 6
      application/src/test/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngineTest.java
  87. 48
      application/src/test/java/org/thingsboard/server/system/SystemNoSqlTestSuite.java
  88. 24
      application/src/test/java/org/thingsboard/server/system/nosql/DeviceApiNoSqlTest.java
  89. 15
      common/dao-api/pom.xml
  90. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  91. 23
      common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
  92. 10
      common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java
  93. 157
      common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java
  94. 232
      common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraDriverOptions.java
  95. 73
      common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraQueryOptions.java
  96. 76
      common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraSocketOptions.java
  97. 84
      common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/GuavaDriverContext.java
  98. 79
      common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/GuavaRequestAsyncProcessor.java
  99. 51
      common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/GuavaSession.java
  100. 59
      common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/GuavaSessionBuilder.java

14
application/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.0.0-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>application</artifactId>
@ -111,16 +111,12 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.thingsboard</groupId>
<artifactId>ui-ngx</artifactId>
<artifactId>ui</artifactId>
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
@ -245,12 +241,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>

39
application/src/main/data/json/demo/dashboards/gateways.json

@ -128,14 +128,13 @@
"actions": {
"headerButton": [
{
"id": "70837a9d-c3de-a9a7-03c5-dccd14998758",
"name": "Add device",
"icon": "add",
"type": "customPretty",
"customHtml": "<form #addDeviceForm=\"ngForm\" [formGroup]=\"addDeviceFormGroup\"\n (ngSubmit)=\"save()\" style=\"width: 480px;\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Add device</h2>\n <span fxFlex></span>\n <button mat-button mat-icon-button\n (click)=\"cancel()\"\n type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content>\n <div class=\"mat-padding\" fxLayout=\"column\">\n <mat-form-field class=\"mat-block\">\n <mat-label>Device name</mat-label>\n <input matInput formControlName=\"deviceName\" required>\n <mat-error *ngIf=\"addDeviceFormGroup.get('deviceName').hasError('required')\">\n Device name is required.\n </mat-error>\n </mat-form-field>\n <div formGroupName=\"attributes\" fxFlex fxLayout=\"row\" fxLayoutGap=\"8px\">\n <mat-form-field fxFlex=\"50\" class=\"mat-block\">\n <mat-label>Latitude</mat-label>\n <input type=\"number\" step=\"any\" matInput formControlName=\"latitude\">\n </mat-form-field>\n <mat-form-field fxFlex=\"50\" class=\"mat-block\">\n <mat-label>Longitude</mat-label>\n <input type=\"number\" step=\"any\" matInput formControlName=\"longitude\">\n </mat-form-field>\n </div>\n <mat-form-field class=\"mat-block\">\n <mat-label>Label</mat-label>\n <input matInput formControlName=\"deviceLabel\">\n </mat-form-field>\n </div> \n </div>\n <div mat-dialog-actions fxLayout=\"row\">\n <span fxFlex></span>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || addDeviceForm.invalid || !addDeviceForm.dirty\">\n Create\n </button>\n <button mat-button color=\"primary\"\n style=\"margin-right: 20px;\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>\n",
"customHtml": "<md-dialog aria-label=\"Add entity\" style=\"width: 480px\">\n <form name=\"addDeviceForm\" ng-submit=\"vm.save()\">\n <md-toolbar>\n <div class=\"md-toolbar-tools\">\n <h2>Add device</h2>\n <span flex></span>\n <md-button class=\"md-icon-button\" ng-click=\"vm.cancel()\">\n <ng-md-icon icon=\"close\" aria-label=\"Close\"></ng-md-icon>\n </md-button>\n </div>\n </md-toolbar>\n <md-progress-linear class=\"md-warn\" md-mode=\"indeterminate\" ng-disabled=\"!$root.loading && !vm.loading\" ng-show=\"$root.loading || vm.loading\"></md-progress-linear>\n <span style=\"min-height: 5px;\" flex=\"\" ng-show=\"!$root.loading && !vm.loading\"></span>\n <md-dialog-content>\n <div class=\"md-dialog-content\">\n <fieldset ng-disabled=\"$root.loading || vm.loading\">\n <md-input-container flex class=\"md-block\">\n <label>Device name</label>\n <input ng-model=\"vm.deviceName\" name=deviceName required>\n <div ng-messages=\"addDeviceForm.deviceName.$error\">\n <div ng-message=\"required\">Device name is required.</div>\n </div>\n </md-input-container>\n <div flex layout=\"row\">\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Latitude</label>\n <input type=\"number\" step=\"any\" name=\"latitude\" ng-model=\"vm.attributes.latitude\">\n </md-input-container>\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Longitude</label>\n <input type=\"number\" step=\"any\" name=\"longitude\" ng-model=\"vm.attributes.longitude\">\n </md-input-container>\n </div>\n <md-input-container class=\"md-block\">\n <label>Label</label>\n <input name=\"deviceLabel\" ng-model=\"vm.deviceLabel\">\n </md-input-container>\n </fieldset>\n </div>\n </md-dialog-content>\n <md-dialog-actions>\n <md-button type=\"submit\" ng-disabled=\"vm.loading || addDeviceForm.$invalid || !addDeviceForm.$dirty\" class=\"md-raised md-primary\">Create</md-button>\n <md-button ng-click=\"vm.cancel()\" class=\"md-primary\">Cancel</md-button>\n </md-dialog-actions>\n </form>\n</md-dialog>\n",
"customCss": "",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenAddDeviceDialog();\n\nfunction openAddDeviceDialog() {\n customDialog.customDialog(htmlTemplate, AddDeviceDialogController).subscribe();\n}\n\nfunction AddDeviceDialogController(instance) {\n let vm = instance;\n \n vm.addDeviceFormGroup = vm.fb.group({\n deviceName: ['', [vm.validators.required]],\n deviceLabel: [''],\n attributes: vm.fb.group({\n latitude: [null],\n longitude: [null]\n }) \n });\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n vm.save = function() {\n vm.addDeviceFormGroup.markAsPristine();\n let device = {\n additionalInfo: {gateway: true},\n name: vm.addDeviceFormGroup.get('deviceName').value,\n type: 'gateway',\n label: vm.addDeviceFormGroup.get('deviceLabel').value\n };\n deviceService.saveDevice(device).subscribe(\n function (device) {\n saveAttributes(device.id).subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n function saveAttributes(entityId) {\n let attributes = vm.addDeviceFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}\n",
"customResources": [],
"id": "70837a9d-c3de-a9a7-03c5-dccd14998758"
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n $q = $injector.get('$q'),\n $rootScope = $injector.get('$rootScope'),\n types = $injector.get('types'),\n deviceService = $injector.get('deviceService'),\n attributeService = $injector.get('attributeService');\n\nopenAddDeviceDialog();\n\nfunction openAddDeviceDialog() {\n $mdDialog.show({\n controller: ['$scope', '$mdDialog',\n AddDeviceDialogController\n ],\n controllerAs: 'vm',\n template: htmlTemplate,\n parent: angular.element($document[0].body),\n targetEvent: $event,\n multiple: true,\n clickOutsideToClose: false\n });\n}\n\nfunction AddDeviceDialogController($scope, $mdDialog) {\n let vm = this;\n vm.types = types;\n vm.attributes = {};\n vm.deviceType = \"gateway\";\n\n vm.cancel = () => {\n $mdDialog.hide();\n };\n\n vm.save = () => {\n vm.loading = true;\n $scope.addDeviceForm.$setPristine();\n let device = {\n additionalInfo: {gateway: true},\n name: vm.deviceName,\n type: vm.deviceType,\n label: vm.deviceLabel\n };\n deviceService.saveDevice(device).then(\n (device) => {\n saveAttributes(device.id).then(\n () => {\n vm.loading = false;\n updateAliasData();\n $mdDialog.hide();\n }\n );\n },\n () => {\n vm.loading = false;\n }\n );\n };\n\n function saveAttributes(entityId) {\n let attributesArray = [];\n for (let key in vm.attributes) {\n attributesArray.push({\n key: key,\n value: vm.attributes[key]\n });\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(\n entityId.entityType, entityId.id,\n \"SERVER_SCOPE\", attributesArray);\n } else {\n return $q.when([]);\n }\n }\n\n function updateAliasData() {\n let aliasIds = [];\n for (let id in widgetContext.aliasController\n .resolvedAliases) {\n aliasIds.push(id);\n }\n let tasks = [];\n aliasIds.forEach((aliasId) => {\n widgetContext.aliasController\n .setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController\n .getAliasInfo(aliasId));\n });\n $q.all(tasks).then(() => {\n $rootScope.$broadcast(\n 'widgetForceReInit');\n });\n }\n}"
}
],
"actionCellButton": [
@ -157,21 +156,20 @@
"setEntityId": true
},
{
"id": "242671f3-76c6-6982-7acc-6f12addf0ccc",
"name": "Edit device",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editDeviceForm=\"ngForm\" [formGroup]=\"editDeviceFormGroup\"\n (ngSubmit)=\"save()\" style=\"width: 480px;\">\n <mat-toolbar fxLayout=\"row\" color=\"primary\">\n <h2>Edit device</h2>\n <span fxFlex></span>\n <button mat-button mat-icon-button\n (click)=\"cancel()\"\n type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content>\n <div class=\"mat-padding\" fxLayout=\"column\">\n <mat-form-field class=\"mat-block\">\n <mat-label>Device name</mat-label>\n <input matInput formControlName=\"deviceName\" required>\n <mat-error *ngIf=\"editDeviceFormGroup.get('deviceName').hasError('required')\">\n Device name is required.\n </mat-error>\n </mat-form-field>\n <div formGroupName=\"attributes\" fxFlex fxLayout=\"row\" fxLayoutGap=\"8px\">\n <mat-form-field fxFlex=\"50\" class=\"mat-block\">\n <mat-label>Latitude</mat-label>\n <input type=\"number\" step=\"any\" matInput formControlName=\"latitude\">\n </mat-form-field>\n <mat-form-field fxFlex=\"50\" class=\"mat-block\">\n <mat-label>Longitude</mat-label>\n <input type=\"number\" step=\"any\" matInput formControlName=\"longitude\">\n </mat-form-field>\n </div>\n <mat-form-field class=\"mat-block\">\n <mat-label>Label</mat-label>\n <input matInput formControlName=\"deviceLabel\">\n </mat-form-field>\n </div> \n </div>\n <div mat-dialog-actions fxLayout=\"row\">\n <span fxFlex></span>\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editDeviceForm.invalid || !editDeviceForm.dirty\">\n Update\n </button>\n <button mat-button color=\"primary\"\n style=\"margin-right: 20px;\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>\n",
"customHtml": "<md-dialog aria-label=\"Edit entity\" style=\"width: 480px\">\n <form name=\"editDeviceForm\" ng-submit=\"vm.save()\">\n <md-toolbar>\n <div class=\"md-toolbar-tools\">\n <h2>Edit device</h2>\n <span flex></span>\n <md-button class=\"md-icon-button\" ng-click=\"vm.cancel()\">\n <ng-md-icon icon=\"close\" aria-label=\"Close\"></ng-md-icon>\n </md-button>\n </div>\n </md-toolbar>\n <md-progress-linear class=\"md-warn\" md-mode=\"indeterminate\" ng-disabled=\"!$root.loading && !vm.loading\" ng-show=\"$root.loading || vm.loading\"></md-progress-linear>\n <span style=\"min-height: 5px;\" flex=\"\" ng-show=\"!$root.loading && !vm.loading\"></span>\n <md-dialog-content>\n <div class=\"md-dialog-content\">\n <fieldset ng-disabled=\"$root.loading || vm.loading\">\n <md-input-container flex class=\"md-block\">\n <label>Device name</label>\n <input ng-model=\"vm.device.name\" name=deviceName required>\n <div ng-messages=\"editDeviceForm.deviceName.$error\">\n <div ng-message=\"required\">Device name is required.</div>\n </div>\n </md-input-container>\n <!--<div flex layout=\"row\">-->\n <!--<tb-entity-subtype-autocomplete flex=\"50\"-->\n <!-- ng-disabled=\"true\"-->\n <!-- tb-required=\"true\"-->\n <!-- the-form=\"editDeviceForm\"-->\n <!-- ng-model=\"vm.device.type\"-->\n <!-- entity-type=\"vm.types.entityType.device\">-->\n <!--</tb-entity-subtype-autocomplete>-->\n <!-- <md-input-container flex=\"50\" class=\"md-block\">-->\n <!-- <label>Label</label>-->\n <!-- <input name=\"deviceLabel\" ng-model=\"vm.device.label\">-->\n <!-- </md-input-container>-->\n <!--</div>-->\n <div flex layout=\"row\">\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Latitude</label>\n <input type=\"number\" step=\"any\" name=\"latitude\" ng-model=\"vm.attributes.latitude\">\n </md-input-container>\n <md-input-container flex=\"50\" class=\"md-block\">\n <label>Longitude</label>\n <input type=\"number\" step=\"any\" name=\"longitude\" ng-model=\"vm.attributes.longitude\">\n </md-input-container>\n </div>\n <md-input-container class=\"md-block\">\n <label>Label</label>\n <input name=\"deviceLabel\" ng-model=\"vm.device.label\">\n </md-input-container>\n </fieldset>\n </div>\n </md-dialog-content>\n <md-dialog-actions>\n <md-button type=\"submit\" ng-disabled=\"vm.loading || editDeviceForm.$invalid || !editDeviceForm.$dirty\" class=\"md-raised md-primary\">Create</md-button>\n <md-button ng-click=\"vm.cancel()\" class=\"md-primary\">Cancel</md-button>\n </md-dialog-actions>\n </form>\n</md-dialog>",
"customCss": "/*=======================================================================*/\n/*========== There are two examples: for edit and add entity ==========*/\n/*=======================================================================*/\n/*======================== Edit entity example ========================*/\n/*=======================================================================*/\n/*\n.edit-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.edit-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.edit-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n\n.relations-list.old-relations tb-entity-select tb-entity-autocomplete button {\n display: none;\n} \n*/\n/*========================================================================*/\n/*========================= Add entity example =========================*/\n/*========================================================================*/\n/*\n.add-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.add-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.add-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n.relations-list .header {\n padding-right: 5px;\n padding-bottom: 5px;\n padding-left: 5px;\n}\n\n.relations-list .header .cell {\n padding-right: 5px;\n padding-left: 5px;\n font-size: 12px;\n font-weight: 700;\n color: rgba(0, 0, 0, .54);\n white-space: nowrap;\n}\n\n.relations-list .body {\n padding-right: 5px;\n padding-bottom: 15px;\n padding-left: 5px;\n}\n\n.relations-list .body .row {\n padding-top: 5px;\n}\n\n.relations-list .body .cell {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.relations-list .body md-autocomplete-wrap md-input-container {\n height: 30px;\n}\n\n.relations-list .body .md-button {\n margin: 0;\n}\n*/\n",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenEditDeviceDialog();\n\nfunction openEditDeviceDialog() {\n customDialog.customDialog(htmlTemplate, EditDeviceDialogController).subscribe();\n}\n\nfunction EditDeviceDialogController(instance) {\n let vm = instance;\n \n vm.device = null;\n vm.attributes = {};\n \n vm.editDeviceFormGroup = vm.fb.group({\n deviceName: ['', [vm.validators.required]],\n deviceLabel: [''],\n attributes: vm.fb.group({\n latitude: [null],\n longitude: [null]\n }) \n });\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n vm.save = function() {\n vm.editDeviceFormGroup.markAsPristine();\n vm.device.name = vm.editDeviceFormGroup.get('deviceName').value;\n vm.device.label = vm.editDeviceFormGroup.get('deviceLabel').value;\n deviceService.saveDevice(vm.device).subscribe(\n function () {\n saveAttributes().subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n getEntityInfo();\n \n function getEntityInfo() {\n deviceService.getDevice(entityId.id).subscribe(\n function (device) {\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE',\n ['latitude', 'longitude']).subscribe(\n function (attributes) {\n for (let i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value; \n }\n vm.device = device;\n vm.editDeviceFormGroup.patchValue(\n {\n deviceName: vm.device.name,\n deviceLabel: vm.device.label,\n attributes: {\n latitude: vm.attributes.latitude,\n longitude: vm.attributes.longitude\n }\n }, {emitEvent: false}\n );\n } \n );\n }\n ); \n }\n \n function saveAttributes() {\n let attributes = vm.editDeviceFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, 'SERVER_SCOPE', attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}\n",
"customResources": [],
"id": "242671f3-76c6-6982-7acc-6f12addf0ccc"
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n $q = $injector.get('$q'),\n $rootScope = $injector.get('$rootScope'),\n types = $injector.get('types'),\n deviceService = $injector.get('deviceService'),\n attributeService = $injector.get('attributeService');\n \nopenEditDeviceDialog();\n\nfunction openEditDeviceDialog() {\n $mdDialog.show({\n controller: ['$scope','$mdDialog', EditDeviceDialogController],\n controllerAs: 'vm',\n template: htmlTemplate,\n parent: angular.element($document[0].body),\n targetEvent: $event,\n multiple: true,\n clickOutsideToClose: false\n });\n}\n\nfunction EditDeviceDialogController($scope,$mdDialog) {\n let vm = this;\n vm.types = types;\n vm.loading = false;\n vm.attributes = {};\n \n getEntityInfo();\n \n function getEntityInfo() {\n vm.loading = true;\n deviceService.getDevice(entityId.id).then(\n (device) => {\n attributeService.getEntityAttributesValues(entityId.entityType, entityId.id, 'SERVER_SCOPE').then(\n (data) => {\n if (data.length) {\n getEntityAttributes(data);\n }\n vm.device = device;\n vm.loading = false;\n } \n );\n }\n )\n }\n \n vm.cancel = function() {\n $mdDialog.hide();\n };\n \n vm.save = () => {\n vm.loading = true;\n $scope.editDeviceForm.$setPristine();\n deviceService.saveDevice(vm.device).then(\n () => {\n saveAttributes().then(\n () => {\n updateAliasData();\n vm.loading = false;\n $mdDialog.hide();\n }\n );\n },\n () => {\n vm.loading = false;\n }\n );\n }\n \n function getEntityAttributes(attributes) {\n for (let i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value; \n }\n }\n \n function saveAttributes() {\n let attributesArray = [];\n for (let key in vm.attributes) {\n attributesArray.push({key: key, value: vm.attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId.entityType, entityId.id, \"SERVER_SCOPE\", attributesArray);\n } else {\n return $q.when([]);\n }\n }\n \n function updateAliasData() {\n let aliasIds = [];\n for (let id in widgetContext.aliasController.resolvedAliases) {\n aliasIds.push(id);\n }\n let tasks = [];\n aliasIds.forEach((aliasId) => {\n widgetContext.aliasController.setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController.getAliasInfo(aliasId));\n });\n console.log(widgetContext);\n $q.all(tasks).then(() => {\n $rootScope.$broadcast('widgetForceReInit');\n });\n }\n}\n"
},
{
"id": "862ec2b7-fbcf-376e-f85f-b77c07f36efa",
"name": "Delete device",
"icon": "delete",
"type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenDeleteDeviceDialog();\n\nfunction openDeleteDeviceDialog() {\n let title = \"Are you sure you want to delete the device \" + entityName + \"?\";\n let content = \"Be careful, after the confirmation, the device and all related data will become unrecoverable!\";\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\n function (result) {\n if (result) {\n deleteDevice();\n }\n }\n );\n}\n\nfunction deleteDevice() {\n deviceService.deleteDevice(entityId.id).subscribe(\n function () {\n widgetContext.updateAliases();\n }\n );\n}\n",
"id": "862ec2b7-fbcf-376e-f85f-b77c07f36efa"
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n types = $injector.get('types'),\n deviceService = $injector.get('deviceService'),\n $rootScope = $injector.get('$rootScope'),\n $q = $injector.get('$q');\n\nopenDeleteDeviceDialog();\n\nfunction openDeleteDeviceDialog() {\n let title = \"Are you sure you want to delete the device \" + entityName + \"?\";\n let content = \"Be careful, after the confirmation, the device and all related data will become unrecoverable!\";\n let confirm = $mdDialog.confirm()\n .targetEvent($event)\n .title(title)\n .htmlContent(content)\n .ariaLabel(title)\n .cancel('Cancel')\n .ok('Delete');\n $mdDialog.show(confirm).then(() => {\n deleteDevice();\n })\n}\n\nfunction deleteDevice() {\n deviceService.deleteDevice(entityId.id).then(\n () => {\n updateAliasData();\n }\n );\n}\n\nfunction updateAliasData() {\n let aliasIds = [];\n for (let id in widgetContext.aliasController.resolvedAliases) {\n aliasIds.push(id);\n }\n let tasks = [];\n aliasIds.forEach((aliasId) => {\n widgetContext.aliasController.setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController.getAliasInfo(aliasId));\n });\n $q.all(tasks).then(() => {\n $rootScope.$broadcast('entityAliasesChanged', aliasIds);\n });\n}"
}
],
"rowClick": [
@ -1104,11 +1102,14 @@
"backgroundColor": "#eeeeee",
"color": "rgba(0,0,0,0.870588)",
"columns": 24,
"margins": [
10,
10
],
"backgroundSizeMode": "100%",
"autoFillHeight": true,
"mobileAutoFillHeight": false,
"mobileRowHeight": 70,
"margin": 10
"mobileRowHeight": 70
}
}
}
@ -1154,11 +1155,14 @@
"backgroundColor": "#eeeeee",
"color": "rgba(0,0,0,0.870588)",
"columns": 24,
"margins": [
10,
10
],
"backgroundSizeMode": "100%",
"autoFillHeight": true,
"mobileAutoFillHeight": false,
"mobileRowHeight": 70,
"margin": 10
"mobileRowHeight": 70
}
}
}
@ -1207,11 +1211,14 @@
"backgroundColor": "#eeeeee",
"color": "rgba(0,0,0,0.870588)",
"columns": 24,
"margins": [
10,
10
],
"backgroundSizeMode": "auto 100%",
"autoFillHeight": true,
"mobileAutoFillHeight": true,
"mobileRowHeight": 70,
"margin": 10
"mobileRowHeight": 70
}
}
}

62
application/src/main/data/json/demo/dashboards/thermostats.json

@ -115,14 +115,13 @@
"setEntityId": false
},
{
"id": "8ab5a518-67d2-b6a2-956d-81fd512294b2",
"name": "Add",
"icon": "add",
"type": "customPretty",
"customHtml": "<form #addEntityForm=\"ngForm\" [formGroup]=\"addEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"add-entity-form\">\n <mat-toolbar color=\"primary\">\n <h2>Add thermostat</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Thermostat name</mat-label>\n <input matInput formControlName=\"entityName\" required>\n <mat-error *ngIf=\"addEntityFormGroup.get('entityName').hasError('required')\">\n Thermostat name is required.\n </mat-error>\n </mat-form-field>\n <div formGroupName=\"attributes\" fxLayout=\"column\">\n <mat-slide-toggle formControlName=\"alarmTemperature\">\n High temperature alarm\n </mat-slide-toggle>\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>High temperature threshold, °C</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"addEntityFormGroup.get('attributes').get('alarmTemperature').value\"\n formControlName=\"thresholdTemperature\">\n <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('thresholdTemperature').hasError('required')\">\n High temperature threshold is required.\n </mat-error>\n </mat-form-field>\n \n <mat-slide-toggle formControlName=\"alarmHumidity\">\n Low humidity alarm\n </mat-slide-toggle>\n \n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Low humidity threshold, %</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"addEntityFormGroup.get('attributes').get('alarmHumidity').value\"\n formControlName=\"thresholdHumidity\">\n <mat-error *ngIf=\"addEntityFormGroup.get('attributes').get('thresholdHumidity').hasError('required')\">\n Low humidity threshold is required.\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || addEntityForm.invalid || !addEntityForm.dirty\">\n Create\n </button>\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>",
"customCss": ".add-entity-form{\n width: 300px;\n}\n",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenAddEntityDialog();\n\nfunction openAddEntityDialog() {\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\n}\n\nfunction AddEntityDialogController(instance) {\n let vm = instance;\n \n vm.addEntityFormGroup = vm.fb.group({\n entityName: ['', [vm.validators.required]],\n attributes: vm.fb.group({\n alarmTemperature: [false],\n thresholdTemperature: [{value: null, disabled: true}],\n alarmHumidity: [false],\n thresholdHumidity: [{value: null, disabled: true}]\n })\n });\n \n vm.addEntityFormGroup.get('attributes').get('alarmTemperature').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.addEntityFormGroup.get('attributes').get('thresholdTemperature').enable();\n } else {\n vm.addEntityFormGroup.get('attributes').get('thresholdTemperature').disable();\n }\n });\n \n vm.addEntityFormGroup.get('attributes').get('alarmHumidity').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.addEntityFormGroup.get('attributes').get('thresholdHumidity').enable();\n } else {\n vm.addEntityFormGroup.get('attributes').get('thresholdHumidity').disable();\n }\n });\n\n vm.save = function() {\n vm.addEntityFormGroup.markAsPristine();\n saveEntityObservable().subscribe(\n function (entity) {\n saveAttributes(entity.id).subscribe(\n function () {\n widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n }\n );\n };\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n function saveEntityObservable() {\n const formValues = vm.addEntityFormGroup.value;\n let entity = {\n name: formValues.entityName,\n type: \"thermostat\"\n };\n return deviceService.saveDevice(entity);\n }\n \n function saveAttributes(entityId) {\n let attributes = vm.addEntityFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n if(attributes[key] !== null) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}",
"customResources": [],
"id": "8ab5a518-67d2-b6a2-956d-81fd512294b2"
"customHtml": "<md-dialog aria-label=\"Add entity\">\n <form name=\"addEntityForm\" class=\"add-entity-form\" ng-submit=\"vm.save()\">\n <md-toolbar>\n <div class=\"md-toolbar-tools\">\n <h2>Add thermostat</h2>\n <span flex></span>\n <md-button class=\"md-icon-button\" ng-click=\"vm.cancel()\">\n <ng-md-icon icon=\"close\" aria-label=\"Close\"></ng-md-icon>\n </md-button>\n </div>\n </md-toolbar>\n <md-dialog-content>\n <div class=\"md-dialog-content\">\n <md-input-container flex class=\"md-block\">\n <label>Thermostat name</label>\n <input ng-model=\"vm.entityName\" name=entityName required>\n <div ng-messages=\"addEntityForm.entityName.$error\">\n <div ng-message=\"required\">Thermostat name is required.</div>\n </div>\n </md-input-container>\n <md-switch ng-model=\"vm.attributes.alarmTemperature\">\n High temperature alarm\n </md-switch>\n <md-input-container flex class=\"md-block\">\n <label>High temperature threshold, °C</label>\n <input name=\"thresholdTemperature\" type=\"number\" step=\"any\" \n ng-model=\"vm.attributes.thresholdTemperature\"\n ng-disabled=\"!vm.attributes.alarmTemperature\"\n ng-required=\"vm.attributes.alarmTemperature\">\n <div ng-messages=\"addEntityForm.thresholdTemperature.$error\">\n <div ng-message=\"required\">High temperature threshold is required.</div>\n </div>\n </md-input-container>\n <md-switch ng-model=\"vm.attributes.alarmHumidity\">\n Low humidity alarm\n </md-switch>\n <md-input-container flex class=\"md-block\">\n <label>Low humidity threshold, %</label>\n <input name=\"thresholdHumidity\" type=\"number\" step=\"any\" \n ng-model=\"vm.attributes.thresholdHumidity\"\n ng-disabled=\"!vm.attributes.alarmHumidity\"\n ng-required=\"vm.attributes.alarmHumidity\">\n <div ng-messages=\"addEntityForm.thresholdHumidity.$error\">\n <div ng-message=\"required\">Low humidity threshold is required.</div>\n </div>\n </md-input-container>\n </div>\n </md-dialog-content>\n <md-dialog-actions>\n <md-button type=\"submit\" ng-disabled=\"addEntityForm.$invalid || !addEntityForm.$dirty\" class=\"md-raised md-primary\">Create</md-button>\n <md-button ng-click=\"vm.cancel()\" class=\"md-primary\">Cancel</md-button>\n </md-dialog-actions>\n </form>\n</md-dialog>",
"customCss": ".add-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.add-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.add-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n\n",
"customFunction": "var $injector = widgetContext.$scope.$injector;\nvar $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n $q = $injector.get('$q'),\n $rootScope = $injector.get('$rootScope'),\n deviceService = $injector.get('deviceService'),\n attributeService = $injector.get('attributeService');\n\nopenAddEntityDialog();\n\nfunction openAddEntityDialog() {\n $mdDialog.show({\n controller: ['$scope','$mdDialog', AddEntityDialogController],\n controllerAs: 'vm',\n template: htmlTemplate,\n locals: {\n entityId: entityId\n },\n parent: angular.element($document[0].body),\n targetEvent: $event,\n multiple: true,\n clickOutsideToClose: false\n });\n}\n\nfunction AddEntityDialogController($scope, $mdDialog) {\n var vm = this;\n vm.attributes = {};\n\n vm.save = function() {\n $scope.addEntityForm.$setPristine();\n saveEntityPromise().then(\n function (entity) {\n saveAttributes(entity.id).then(() => {\n updateAliasData();\n $mdDialog.hide();\n });\n }\n );\n };\n vm.cancel = function() {\n $mdDialog.hide();\n };\n \n \n function saveEntityPromise() {\n var entity = {\n name: vm.entityName,\n type: \"thermostat\"\n };\n return deviceService.saveDevice(entity);\n }\n \n function saveAttributes(entityId) {\n var attributesArray = [];\n for (var key in vm.attributes) {\n attributesArray.push({key: key, value: vm.attributes[key]});\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId.entityType, entityId.id, \"SERVER_SCOPE\", attributesArray);\n } else {\n return $q.when(null);\n } \n }\n \n function updateAliasData() {\n var aliasIds = [];\n for (var id in widgetContext.aliasController.resolvedAliases) {\n aliasIds.push(id);\n }\n var tasks = [];\n aliasIds.forEach(function(aliasId) {\n widgetContext.aliasController.setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController.getAliasInfo(aliasId));\n });\n $q.all(tasks).then(function() {\n $rootScope.$broadcast('widgetForceReInit');\n });\n }\n}"
}
],
"actionCellButton": [
@ -135,21 +134,20 @@
"setEntityId": true
},
{
"id": "7506576f-87ba-d3a0-88fb-e304d451776d",
"name": "Edit",
"icon": "edit",
"type": "customPretty",
"customHtml": "<form #editEntityForm=\"ngForm\" [formGroup]=\"editEntityFormGroup\"\n (ngSubmit)=\"save()\" class=\"edit-entity-form\">\n <mat-toolbar color=\"primary\">\n <h2>Edit thermostat {{entityName}}</h2>\n <span fxFlex></span>\n <button mat-icon-button (click)=\"cancel()\" type=\"button\">\n <mat-icon class=\"material-icons\">close</mat-icon>\n </button>\n </mat-toolbar>\n <mat-progress-bar color=\"warn\" mode=\"indeterminate\" *ngIf=\"isLoading$ | async\">\n </mat-progress-bar>\n <div style=\"height: 4px;\" *ngIf=\"!(isLoading$ | async)\"></div>\n <div mat-dialog-content fxLayout=\"column\">\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Thermostat name</mat-label>\n <input matInput formControlName=\"entityName\" readonly>\n </mat-form-field>\n <div formGroupName=\"attributes\" fxLayout=\"column\">\n <mat-slide-toggle formControlName=\"alarmTemperature\">\n High temperature alarm\n </mat-slide-toggle>\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>High temperature threshold, °C</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"editEntityFormGroup.get('attributes').get('alarmTemperature').value\"\n formControlName=\"thresholdTemperature\">\n <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('thresholdTemperature').hasError('required')\">\n High temperature threshold is required.\n </mat-error>\n </mat-form-field>\n\n <mat-slide-toggle formControlName=\"alarmHumidity\">\n Low humidity alarm\n </mat-slide-toggle>\n\n <mat-form-field fxFlex class=\"mat-block\">\n <mat-label>Low humidity threshold, %</mat-label>\n <input type=\"number\" step=\"any\" matInput\n [required] = \"editEntityFormGroup.get('attributes').get('alarmHumidity').value\"\n formControlName=\"thresholdHumidity\">\n <mat-error *ngIf=\"editEntityFormGroup.get('attributes').get('thresholdHumidity').hasError('required')\">\n Low humidity threshold is required.\n </mat-error>\n </mat-form-field>\n </div>\n </div>\n <div mat-dialog-actions fxLayout=\"row\" fxLayoutAlign=\"end center\">\n <button mat-raised-button color=\"primary\"\n type=\"submit\"\n [disabled]=\"(isLoading$ | async) || editEntityForm.invalid || !editEntityForm.dirty\">\n Save\n </button>\n <button mat-button color=\"primary\"\n type=\"button\"\n [disabled]=\"(isLoading$ | async)\"\n (click)=\"cancel()\" cdkFocusInitial>\n Cancel\n </button>\n </div>\n</form>",
"customCss": ".edit-entity-form{\n width: 300px;\n}",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId;\n vm.entityName = entityName;\n vm.attributes = {};\n \n vm.editEntityFormGroup = vm.fb.group({\n entityName: [''],\n attributes: vm.fb.group({\n alarmTemperature: [false],\n thresholdTemperature: [{value: null, disabled: true}],\n alarmHumidity: [false],\n thresholdHumidity: [{value: null, disabled: true}]\n })\n });\n \n vm.editEntityFormGroup.get('attributes').get('alarmTemperature').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.editEntityFormGroup.get('attributes').get('thresholdTemperature').enable();\n } else {\n vm.editEntityFormGroup.get('attributes').get('thresholdTemperature').disable();\n }\n });\n \n vm.editEntityFormGroup.get('attributes').get('alarmHumidity').valueChanges\n .subscribe(activate => {\n if (activate) {\n vm.editEntityFormGroup.get('attributes').get('thresholdHumidity').enable();\n } else {\n vm.editEntityFormGroup.get('attributes').get('thresholdHumidity').disable();\n }\n });\n \n \n getEntityInfo();\n \n \n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveAttributes(entityId).subscribe(\n function () {\n vm.dialogRef.close(null);\n }\n );\n };\n \n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n \n function getEntityAttributes(attributes) {\n for (var i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value;\n }\n }\n \n function getEntityInfo() {\n attributeService.getEntityAttributes(entityId, 'SERVER_SCOPE').subscribe(\n function (attributes) {\n getEntityAttributes(attributes);\n vm.editEntityFormGroup.patchValue({\n entityName: vm.entityName,\n attributes: vm.attributes\n });\n // if(vm.attributes.alarmTemperature) {\n // vm.editEntityFormGroup.get('attributes').get('thresholdTemperature').enable();\n // }\n // if(vm.attributes.alarmHumidity) {\n // vm.editEntityFormGroup.get('attributes').get('thresholdHumidity').enable();\n // }\n }\n );\n }\n \n function saveAttributes(entityId) {\n let attributes = vm.editEntityFormGroup.get('attributes').value;\n let attributesArray = [];\n for (let key in attributes) {\n if (attributes[key] !== vm.attributes[key]) {\n attributesArray.push({key: key, value: attributes[key]});\n }\n }\n if (attributesArray.length > 0) {\n return attributeService.saveEntityAttributes(entityId, \"SERVER_SCOPE\", attributesArray);\n } else {\n return widgetContext.rxjs.of([]);\n }\n }\n}",
"customResources": [],
"id": "7506576f-87ba-d3a0-88fb-e304d451776d"
"customHtml": "<md-dialog aria-label=\"Edit entity\">\n <form name=\"editEntityForm\" class=\"edit-entity-form\" ng-submit=\"vm.save()\">\n <md-toolbar>\n <div class=\"md-toolbar-tools\">\n <h2>Edit thermostat {{vm.entityName}}</h2>\n <span flex></span>\n <md-button class=\"md-icon-button\" ng-click=\"vm.cancel()\">\n <ng-md-icon icon=\"close\" aria-label=\"Close\"></ng-md-icon>\n </md-button>\n </div>\n </md-toolbar>\n <md-dialog-content>\n <div class=\"md-dialog-content\">\n <md-input-container flex class=\"md-block\">\n <label>Thermostat name</label>\n <input ng-model=\"vm.entityName\" readonly>\n </md-input-container>\n <md-switch ng-model=\"vm.attributes.alarmTemperature\">\n High temperature alarm\n </md-switch>\n <md-input-container flex class=\"md-block\">\n <label>High temperature threshold, °C</label>\n <input name=\"thresholdTemperature\" type=\"number\" step=\"any\" \n ng-model=\"vm.attributes.thresholdTemperature\"\n ng-disabled=\"!vm.attributes.alarmTemperature\"\n ng-required=\"vm.attributes.alarmTemperature\">\n <div ng-messages=\"editEntityForm.thresholdTemperature.$error\">\n <div ng-message=\"required\">High temperature threshold is required.</div>\n </div>\n </md-input-container>\n <md-switch ng-model=\"vm.attributes.alarmHumidity\">\n Low humidity alarm\n </md-switch>\n <md-input-container flex class=\"md-block\">\n <label>Low humidity threshold, %</label>\n <input name=\"thresholdHumidity\" type=\"number\" step=\"any\" \n ng-model=\"vm.attributes.thresholdHumidity\"\n ng-disabled=\"!vm.attributes.alarmHumidity\"\n ng-required=\"vm.attributes.alarmHumidity\">\n <div ng-messages=\"editEntityForm.thresholdHumidity.$error\">\n <div ng-message=\"required\">Low humidity threshold is required.</div>\n </div>\n </md-input-container>\n </div>\n </md-dialog-content>\n <md-dialog-actions>\n <md-button type=\"submit\" ng-disabled=\"editEntityForm.$invalid || !editEntityForm.$dirty\" class=\"md-raised md-primary\">Save</md-button>\n <md-button ng-click=\"vm.cancel()\" class=\"md-primary\">Cancel</md-button>\n </md-dialog-actions>\n </form>\n</md-dialog>",
"customCss": ".edit-entity-form md-input-container {\n padding-right: 10px;\n}\n\n.edit-entity-form .boolean-value-input {\n padding-left: 5px;\n}\n\n.edit-entity-form .boolean-value-input .checkbox-label {\n margin-bottom: 8px;\n color: rgba(0,0,0,0.54);\n font-size: 12px;\n}\n",
"customFunction": "var $injector = widgetContext.$scope.$injector;\nvar $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n attributeService = $injector.get('attributeService');\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n $mdDialog.show({\n controller: ['$scope','$mdDialog', EditEntityDialogController],\n controllerAs: 'vm',\n template: htmlTemplate,\n locals: {\n entityId: entityId\n },\n parent: angular.element($document[0].body),\n targetEvent: $event,\n multiple: true,\n clickOutsideToClose: false\n });\n}\n\nfunction EditEntityDialogController($scope,$mdDialog) {\n var vm = this;\n vm.entityId = entityId;\n vm.entityName = entityName;\n vm.entityType = entityId.entityType;\n vm.attributes = {};\n vm.serverAttributes = {};\n getEntityInfo();\n \n vm.save = function() {\n saveAttributes();\n $mdDialog.hide();\n };\n vm.cancel = function() {\n $mdDialog.hide();\n };\n \n function getEntityAttributes(attributes) {\n for (var i = 0; i < attributes.length; i++) {\n vm.attributes[attributes[i].key] = attributes[i].value; \n }\n vm.serverAttributes = angular.copy(vm.attributes);\n }\n \n function getEntityInfo() {\n attributeService.getEntityAttributesValues(entityId.entityType, entityId.id, 'SERVER_SCOPE').then(\n function(data){\n if (data.length) {\n getEntityAttributes(data);\n }\n });\n }\n \n function saveAttributes() {\n var attributesArray = [];\n for (var key in vm.attributes) {\n if (vm.attributes[key] !== vm.serverAttributes[key]) {\n attributesArray.push({key: key, value: vm.attributes[key]});\n }\n }\n if (attributesArray.length > 0) {\n attributeService.saveEntityAttributes(entityId.entityType, entityId.id, \"SERVER_SCOPE\", attributesArray);\n } \n }\n}"
},
{
"id": "3488848b-e47d-6af6-659f-5d78369ece5e",
"name": "Delete",
"icon": "delete",
"type": "custom",
"customFunction": "let $injector = widgetContext.$scope.$injector;\nlet dialogs = $injector.get(widgetContext.servicesMap.get('dialogs'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenDeleteEntityDialog();\n\nfunction openDeleteEntityDialog() {\n let title = 'Delete thermostat \"' + entityName + '\"';\n let content = 'Are you sure you want to delete the thermostat \"' +\n entityName + '\"?';\n dialogs.confirm(title, content, 'Cancel', 'Delete').subscribe(\n function(result) {\n if (result) {\n deleteEntity();\n }\n }\n );\n}\n\nfunction deleteEntity() {\n deviceService.deleteDevice(entityId.id).subscribe(\n function success() {\n widgetContext.updateAliases();\n },\n function fail() {\n showErrorDialog();\n }\n );\n}\n\nfunction showErrorDialog() {\n let title = 'Error';\n let content = 'An error occurred while deleting the thermostat. Please try again.';\n dialogs.alert(title, content, 'CLOSE').subscribe(\n function(result) {}\n );\n}",
"id": "3488848b-e47d-6af6-659f-5d78369ece5e"
"customFunction": "var $injector = widgetContext.$scope.$injector;\nvar $mdDialog = $injector.get('$mdDialog'),\n $document = $injector.get('$document'),\n deviceService = $injector.get('deviceService')\n $rootScope = $injector.get('$rootScope'),\n $q = $injector.get('$q');\n\nopenDeleteEntityDialog();\n\nfunction openDeleteEntityDialog() {\n var title = 'Delete thermostat \"' + entityName + '\"';\n var content = 'Are you sure you want to delete the thermostat \"' +\n entityName + '\"?';\n var confirm = $mdDialog.confirm()\n .targetEvent($event)\n .title(title)\n .htmlContent(content)\n .ariaLabel(title)\n .cancel('Cancel')\n .ok('Delete');\n $mdDialog.show(confirm).then(function() {\n deleteEntity();\n })\n}\n\nfunction deleteEntity() {\n deviceService.deleteDevice(entityId.id).then(\n function success() {\n updateAliasData();\n },\n function fail() {\n showErrorDialog();\n }\n );\n}\n\nfunction updateAliasData() {\n var aliasIds = [];\n for (var id in widgetContext.aliasController.resolvedAliases) {\n aliasIds.push(id);\n }\n var tasks = [];\n aliasIds.forEach(function(aliasId) {\n widgetContext.aliasController.setAliasUnresolved(aliasId);\n tasks.push(widgetContext.aliasController.getAliasInfo(aliasId));\n });\n $q.all(tasks).then(function() {\n $rootScope.$broadcast('entityAliasesChanged', aliasIds);\n });\n}\n\nfunction showErrorDialog() {\n var title = 'Error';\n var content = 'An error occurred while deleting the thermostat. Please try again.';\n var alert = $mdDialog.alert()\n .title(title)\n .htmlContent(content)\n .ariaLabel(title)\n .parent(angular.element($document[0].body))\n .targetEvent($event)\n .multiple(true)\n .clickOutsideToClose(true)\n .ok('CLOSE');\n $mdDialog.show(alert);\n}"
}
],
"rowClick": []
@ -850,7 +848,10 @@
"mapProvider": "OpenStreetMap.HOT",
"showTooltip": true,
"autocloseTooltip": true,
"defaultCenterPosition": "0,0",
"defaultCenterPosition": [
0,
0
],
"customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
"showTooltipAction": "click",
"polygonKeyName": "coordinates",
@ -865,9 +866,7 @@
"defaultZoomLevel": 12,
"labelFunction": "var color;\nif(dsData[dsIndex].active !== \"true\"){\n color = 'rgb(255, 0, 0)';\n} else {\n color = 'rgb(39, 134, 34)';\n}\nreturn '<span style=\"border: solid ' + color + '; border-radius: 10px; color: ' + color + '; background-color: #fff; padding: 3px 5px; font-size: 14px\">' + \n '${entityLabel}' + \n '</span>'",
"markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;",
"useLabelFunction": true,
"provider": "openstreet-map",
"draggableMarker": true
"useLabelFunction": true
},
"title": "New Markers Placement - OpenStreetMap",
"dropShadow": true,
@ -985,7 +984,10 @@
"mapProvider": "OpenStreetMap.HOT",
"showTooltip": true,
"autocloseTooltip": true,
"defaultCenterPosition": "0,0",
"defaultCenterPosition": [
37.7749,
-122.4194
],
"customProviderTileUrl": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
"showTooltipAction": "click",
"polygonKeyName": "coordinates",
@ -1001,8 +1003,7 @@
"labelFunction": "var color;\nif(dsData[dsIndex].active !== \"true\"){\n color = 'rgb(255, 0, 0)';\n} else {\n color = 'rgb(39, 134, 34)';\n}\nreturn '<span style=\"border: solid ' + color + '; border-radius: 10px; color: ' + color + '; background-color: #fff; padding: 3px 5px; font-size: 14px\">' + \n '${entityLabel}' + \n '</span>'",
"markerImageFunction": "var res;\nif(dsData[dsIndex].active !== \"true\"){\n\tvar res = {\n\t url: images[0],\n\t size: 48\n\t}\n} else {\n var res = {\n\t url: images[1],\n\t size: 48\n\t}\n}\nreturn res;",
"useLabelFunction": true,
"provider": "openstreet-map",
"draggableMarker": true
"useDefaultCenterPosition": true
},
"title": "New Markers Placement - OpenStreetMap",
"dropShadow": true,
@ -1065,11 +1066,14 @@
"backgroundColor": "#eeeeee",
"color": "rgba(0,0,0,0.870588)",
"columns": 24,
"margins": [
10,
10
],
"backgroundSizeMode": "100%",
"autoFillHeight": true,
"mobileAutoFillHeight": false,
"mobileRowHeight": 70,
"margin": 10
"mobileRowHeight": 70
}
}
}
@ -1091,11 +1095,14 @@
"backgroundColor": "#eeeeee",
"color": "rgba(0,0,0,0.870588)",
"columns": 24,
"margins": [
10,
10
],
"backgroundSizeMode": "100%",
"autoFillHeight": true,
"mobileAutoFillHeight": false,
"mobileRowHeight": 70,
"margin": 10
"mobileRowHeight": 70
}
}
}
@ -1137,11 +1144,14 @@
"backgroundColor": "#eeeeee",
"color": "rgba(0,0,0,0.870588)",
"columns": 24,
"margins": [
10,
10
],
"backgroundSizeMode": "100%",
"autoFillHeight": true,
"mobileAutoFillHeight": false,
"mobileRowHeight": 70,
"margin": 10
"mobileRowHeight": 70
}
}
}
@ -1214,4 +1224,4 @@
}
},
"name": "Thermostats"
}
}

4
application/src/main/data/json/system/widget_bundles/alarm_widgets.json

@ -13,9 +13,9 @@
"sizeX": 10.5,
"sizeY": 6.5,
"resources": [],
"templateHtml": "<tb-alarms-table-widget \n [ctx]=\"ctx\">\n</tb-alarms-table-widget>",
"templateHtml": "<tb-alarms-table-widget \n table-id=\"tableId\"\n ctx=\"ctx\">\n</tb-alarms-table-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.alarmsTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
"controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n var id = self.ctx.$scope.$injector.get('utils').guid();\n scope.tableId = \"table-\"+id;\n scope.ctx = self.ctx;\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.$broadcast('alarms-table-data-updated', self.ctx.$scope.tableId);\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"AlarmTableSettings\",\n \"properties\": {\n \"alarmsTitle\": {\n \"title\": \"Alarms table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSelection\": {\n \"title\": \"Enable alarms selection\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSearch\": {\n \"title\": \"Enable alarms search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSelectColumnDisplay\": {\n \"title\": \"Enable select columns to display\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStatusFilter\": {\n \"title\": \"Enable alarm status filter\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayDetails\": {\n \"title\": \"Display alarm details\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowAcknowledgment\": {\n \"title\": \"Allow alarms acknowledgment\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowClear\": {\n \"title\": \"Allow alarms clear\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"-createdTime\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"alarmsTitle\",\n \"enableSelection\",\n \"enableSearch\",\n \"enableSelectColumnDisplay\",\n \"enableStatusFilter\",\n \"displayDetails\",\n \"allowAcknowledgment\",\n \"allowClear\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\"\n ]\n}",
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"columnWidth\": {\n \"title\": \"Column width (px or %)\",\n \"type\": \"string\",\n \"default\": \"0px\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, alarm, filter)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStatusFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{}}"

24
application/src/main/data/json/system/widget_bundles/cards.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_bundles/charts.json

@ -55,7 +55,7 @@
],
"templateHtml": "<canvas id=\"pieChart\"></canvas>\n",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n var borderColor = self.ctx.settings.borderColor || '#fff';\n var borderWidth = typeof self.ctx.settings.borderWidth !== 'undefined' ? self.ctx.settings.borderWidth : 5;\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n pieData.labels.push(dataKey.label);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push(borderColor);\n dataset.borderWidth.push(borderWidth);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var options = {\n responsive: false,\n maintainAspectRatio: false,\n legend: {\n display: true,\n labels: {\n fontColor: '#666'\n }\n },\n tooltips: {\n callbacks: {\n label: function(tooltipItem, data) {\n var label = data.labels[tooltipItem.index];\n var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];\n var content = label + ': ' + value;\n var units = self.ctx.settings.units ? self.ctx.settings.units : self.ctx.units;\n if (units) {\n content += ' ' + units;\n } \n return content;\n }\n }\n }\n };\n\n if (self.ctx.settings.legend) {\n options.legend.display = self.ctx.settings.legend.display !== false;\n options.legend.labels.fontColor = self.ctx.settings.legend.labelsFontColor || '#666';\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'doughnut',\n data: pieData,\n options: options\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = tvPair[1];\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\n",
"controllerScript": "self.onInit = function() {\n var pieData = {\n labels: [],\n datasets: []\n };\n\n var dataset = {\n data: [],\n backgroundColor: [],\n borderColor: [],\n borderWidth: [],\n hoverBackgroundColor: []\n }\n \n var borderColor = self.ctx.settings.borderColor || '#fff';\n var borderWidth = angular.isDefined(self.ctx.settings.borderWidth) ? self.ctx.settings.borderWidth : 5;\n \n pieData.datasets.push(dataset);\n \n for (var i=0; i < self.ctx.data.length; i++) {\n var dataKey = self.ctx.data[i].dataKey;\n pieData.labels.push(dataKey.label);\n dataset.data.push(0);\n var hoverBackgroundColor = tinycolor(dataKey.color).lighten(15);\n dataset.backgroundColor.push(dataKey.color);\n dataset.borderColor.push(borderColor);\n dataset.borderWidth.push(borderWidth);\n dataset.hoverBackgroundColor.push(hoverBackgroundColor.toRgbString());\n }\n\n var options = {\n responsive: false,\n maintainAspectRatio: false,\n legend: {\n display: true,\n labels: {\n fontColor: '#666'\n }\n },\n tooltips: {\n callbacks: {\n label: function(tooltipItem, data) {\n var label = data.labels[tooltipItem.index];\n var value = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];\n var content = label + ': ' + value;\n var units = self.ctx.settings.units ? self.ctx.settings.units : self.ctx.units;\n if (units) {\n content += ' ' + units;\n } \n return content;\n }\n }\n }\n };\n\n if (self.ctx.settings.legend) {\n options.legend.display = self.ctx.settings.legend.display !== false;\n options.legend.labels.fontColor = self.ctx.settings.legend.labelsFontColor || '#666';\n }\n\n var ctx = $('#pieChart', self.ctx.$container);\n self.ctx.chart = new Chart(ctx, {\n type: 'doughnut',\n data: pieData,\n options: options\n });\n \n self.onResize();\n}\n\nself.onDataUpdated = function() {\n for (var i = 0; i < self.ctx.data.length; i++) {\n var cellData = self.ctx.data[i];\n if (cellData.data.length > 0) {\n var tvPair = cellData.data[cellData.data.length - 1];\n var value = tvPair[1];\n self.ctx.chart.data.datasets[0].data[i] = parseFloat(value);\n }\n }\n self.ctx.chart.update();\n}\n\nself.onResize = function() {\n self.ctx.chart.resize();\n}\n\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"borderWidth\": {\n \"title\": \"Border width\",\n \"type\": \"number\",\n \"default\": 5\n },\n \"borderColor\": {\n \"title\": \"Border color\",\n \"type\": \"string\",\n \"default\": \"#fff\"\n },\n \"legend\": {\n \"title\": \"Legend settings\",\n \"type\": \"object\",\n \"properties\": {\n \"display\": {\n \"title\": \"Display legend\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"labelsFontColor\": {\n \"title\": \"Labels font color\",\n \"type\": \"string\",\n \"default\": \"#666\"\n }\n }\n }\n },\n \"required\": []\n },\n \"form\": [\n \"borderWidth\", \n {\n \"key\": \"borderColor\",\n \"type\": \"color\"\n }, \n {\n \"key\": \"legend\",\n \"items\": [\n \"legend.display\",\n {\n \"key\": \"legend.labelsFontColor\",\n \"type\": \"color\"\n }\n ]\n }\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#26a69a\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#f57c00\",\"settings\":{},\"_hash\":0.545701115289893,\"funcBody\":\"var value = (prevValue-20) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+20;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Third\",\"color\":\"#afb42b\",\"settings\":{},\"_hash\":0.2592906835158064,\"funcBody\":\"var value = (prevValue-40) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+40;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Fourth\",\"color\":\"#673ab7\",\"settings\":{},\"_hash\":0.12880275585455747,\"funcBody\":\"var value = (prevValue-50) + Math.random() * 2 - 1;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value+50;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"borderWidth\":5,\"borderColor\":\"#fff\",\"legend\":{\"display\":true,\"labelsFontColor\":\"#666666\"}},\"title\":\"Doughnut - Chart.js\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400}}"

34
application/src/main/data/json/system/widget_bundles/control_widgets.json

File diff suppressed because one or more lines are too long

4
application/src/main/data/json/system/widget_bundles/date.json

@ -13,9 +13,9 @@
"sizeX": 5,
"sizeY": 5.5,
"resources": [],
"templateHtml": "<tb-date-range-navigator-widget [ctx]=\"ctx\"></tb-date-range-navigator-widget>",
"templateHtml": "<date-range-navigator-widget class=\"date-range-navigator-widget\" ctx=\"ctx\"></date-range-navigator-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n",
"controllerScript": "self.onInit = function() {\n scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"hidePicker\": {\n \"title\": \"Hide date range picker\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"onePanel\": {\n \"title\": \"Date range picker one panel\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"autoConfirm\": {\n \"title\": \"Date range picker auto confirm\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"showTemplate\": {\n \"title\": \"Date range picker show template\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"firstDayOfWeek\": {\n \"title\": \"First day of the week\",\n \"type\": \"number\",\n \"default\": 1\n },\n \"hideInterval\": {\n \"title\": \"Hide interval\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"initialInterval\": {\n\t\t\t\t\"title\": \"Initial interval\",\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"default\": \"week\"\n\t\t\t},\n \"hideStepSize\": {\n \"title\": \"Hide step size\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"stepSize\": {\n\t\t\t\t\"title\": \"Initial step size\",\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"default\": \"day\"\n\t\t\t},\n \"hideLabels\": {\n \"title\": \"Hide labels\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useSessionStorage\": {\n \"title\": \"Use session storage\",\n \"type\": \"boolean\",\n \"default\": true\n }\n }\n },\n \"form\": [\n \"hidePicker\",\n\t\t\"onePanel\",\n\t\t\"autoConfirm\",\n\t\t\"showTemplate\",\n\t\t\"firstDayOfWeek\",\n \"hideInterval\",\n {\n\t\t\t\"key\": \"initialInterval\",\n\t\t\t\"type\": \"rc-select\",\n\t\t\t\"multiple\": false,\n\t\t\t\"items\": [\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"hour\",\n\t\t\t\t\t\"label\": \"Hour\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"day\",\n\t\t\t\t\t\"label\": \"Day\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"week\",\n\t\t\t\t\t\"label\": \"Week\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"twoWeeks\",\n\t\t\t\t\t\"label\": \"2 weeks\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"month\",\n\t\t\t\t\t\"label\": \"Month\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"threeMonths\",\n\t\t\t\t\t\"label\": \"3 months\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"sixMonths\",\n\t\t\t\t\t\"label\": \"6 months\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n \"hideStepSize\",\n {\n\t\t\t\"key\": \"stepSize\",\n\t\t\t\"type\": \"rc-select\",\n\t\t\t\"multiple\": false,\n\t\t\t\"items\": [\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"hour\",\n\t\t\t\t\t\"label\": \"Hour\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"day\",\n\t\t\t\t\t\"label\": \"Day\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"week\",\n\t\t\t\t\t\"label\": \"Week\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"twoWeeks\",\n\t\t\t\t\t\"label\": \"2 weeks\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"month\",\n\t\t\t\t\t\"label\": \"Month\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"threeMonths\",\n\t\t\t\t\t\"label\": \"3 months\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"sixMonths\",\n\t\t\t\t\t\"label\": \"6 months\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t\"hideLabels\",\n\t\t\"useSessionStorage\"\n ]\n}",
"dataKeySettingsSchema": "{}\n",
"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\":\"8px\",\"settings\":{\"defaultInterval\":\"week\",\"stepSize\":\"day\"},\"title\":\"Date-range-navigator\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}"

14
application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json

File diff suppressed because one or more lines are too long

58
application/src/main/data/json/system/widget_bundles/gateway_widgets.json

File diff suppressed because one or more lines are too long

48
application/src/main/data/json/system/widget_bundles/gpio_widgets.json

File diff suppressed because one or more lines are too long

158
application/src/main/data/json/system/widget_bundles/input_widgets.json

File diff suppressed because one or more lines are too long

40
application/src/main/data/json/system/widget_bundles/maps.json

File diff suppressed because one or more lines are too long

73
application/src/main/data/json/system/widget_bundles/maps__deprecated_.json

File diff suppressed because one or more lines are too long

4
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -16,6 +16,7 @@
package org.thingsboard.server.actors.ruleChain;
import akka.actor.ActorRef;
import com.datastax.driver.core.ResultSetFuture;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.EventLoopGroup;
@ -52,7 +53,6 @@ import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.nosql.CassandraStatementTask;
import org.thingsboard.server.dao.nosql.TbResultSetFuture;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
@ -405,7 +405,7 @@ class DefaultTbContext implements TbContext {
}
@Override
public TbResultSetFuture submitCassandraTask(CassandraStatementTask task) {
public ResultSetFuture submitCassandraTask(CassandraStatementTask task) {
return mainCtx.getCassandraBufferedRateExecutor().submit(task);
}

4
application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java

@ -69,7 +69,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public";
public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";
protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/assets/**", "/static/**", "/api/noauth/**", "/webjars/**"};
protected static final String[] NON_TOKEN_BASED_AUTH_ENTRY_POINTS = new String[] {"/index.html", "/static/**", "/api/noauth/**", "/webjars/**"};
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
public static final String WS_TOKEN_BASED_AUTH_ENTRY_POINT = "/api/ws/**";
@ -172,7 +172,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**");
web.ignoring().antMatchers("/static/**");
}
@Override

2
application/src/main/java/org/thingsboard/server/config/WebConfig.java

@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class WebConfig {
@RequestMapping(value = "/{path:^(?!api$)(?!assets$)(?!static$)(?!webjars$)[^\\.]*}/**")
@RequestMapping(value = "/{path:^(?!api$)(?!static$)(?!webjars$)[^\\.]*}/**")
public String redirect() {
return "forward:/index.html";
}

21
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -39,14 +39,12 @@ 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.page.PageData;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.UUID;
@RestController
@TbCoreComponent
@RequestMapping("/api")
@ -151,18 +149,15 @@ public class AlarmController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public PageData<AlarmInfo> getAlarms(
public TimePageData<AlarmInfo> getAlarms(
@PathVariable("entityType") String strEntityType,
@PathVariable("entityId") String strEntityId,
@RequestParam(required = false) String searchStatus,
@RequestParam(required = false) String status,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset,
@RequestParam(required = false) Boolean fetchOriginator
) throws ThingsboardException {
@ -176,13 +171,9 @@ public class AlarmController extends BaseController {
"and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
checkEntityId(entityId, Operation.READ);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
UUID idOffsetUuid = null;
if (StringUtils.isNotEmpty(offset)) {
idOffsetUuid = toUUID(offset);
}
try {
return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator, idOffsetUuid)).get());
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(alarmService.findAlarms(getCurrentUser().getTenantId(), new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
} catch (Exception e) {
throw handleException(e);
}

95
application/src/main/java/org/thingsboard/server/controller/AssetController.java

@ -30,16 +30,14 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.asset.AssetSearchQuery;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
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.security.Authority;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -71,19 +69,6 @@ public class AssetController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/asset/info/{assetId}", method = RequestMethod.GET)
@ResponseBody
public AssetInfo getAssetInfoById(@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter(ASSET_ID, strAssetId);
try {
AssetId assetId = new AssetId(toUUID(strAssetId));
return checkAssetInfoId(assetId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/asset", method = RequestMethod.POST)
@ResponseBody
@ -222,18 +207,17 @@ public class AssetController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/tenant/assets", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<Asset> getTenantAssets(
@RequestParam int pageSize,
@RequestParam int page,
public TextPageData<Asset> getTenantAssets(
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length()>0) {
return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink));
} else {
@ -244,29 +228,6 @@ public class AssetController extends BaseController {
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/assetInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<AssetInfo> getTenantAssetInfos(
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
if (type != null && type.trim().length() > 0) {
return checkNotNull(assetService.findAssetInfosByTenantIdAndType(tenantId, type, pageLink));
} else {
return checkNotNull(assetService.findAssetInfosByTenantId(tenantId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/assets", params = {"assetName"}, method = RequestMethod.GET)
@ResponseBody
@ -281,22 +242,21 @@ public class AssetController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/customer/{customerId}/assets", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<Asset> getCustomerAssets(
public TextPageData<Asset> getCustomerAssets(
@PathVariable("customerId") String strCustomerId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length()>0) {
return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
} else {
@ -307,33 +267,6 @@ public class AssetController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/assetInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<AssetInfo> getCustomerAssetInfos(
@PathVariable("customerId") String strCustomerId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
if (type != null && type.trim().length() > 0) {
return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
} else {
return checkNotNull(assetService.findAssetInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/assets", params = {"assetIds"}, method = RequestMethod.GET)
@ResponseBody

58
application/src/main/java/org/thingsboard/server/controller/AuditLogController.java

@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -45,22 +45,20 @@ import java.util.stream.Collectors;
public class AuditLogController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs/customer/{customerId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/audit/logs/customer/{customerId}", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<AuditLog> getAuditLogsByCustomerId(
public TimePageData<AuditLog> getAuditLogsByCustomerId(
@PathVariable("customerId") String strCustomerId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset,
@RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
try {
checkParameter("CustomerId", strCustomerId);
TenantId tenantId = getCurrentUser().getTenantId();
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
List<ActionType> actionTypes = parseActionTypesStr(actionTypesStr);
return checkNotNull(auditLogService.findAuditLogsByTenantIdAndCustomerId(tenantId, new CustomerId(UUID.fromString(strCustomerId)), actionTypes, pageLink));
} catch (Exception e) {
@ -69,22 +67,20 @@ public class AuditLogController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs/user/{userId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/audit/logs/user/{userId}", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<AuditLog> getAuditLogsByUserId(
public TimePageData<AuditLog> getAuditLogsByUserId(
@PathVariable("userId") String strUserId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset,
@RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
try {
checkParameter("UserId", strUserId);
TenantId tenantId = getCurrentUser().getTenantId();
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
List<ActionType> actionTypes = parseActionTypesStr(actionTypesStr);
return checkNotNull(auditLogService.findAuditLogsByTenantIdAndUserId(tenantId, new UserId(UUID.fromString(strUserId)), actionTypes, pageLink));
} catch (Exception e) {
@ -93,24 +89,22 @@ public class AuditLogController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<AuditLog> getAuditLogsByEntityId(
public TimePageData<AuditLog> getAuditLogsByEntityId(
@PathVariable("entityType") String strEntityType,
@PathVariable("entityId") String strEntityId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset,
@RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
try {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
TenantId tenantId = getCurrentUser().getTenantId();
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
List<ActionType> actionTypes = parseActionTypesStr(actionTypesStr);
return checkNotNull(auditLogService.findAuditLogsByTenantIdAndEntityId(tenantId, EntityIdFactory.getByTypeAndId(strEntityType, strEntityId), actionTypes, pageLink));
} catch (Exception e) {
@ -119,21 +113,19 @@ public class AuditLogController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/audit/logs", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<AuditLog> getAuditLogs(
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
public TimePageData<AuditLog> getAuditLogs(
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset,
@RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
List<ActionType> actionTypes = parseActionTypesStr(actionTypesStr);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
return checkNotNull(auditLogService.findAuditLogsByTenantId(tenantId, actionTypes, pageLink));
} catch (Exception e) {
throw handleException(e);

72
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -26,8 +26,6 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
@ -43,7 +41,6 @@ import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -62,8 +59,7 @@ import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
@ -268,27 +264,21 @@ public abstract class BaseController {
return UUID.fromString(id);
}
PageLink createPageLink(int pageSize, int page, String textSearch, String sortProperty, String sortOrder) throws ThingsboardException {
if (!StringUtils.isEmpty(sortProperty)) {
SortOrder.Direction direction = SortOrder.Direction.ASC;
if (!StringUtils.isEmpty(sortOrder)) {
try {
direction = SortOrder.Direction.valueOf(sortOrder.toUpperCase());
} catch (IllegalArgumentException e) {
throw new ThingsboardException("Unsupported sort order '" + sortOrder + "'! Only 'ASC' or 'DESC' types are allowed.", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
}
SortOrder sort = new SortOrder(sortProperty, direction);
return new PageLink(pageSize, page, textSearch, sort);
} else {
return new PageLink(pageSize, page, textSearch);
TimePageLink createPageLink(int limit, Long startTime, Long endTime, boolean ascOrder, String idOffset) {
UUID idOffsetUuid = null;
if (StringUtils.isNotEmpty(idOffset)) {
idOffsetUuid = toUUID(idOffset);
}
return new TimePageLink(limit, startTime, endTime, ascOrder, idOffsetUuid);
}
TimePageLink createTimePageLink(int pageSize, int page, String textSearch,
String sortProperty, String sortOrder, Long startTime, Long endTime) throws ThingsboardException {
PageLink pageLink = this.createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return new TimePageLink(pageLink, startTime, endTime);
TextPageLink createPageLink(int limit, String textSearch, String idOffset, String textOffset) {
UUID idOffsetUuid = null;
if (StringUtils.isNotEmpty(idOffset)) {
idOffsetUuid = toUUID(idOffset);
}
return new TextPageLink(limit, textSearch, idOffsetUuid, textOffset);
}
protected SecurityUser getCurrentUser() throws ThingsboardException {
@ -392,18 +382,6 @@ public abstract class BaseController {
}
}
DeviceInfo checkDeviceInfoId(DeviceId deviceId, Operation operation) throws ThingsboardException {
try {
validateId(deviceId, "Incorrect deviceId " + deviceId);
DeviceInfo device = deviceService.findDeviceInfoById(getCurrentUser().getTenantId(), deviceId);
checkNotNull(device);
accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, operation, deviceId, device);
return device;
} catch (Exception e) {
throw handleException(e, false);
}
}
protected EntityView checkEntityViewId(EntityViewId entityViewId, Operation operation) throws ThingsboardException {
try {
validateId(entityViewId, "Incorrect entityViewId " + entityViewId);
@ -416,18 +394,6 @@ public abstract class BaseController {
}
}
EntityViewInfo checkEntityViewInfoId(EntityViewId entityViewId, Operation operation) throws ThingsboardException {
try {
validateId(entityViewId, "Incorrect entityViewId " + entityViewId);
EntityViewInfo entityView = entityViewService.findEntityViewInfoById(getCurrentUser().getTenantId(), entityViewId);
checkNotNull(entityView);
accessControlService.checkPermission(getCurrentUser(), Resource.ENTITY_VIEW, operation, entityViewId, entityView);
return entityView;
} catch (Exception e) {
throw handleException(e, false);
}
}
Asset checkAssetId(AssetId assetId, Operation operation) throws ThingsboardException {
try {
validateId(assetId, "Incorrect assetId " + assetId);
@ -440,18 +406,6 @@ public abstract class BaseController {
}
}
AssetInfo checkAssetInfoId(AssetId assetId, Operation operation) throws ThingsboardException {
try {
validateId(assetId, "Incorrect assetId " + assetId);
AssetInfo asset = assetService.findAssetInfoById(getCurrentUser().getTenantId(), assetId);
checkNotNull(asset);
accessControlService.checkPermission(getCurrentUser(), Resource.ASSET, operation, assetId, asset);
return asset;
} catch (Exception e) {
throw handleException(e, false);
}
}
Alarm checkAlarmId(AlarmId alarmId, Operation operation) throws ThingsboardException {
try {
validateId(alarmId, "Incorrect alarmId " + alarmId);

17
application/src/main/java/org/thingsboard/server/controller/CustomerController.java

@ -34,8 +34,8 @@ import org.thingsboard.server.common.data.audit.ActionType;
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 org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@ -145,15 +145,14 @@ public class CustomerController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customers", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/customers", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<Customer> getCustomers(@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
public TextPageData<Customer> getCustomers(@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(customerService.findCustomersByTenantId(tenantId, pageLink));
} catch (Exception e) {

49
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -36,8 +36,9 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
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.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
@ -417,19 +418,18 @@ public class DashboardController extends BaseController {
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenant/{tenantId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/tenant/{tenantId}/dashboards", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public PageData<DashboardInfo> getTenantDashboards(
public TextPageData<DashboardInfo> getTenantDashboards(
@PathVariable("tenantId") String strTenantId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
checkTenantId(tenantId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
@ -437,17 +437,16 @@ public class DashboardController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/tenant/dashboards", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public PageData<DashboardInfo> getTenantDashboards(
@RequestParam int pageSize,
@RequestParam int page,
public TextPageData<DashboardInfo> getTenantDashboards(
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(dashboardService.findDashboardsByTenantId(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
@ -455,22 +454,22 @@ public class DashboardController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/customer/{customerId}/dashboards", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public PageData<DashboardInfo> getCustomerDashboards(
public TimePageData<DashboardInfo> getCustomerDashboards(
@PathVariable("customerId") String strCustomerId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(dashboardService.findDashboardsByTenantIdAndCustomerId(tenantId, customerId, pageLink).get());
} catch (Exception e) {
throw handleException(e);
}

94
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -37,7 +37,6 @@ import org.thingsboard.server.common.data.ClaimRequest;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
@ -46,8 +45,8 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
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.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.device.claim.ClaimResponse;
import org.thingsboard.server.dao.device.claim.ClaimResult;
@ -85,19 +84,6 @@ public class DeviceController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/info/{deviceId}", method = RequestMethod.GET)
@ResponseBody
public DeviceInfo getDeviceInfoById(@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(DEVICE_ID, strDeviceId);
try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
return checkDeviceInfoId(deviceId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device", method = RequestMethod.POST)
@ResponseBody
@ -283,18 +269,17 @@ public class DeviceController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/tenant/devices", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<Device> getTenantDevices(
@RequestParam int pageSize,
@RequestParam int page,
public TextPageData<Device> getTenantDevices(
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length() > 0) {
return checkNotNull(deviceService.findDevicesByTenantIdAndType(tenantId, type, pageLink));
} else {
@ -305,29 +290,6 @@ public class DeviceController extends BaseController {
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/deviceInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<DeviceInfo> getTenantDeviceInfos(
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
if (type != null && type.trim().length() > 0) {
return checkNotNull(deviceService.findDeviceInfosByTenantIdAndType(tenantId, type, pageLink));
} else {
return checkNotNull(deviceService.findDeviceInfosByTenantId(tenantId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/devices", params = {"deviceName"}, method = RequestMethod.GET)
@ResponseBody
@ -342,22 +304,21 @@ public class DeviceController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/customer/{customerId}/devices", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<Device> getCustomerDevices(
public TextPageData<Device> getCustomerDevices(
@PathVariable("customerId") String strCustomerId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length() > 0) {
return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
} else {
@ -368,33 +329,6 @@ public class DeviceController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/deviceInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<DeviceInfo> getCustomerDeviceInfos(
@PathVariable("customerId") String strCustomerId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
if (type != null && type.trim().length() > 0) {
return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
} else {
return checkNotNull(deviceService.findDeviceInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/devices", params = {"deviceIds"}, method = RequestMethod.GET)
@ResponseBody

99
application/src/main/java/org/thingsboard/server/controller/EntityViewController.java

@ -30,7 +30,11 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery;
import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -40,8 +44,8 @@ import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -81,19 +85,6 @@ public class EntityViewController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/entityView/info/{entityViewId}", method = RequestMethod.GET)
@ResponseBody
public EntityViewInfo getEntityViewInfoById(@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
try {
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
return checkEntityViewInfoId(entityViewId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/entityView", method = RequestMethod.POST)
@ResponseBody
@ -268,22 +259,21 @@ public class EntityViewController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/customer/{customerId}/entityViews", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<EntityView> getCustomerEntityViews(
public TextPageData<EntityView> getCustomerEntityViews(
@PathVariable("customerId") String strCustomerId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length() > 0) {
return checkNotNull(entityViewService.findEntityViewsByTenantIdAndCustomerIdAndType(tenantId, customerId, pageLink, type));
} else {
@ -294,46 +284,18 @@ public class EntityViewController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/entityViewInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<EntityViewInfo> getCustomerEntityViewInfos(
@PathVariable("customerId") String strCustomerId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
TenantId tenantId = getCurrentUser().getTenantId();
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
if (type != null && type.trim().length() > 0) {
return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
} else {
return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/tenant/entityViews", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<EntityView> getTenantEntityViews(
@RequestParam int pageSize,
@RequestParam int page,
public TextPageData<EntityView> getTenantEntityViews(
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (type != null && type.trim().length() > 0) {
return checkNotNull(entityViewService.findEntityViewByTenantIdAndType(tenantId, pageLink, type));
@ -345,29 +307,6 @@ public class EntityViewController extends BaseController {
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/entityViewInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData<EntityViewInfo> getTenantEntityViewInfos(
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
if (type != null && type.trim().length() > 0) {
return checkNotNull(entityViewService.findEntityViewInfosByTenantIdAndType(tenantId, type, pageLink));
} else {
return checkNotNull(entityViewService.findEntityViewInfosByTenantId(tenantId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/entityViews", method = RequestMethod.POST)
@ResponseBody

34
application/src/main/java/org/thingsboard/server/controller/EventController.java

@ -28,7 +28,7 @@ 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.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -45,18 +45,17 @@ public class EventController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET)
@ResponseBody
public PageData<Event> getEvents(
public TimePageData<Event> getEvents(
@PathVariable("entityType") String strEntityType,
@PathVariable("entityId") String strEntityId,
@PathVariable("eventType") String eventType,
@RequestParam("tenantId") String strTenantId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime) throws ThingsboardException {
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset
) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
try {
@ -64,7 +63,8 @@ public class EventController extends BaseController {
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
checkEntityId(entityId, Operation.READ);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(eventService.findEvents(tenantId, entityId, eventType, pageLink));
} catch (Exception e) {
throw handleException(e);
@ -74,17 +74,16 @@ public class EventController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public PageData<Event> getEvents(
public TimePageData<Event> getEvents(
@PathVariable("entityType") String strEntityType,
@PathVariable("entityId") String strEntityId,
@RequestParam("tenantId") String strTenantId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime) throws ThingsboardException {
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset
) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
try {
@ -93,8 +92,7 @@ public class EventController extends BaseController {
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
checkEntityId(entityId, Operation.READ);
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime);
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(eventService.findEvents(tenantId, entityId, pageLink));
} catch (Exception e) {
throw handleException(e);

18
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -44,8 +45,8 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
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.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
@ -222,17 +223,16 @@ public class RuleChainController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/ruleChains", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<RuleChain> getRuleChains(
@RequestParam int pageSize,
@RequestParam int page,
public TextPageData<RuleChain> getRuleChains(
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(ruleChainService.findTenantRuleChains(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);

17
application/src/main/java/org/thingsboard/server/controller/TenantController.java

@ -30,8 +30,8 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.exception.ThingsboardException;
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.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -102,15 +102,14 @@ public class TenantController extends BaseController {
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenants", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/tenants", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public PageData<Tenant> getTenants(@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
public TextPageData<Tenant> getTenants(@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(tenantService.findTenants(pageLink));
} catch (Exception e) {
throw handleException(e);

30
application/src/main/java/org/thingsboard/server/controller/UserController.java

@ -40,8 +40,8 @@ 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 org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -250,19 +250,18 @@ public class UserController extends BaseController {
}
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenant/{tenantId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/tenant/{tenantId}/users", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public PageData<User> getTenantAdmins(
public TextPageData<User> getTenantAdmins(
@PathVariable("tenantId") String strTenantId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("tenantId", strTenantId);
try {
TenantId tenantId = new TenantId(toUUID(strTenantId));
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(userService.findTenantAdmins(tenantId, pageLink));
} catch (Exception e) {
throw handleException(e);
@ -270,20 +269,19 @@ public class UserController extends BaseController {
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/users", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/customer/{customerId}/users", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public PageData<User> getCustomerUsers(
public TextPageData<User> getCustomerUsers(
@PathVariable("customerId") String strCustomerId,
@RequestParam int pageSize,
@RequestParam int page,
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
try {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(userService.findCustomerUsers(tenantId, customerId, pageLink));
} catch (Exception e) {

17
application/src/main/java/org/thingsboard/server/controller/WidgetsBundleController.java

@ -28,8 +28,8 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -93,16 +93,15 @@ public class WidgetsBundleController extends BaseController {
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/widgetsBundles", params = {"pageSize", "page"}, method = RequestMethod.GET)
@RequestMapping(value = "/widgetsBundles", params = { "limit" }, method = RequestMethod.GET)
@ResponseBody
public PageData<WidgetsBundle> getWidgetsBundles(
@RequestParam int pageSize,
@RequestParam int page,
public TextPageData<WidgetsBundle> getWidgetsBundles(
@RequestParam int limit,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
if (getCurrentUser().getAuthority() == Authority.SYS_ADMIN) {
return checkNotNull(widgetsBundleService.findSystemWidgetsBundlesByPageLink(getTenantId(), pageLink));
} else {

127
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -28,7 +28,6 @@ import org.thingsboard.server.service.install.DatabaseTsUpgradeService;
import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
import org.thingsboard.server.service.install.SystemDataLoaderService;
import org.thingsboard.server.service.install.TsDatabaseSchemaService;
import org.thingsboard.server.service.install.migrate.EntitiesMigrateService;
import org.thingsboard.server.service.install.update.DataUpdateService;
@Service
@ -69,99 +68,99 @@ public class ThingsboardInstallService {
@Autowired
private DataUpdateService dataUpdateService;
@Autowired(required = false)
private EntitiesMigrateService entitiesMigrateService;
public void performInstall() {
try {
if (isUpgrade) {
log.info("Starting ThingsBoard Upgrade from version {} ...", upgradeFromVersion);
if ("2.5.0-cassandra".equals(upgradeFromVersion)) {
log.info("Migrating ThingsBoard entities data from cassandra to SQL database ...");
entitiesMigrateService.migrate();
log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets();
} else {
switch (upgradeFromVersion) {
case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
log.info("Upgrading ThingsBoard from version 1.2.3 to 1.3.0 ...");
switch (upgradeFromVersion) {
case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
log.info("Upgrading ThingsBoard from version 1.2.3 to 1.3.0 ...");
databaseEntitiesUpgradeService.upgradeDatabase("1.2.3");
case "1.3.0": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
log.info("Upgrading ThingsBoard from version 1.3.0 to 1.3.1 ...");
databaseEntitiesUpgradeService.upgradeDatabase("1.2.3");
databaseEntitiesUpgradeService.upgradeDatabase("1.3.0");
case "1.3.0": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
log.info("Upgrading ThingsBoard from version 1.3.0 to 1.3.1 ...");
case "1.3.1": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
log.info("Upgrading ThingsBoard from version 1.3.1 to 1.4.0 ...");
databaseEntitiesUpgradeService.upgradeDatabase("1.3.0");
databaseEntitiesUpgradeService.upgradeDatabase("1.3.1");
case "1.3.1": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
log.info("Upgrading ThingsBoard from version 1.3.1 to 1.4.0 ...");
case "1.4.0":
log.info("Upgrading ThingsBoard from version 1.4.0 to 2.0.0 ...");
databaseEntitiesUpgradeService.upgradeDatabase("1.3.1");
databaseEntitiesUpgradeService.upgradeDatabase("1.4.0");
case "1.4.0":
log.info("Upgrading ThingsBoard from version 1.4.0 to 2.0.0 ...");
dataUpdateService.updateData("1.4.0");
databaseEntitiesUpgradeService.upgradeDatabase("1.4.0");
case "2.0.0":
log.info("Upgrading ThingsBoard from version 2.0.0 to 2.1.1 ...");
dataUpdateService.updateData("1.4.0");
databaseEntitiesUpgradeService.upgradeDatabase("2.0.0");
case "2.0.0":
log.info("Upgrading ThingsBoard from version 2.0.0 to 2.1.1 ...");
case "2.1.1":
log.info("Upgrading ThingsBoard from version 2.1.1 to 2.1.2 ...");
databaseEntitiesUpgradeService.upgradeDatabase("2.0.0");
databaseEntitiesUpgradeService.upgradeDatabase("2.1.1");
case "2.1.3":
log.info("Upgrading ThingsBoard from version 2.1.3 to 2.2.0 ...");
case "2.1.1":
log.info("Upgrading ThingsBoard from version 2.1.1 to 2.1.2 ...");
databaseEntitiesUpgradeService.upgradeDatabase("2.1.3");
databaseEntitiesUpgradeService.upgradeDatabase("2.1.1");
case "2.1.3":
log.info("Upgrading ThingsBoard from version 2.1.3 to 2.2.0 ...");
case "2.3.0":
log.info("Upgrading ThingsBoard from version 2.3.0 to 2.3.1 ...");
databaseEntitiesUpgradeService.upgradeDatabase("2.1.3");
databaseEntitiesUpgradeService.upgradeDatabase("2.3.0");
case "2.3.0":
log.info("Upgrading ThingsBoard from version 2.3.0 to 2.3.1 ...");
case "2.3.1":
log.info("Upgrading ThingsBoard from version 2.3.1 to 2.4.0 ...");
databaseEntitiesUpgradeService.upgradeDatabase("2.3.0");
databaseEntitiesUpgradeService.upgradeDatabase("2.3.1");
case "2.3.1":
log.info("Upgrading ThingsBoard from version 2.3.1 to 2.4.0 ...");
case "2.4.0":
log.info("Upgrading ThingsBoard from version 2.4.0 to 2.4.1 ...");
databaseEntitiesUpgradeService.upgradeDatabase("2.3.1");
case "2.4.1":
log.info("Upgrading ThingsBoard from version 2.4.1 to 2.4.2 ...");
case "2.4.0":
log.info("Upgrading ThingsBoard from version 2.4.0 to 2.4.1 ...");
databaseEntitiesUpgradeService.upgradeDatabase("2.4.1");
case "2.4.2":
log.info("Upgrading ThingsBoard from version 2.4.2 to 2.4.3 ...");
case "2.4.1":
log.info("Upgrading ThingsBoard from version 2.4.1 to 2.4.2 ...");
databaseEntitiesUpgradeService.upgradeDatabase("2.4.2");
databaseEntitiesUpgradeService.upgradeDatabase("2.4.1");
case "2.4.2":
log.info("Upgrading ThingsBoard from version 2.4.2 to 2.4.3 ...");
case "2.4.3":
log.info("Upgrading ThingsBoard from version 2.4.3 to 2.5 ...");
databaseEntitiesUpgradeService.upgradeDatabase("2.4.2");
if (databaseTsUpgradeService != null) {
databaseTsUpgradeService.upgradeDatabase("2.4.3");
}
databaseEntitiesUpgradeService.upgradeDatabase("2.4.3");
case "2.4.3":
log.info("Upgrading ThingsBoard from version 2.4.3 to 2.5 ...");
log.info("Updating system data...");
if (databaseTsUpgradeService != null) {
databaseTsUpgradeService.upgradeDatabase("2.4.3");
}
databaseEntitiesUpgradeService.upgradeDatabase("2.4.3");
systemDataLoaderService.deleteSystemWidgetBundle("charts");
systemDataLoaderService.deleteSystemWidgetBundle("cards");
systemDataLoaderService.deleteSystemWidgetBundle("maps");
systemDataLoaderService.deleteSystemWidgetBundle("analogue_gauges");
systemDataLoaderService.deleteSystemWidgetBundle("digital_gauges");
systemDataLoaderService.deleteSystemWidgetBundle("gpio_widgets");
systemDataLoaderService.deleteSystemWidgetBundle("alarm_widgets");
systemDataLoaderService.deleteSystemWidgetBundle("control_widgets");
systemDataLoaderService.deleteSystemWidgetBundle("maps_v2");
systemDataLoaderService.deleteSystemWidgetBundle("gateway_widgets");
systemDataLoaderService.deleteSystemWidgetBundle("input_widgets");
systemDataLoaderService.deleteSystemWidgetBundle("date");
systemDataLoaderService.deleteSystemWidgetBundle("entity_admin_widgets");
log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets();
break;
case "2.5.0":
log.info("Upgrading ThingsBoard from version 2.5 to 3.0 ...");
log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets();
break;
default:
throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion);
systemDataLoaderService.loadSystemWidgets();
break;
default:
throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion);
}
}
log.info("Upgrade finished successfully!");

8
application/src/main/java/org/thingsboard/server/service/install/CassandraAbstractDatabaseSchemaService.java

@ -45,18 +45,10 @@ public abstract class CassandraAbstractDatabaseSchemaService implements Database
@Override
public void createDatabaseSchema() throws Exception {
this.createDatabaseSchema(true);
}
@Override
public void createDatabaseSchema(boolean createIndexes) throws Exception {
log.info("Installing Cassandra DataBase schema part: " + schemaCql);
Path schemaFile = Paths.get(installScripts.getDataDir(), CASSANDRA_DIR, schemaCql);
loadCql(schemaFile);
}
@Override
public void createDatabaseIndexes() throws Exception {
}
private void loadCql(Path cql) throws Exception {

314
application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java

@ -0,0 +1,314 @@
/**
* Copyright © 2016-2020 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.install;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.exceptions.InvalidQueryException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.util.NoSqlDao;
import org.thingsboard.server.service.install.cql.CassandraDbHelper;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO;
import static org.thingsboard.server.service.install.DatabaseHelper.ASSET;
import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS;
import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION;
import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID;
import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD;
import static org.thingsboard.server.service.install.DatabaseHelper.DEVICE;
import static org.thingsboard.server.service.install.DatabaseHelper.END_TS;
import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_ID;
import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_TYPE;
import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEW;
import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEWS;
import static org.thingsboard.server.service.install.DatabaseHelper.ID;
import static org.thingsboard.server.service.install.DatabaseHelper.KEYS;
import static org.thingsboard.server.service.install.DatabaseHelper.NAME;
import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT;
import static org.thingsboard.server.service.install.DatabaseHelper.START_TS;
import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID;
import static org.thingsboard.server.service.install.DatabaseHelper.TITLE;
import static org.thingsboard.server.service.install.DatabaseHelper.TYPE;
@Service
@NoSqlDao
@Profile("install")
@Slf4j
public class CassandraDatabaseUpgradeService extends AbstractCassandraDatabaseUpgradeService implements DatabaseEntitiesUpgradeService {
private static final String SCHEMA_UPDATE_CQL = "schema_update.cql";
@Autowired
private DashboardService dashboardService;
@Autowired
private InstallScripts installScripts;
@Override
public void upgradeDatabase(String fromVersion) throws Exception {
switch (fromVersion) {
case "1.2.3":
log.info("Upgrading Cassandara DataBase from version {} to 1.3.0 ...", fromVersion);
//Dump devices, assets and relations
cluster.getSession();
KeyspaceMetadata ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName());
log.info("Dumping devices ...");
Path devicesDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), DEVICE,
new String[]{"id", TENANT_ID, CUSTOMER_ID, "name", SEARCH_TEXT, ADDITIONAL_INFO, "type"},
new String[]{"", "", "", "", "", "", "default"},
"tb-devices");
log.info("Devices dumped.");
log.info("Dumping assets ...");
Path assetsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), ASSET,
new String[]{"id", TENANT_ID, CUSTOMER_ID, "name", SEARCH_TEXT, ADDITIONAL_INFO, "type"},
new String[]{"", "", "", "", "", "", "default"},
"tb-assets");
log.info("Assets dumped.");
log.info("Dumping relations ...");
Path relationsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), "relation",
new String[]{"from_id", "from_type", "to_id", "to_type", "relation_type", ADDITIONAL_INFO, "relation_type_group"},
new String[]{"", "", "", "", "", "", "COMMON"},
"tb-relations");
log.info("Relations dumped.");
log.info("Updating schema ...");
Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.3.0", SCHEMA_UPDATE_CQL);
loadCql(schemaUpdateFile);
log.info("Schema updated.");
//Restore devices, assets and relations
log.info("Restoring devices ...");
if (devicesDump != null) {
CassandraDbHelper.loadCf(ks, cluster.getSession(), DEVICE,
new String[]{"id", TENANT_ID, CUSTOMER_ID, "name", SEARCH_TEXT, ADDITIONAL_INFO, "type"}, devicesDump);
Files.deleteIfExists(devicesDump);
}
log.info("Devices restored.");
log.info("Dumping device types ...");
Path deviceTypesDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), DEVICE,
new String[]{TENANT_ID, "type"},
new String[]{"", ""},
"tb-device-types");
if (deviceTypesDump != null) {
CassandraDbHelper.appendToEndOfLine(deviceTypesDump, "DEVICE");
}
log.info("Device types dumped.");
log.info("Loading device types ...");
if (deviceTypesDump != null) {
CassandraDbHelper.loadCf(ks, cluster.getSession(), "entity_subtype",
new String[]{TENANT_ID, "type", "entity_type"}, deviceTypesDump);
Files.deleteIfExists(deviceTypesDump);
}
log.info("Device types loaded.");
log.info("Restoring assets ...");
if (assetsDump != null) {
CassandraDbHelper.loadCf(ks, cluster.getSession(), ASSET,
new String[]{"id", TENANT_ID, CUSTOMER_ID, "name", SEARCH_TEXT, ADDITIONAL_INFO, "type"}, assetsDump);
Files.deleteIfExists(assetsDump);
}
log.info("Assets restored.");
log.info("Dumping asset types ...");
Path assetTypesDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), ASSET,
new String[]{TENANT_ID, "type"},
new String[]{"", ""},
"tb-asset-types");
if (assetTypesDump != null) {
CassandraDbHelper.appendToEndOfLine(assetTypesDump, "ASSET");
}
log.info("Asset types dumped.");
log.info("Loading asset types ...");
if (assetTypesDump != null) {
CassandraDbHelper.loadCf(ks, cluster.getSession(), "entity_subtype",
new String[]{TENANT_ID, "type", "entity_type"}, assetTypesDump);
Files.deleteIfExists(assetTypesDump);
}
log.info("Asset types loaded.");
log.info("Restoring relations ...");
if (relationsDump != null) {
CassandraDbHelper.loadCf(ks, cluster.getSession(), "relation",
new String[]{"from_id", "from_type", "to_id", "to_type", "relation_type", ADDITIONAL_INFO, "relation_type_group"}, relationsDump);
Files.deleteIfExists(relationsDump);
}
log.info("Relations restored.");
break;
case "1.3.0":
break;
case "1.3.1":
cluster.getSession();
ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName());
log.info("Dumping dashboards ...");
Path dashboardsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), DASHBOARD,
new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION},
new String[]{"", "", "", "", "", "", ""},
"tb-dashboards", true);
log.info("Dashboards dumped.");
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.4.0", SCHEMA_UPDATE_CQL);
loadCql(schemaUpdateFile);
log.info("Schema updated.");
log.info("Restoring dashboards ...");
if (dashboardsDump != null) {
CassandraDbHelper.loadCf(ks, cluster.getSession(), DASHBOARD,
new String[]{ID, TENANT_ID, TITLE, SEARCH_TEXT, CONFIGURATION}, dashboardsDump, true);
DatabaseHelper.upgradeTo40_assignDashboards(dashboardsDump, dashboardService, false);
Files.deleteIfExists(dashboardsDump);
}
log.info("Dashboards restored.");
break;
case "1.4.0":
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.0.0", SCHEMA_UPDATE_CQL);
loadCql(schemaUpdateFile);
log.info("Schema updated.");
break;
case "2.0.0":
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.1", SCHEMA_UPDATE_CQL);
loadCql(schemaUpdateFile);
log.info("Schema updated.");
break;
case "2.1.1":
log.info("Upgrading Cassandra DataBase from version {} to 2.1.2 ...", fromVersion);
cluster.getSession();
ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName());
log.info("Dumping entity views ...");
Path entityViewsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), ENTITY_VIEWS,
new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, NAME, TYPE, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO},
new String[]{"", "", "", "", "", "", "default", "", "0", "0", "", ""},
"tb-entity-views");
log.info("Entity views dumped.");
log.info("Updating schema ...");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.2", SCHEMA_UPDATE_CQL);
loadCql(schemaUpdateFile);
log.info("Schema updated.");
log.info("Restoring entity views ...");
if (entityViewsDump != null) {
CassandraDbHelper.loadCf(ks, cluster.getSession(), ENTITY_VIEW,
new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, NAME, TYPE, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, entityViewsDump);
Files.deleteIfExists(entityViewsDump);
}
log.info("Entity views restored.");
break;
case "2.1.3":
break;
case "2.3.0":
break;
case "2.3.1":
log.info("Updating schema ...");
String updateDeviceTableStmt = "alter table device add label text";
try {
cluster.getSession().execute(updateDeviceTableStmt);
Thread.sleep(2500);
} catch (InvalidQueryException e) {
}
log.info("Schema updated.");
break;
case "2.4.1":
log.info("Updating schema ...");
String updateAssetTableStmt = "alter table asset add label text";
try {
log.info("Updating assets ...");
cluster.getSession().execute(updateAssetTableStmt);
Thread.sleep(2500);
log.info("Assets updated.");
} catch (InvalidQueryException e) {
}
log.info("Schema updated.");
break;
case "2.4.2":
log.info("Updating schema ...");
String updateAlarmTableStmt = "alter table alarm add propagate_relation_types text";
try {
log.info("Updating alarms ...");
cluster.getSession().execute(updateAlarmTableStmt);
Thread.sleep(2500);
log.info("Alarms updated.");
} catch (InvalidQueryException e) {
}
log.info("Schema updated.");
break;
case "2.4.3":
log.info("Updating schema ...");
String updateAttributeKvTableStmt = "alter table attributes_kv_cf add json_v text";
try {
log.info("Updating attributes ...");
cluster.getSession().execute(updateAttributeKvTableStmt);
Thread.sleep(2500);
log.info("Attributes updated.");
} catch (InvalidQueryException e) {
}
String updateTenantCoreTableStmt = "alter table tenant add isolated_tb_core boolean";
String updateTenantRuleEngineTableStmt = "alter table tenant add isolated_tb_rule_engine boolean";
try {
log.info("Updating tenant...");
cluster.getSession().execute(updateTenantCoreTableStmt);
Thread.sleep(2500);
cluster.getSession().execute(updateTenantRuleEngineTableStmt);
Thread.sleep(2500);
log.info("Tenant updated.");
} catch (InvalidQueryException e) {
}
log.info("Schema updated.");
break;
default:
throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion);
}
}
}

28
ui-ngx/src/app/modules/home/components/entity/entity-filter-view.component.scss → application/src/main/java/org/thingsboard/server/service/install/CassandraEntityDatabaseSchemaService.java

@ -13,24 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
.tb-entity-filter-view {
.entity-filter-empty {
font-size: 14px;
line-height: 16px;
color: rgba(221, 44, 0, .87);
}
package org.thingsboard.server.service.install;
.entity-filter-type {
font-size: 14px;
line-height: 16px;
color: rgba(0, 0, 0, .570588);
}
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.util.NoSqlDao;
.entity-filter-value {
font-size: 14px;
line-height: 16px;
color: rgba(0, 0, 0, .570588);
@Service
@NoSqlDao
@Profile("install")
public class CassandraEntityDatabaseSchemaService extends CassandraAbstractDatabaseSchemaService
implements EntityDatabaseSchemaService {
public CassandraEntityDatabaseSchemaService() {
super("schema-entities.cql");
}
}
}

2
application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java

@ -15,7 +15,7 @@
*/
package org.thingsboard.server.service.install;
import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException;
import com.datastax.driver.core.exceptions.InvalidQueryException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

4
application/src/main/java/org/thingsboard/server/service/install/DatabaseSchemaService.java

@ -19,8 +19,4 @@ public interface DatabaseSchemaService {
void createDatabaseSchema() throws Exception;
void createDatabaseSchema(boolean createIndexes) throws Exception;
void createDatabaseIndexes() throws Exception;
}

18
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -217,24 +217,6 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
installScripts.loadSystemWidgets();
}
@Override
public void updateSystemWidgets() throws Exception {
this.deleteSystemWidgetBundle("charts");
this.deleteSystemWidgetBundle("cards");
this.deleteSystemWidgetBundle("maps");
this.deleteSystemWidgetBundle("analogue_gauges");
this.deleteSystemWidgetBundle("digital_gauges");
this.deleteSystemWidgetBundle("gpio_widgets");
this.deleteSystemWidgetBundle("alarm_widgets");
this.deleteSystemWidgetBundle("control_widgets");
this.deleteSystemWidgetBundle("maps_v2");
this.deleteSystemWidgetBundle("gateway_widgets");
this.deleteSystemWidgetBundle("input_widgets");
this.deleteSystemWidgetBundle("date");
this.deleteSystemWidgetBundle("entity_admin_widgets");
installScripts.loadSystemWidgets();
}
private User createUser(Authority authority,
TenantId tenantId,
CustomerId customerId,

13
application/src/main/java/org/thingsboard/server/service/install/SqlAbstractDatabaseSchemaService.java

@ -54,11 +54,6 @@ public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchema
@Override
public void createDatabaseSchema() throws Exception {
this.createDatabaseSchema(true);
}
@Override
public void createDatabaseSchema(boolean createIndexes) throws Exception {
log.info("Installing SQL DataBase schema part: " + schemaSql);
@ -68,15 +63,9 @@ public abstract class SqlAbstractDatabaseSchemaService implements DatabaseSchema
conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to load initial thingsboard database schema
}
if (createIndexes) {
this.createDatabaseIndexes();
}
}
@Override
public void createDatabaseIndexes() throws Exception {
if (schemaIdxSql != null) {
log.info("Installing SQL DataBase schema indexes part: " + schemaIdxSql);
Path schemaIdxFile = Paths.get(installScripts.getDataDir(), SQL_DIR, schemaIdxSql);
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
String sql = new String(Files.readAllBytes(schemaIdxFile), Charset.forName("UTF-8"));

2
application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java

@ -23,8 +23,6 @@ public interface SystemDataLoaderService {
void loadSystemWidgets() throws Exception;
void updateSystemWidgets() throws Exception;
void loadDemoData() throws Exception;
void deleteSystemWidgetBundle(String bundleAlias) throws Exception;

102
application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java

@ -15,27 +15,25 @@
*/
package org.thingsboard.server.service.install.cql;
import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.metadata.schema.KeyspaceMetadata;
import com.datastax.oss.driver.api.core.metadata.schema.TableMetadata;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.protocol.internal.ProtocolConstants;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.TableMetadata;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.csv.CSVRecord;
import org.thingsboard.server.dao.cassandra.guava.GuavaSession;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
@ -46,12 +44,12 @@ import static org.thingsboard.server.service.install.DatabaseHelper.CSV_DUMP_FOR
public class CassandraDbHelper {
public static Path dumpCfIfExists(KeyspaceMetadata ks, GuavaSession session, String cfName,
public static Path dumpCfIfExists(KeyspaceMetadata ks, Session session, String cfName,
String[] columns, String[] defaultValues, String dumpPrefix) throws Exception {
return dumpCfIfExists(ks, session, cfName, columns, defaultValues, dumpPrefix, false);
}
public static Path dumpCfIfExists(KeyspaceMetadata ks, GuavaSession session, String cfName,
public static Path dumpCfIfExists(KeyspaceMetadata ks, Session session, String cfName,
String[] columns, String[] defaultValues, String dumpPrefix, boolean printHeader) throws Exception {
if (ks.getTable(cfName) != null) {
Path dumpFile = Files.createTempFile(dumpPrefix, null);
@ -61,8 +59,8 @@ public class CassandraDbHelper {
csvFormat = csvFormat.withHeader(columns);
}
try (CSVPrinter csvPrinter = new CSVPrinter(Files.newBufferedWriter(dumpFile), csvFormat)) {
Statement stmt = SimpleStatement.newInstance("SELECT * FROM " + cfName);
stmt.setPageSize(1000);
Statement stmt = new SimpleStatement("SELECT * FROM " + cfName);
stmt.setFetchSize(1000);
ResultSet rs = session.execute(stmt);
Iterator<Row> iter = rs.iterator();
while (iter.hasNext()) {
@ -97,12 +95,12 @@ public class CassandraDbHelper {
Files.move(tmp, targetDumpFile, StandardCopyOption.REPLACE_EXISTING);
}
public static void loadCf(KeyspaceMetadata ks, GuavaSession session, String cfName, String[] columns, Path sourceFile) throws Exception {
public static void loadCf(KeyspaceMetadata ks, Session session, String cfName, String[] columns, Path sourceFile) throws Exception {
loadCf(ks, session, cfName, columns, sourceFile, false);
}
public static void loadCf(KeyspaceMetadata ks, GuavaSession session, String cfName, String[] columns, Path sourceFile, boolean parseHeader) throws Exception {
TableMetadata tableMetadata = ks.getTable(cfName).get();
public static void loadCf(KeyspaceMetadata ks, Session session, String cfName, String[] columns, Path sourceFile, boolean parseHeader) throws Exception {
TableMetadata tableMetadata = ks.getTable(cfName);
PreparedStatement prepared = session.prepare(createInsertStatement(cfName, columns));
CSVFormat csvFormat = CSV_DUMP_FORMAT;
if (parseHeader) {
@ -112,11 +110,11 @@ public class CassandraDbHelper {
}
try (CSVParser csvParser = new CSVParser(Files.newBufferedReader(sourceFile), csvFormat)) {
csvParser.forEach(record -> {
BoundStatementBuilder boundStatementBuilder = new BoundStatementBuilder(prepared.bind());
BoundStatement boundStatement = prepared.bind();
for (String column : columns) {
setColumnValue(tableMetadata, column, record, boundStatementBuilder);
setColumnValue(tableMetadata, column, record, boundStatement);
}
session.execute(boundStatementBuilder.build());
session.execute(boundStatement);
});
}
}
@ -138,27 +136,27 @@ public class CassandraDbHelper {
}
private static String getColumnValue(String column, String defaultValue, Row row) {
int index = row.getColumnDefinitions().firstIndexOf(column);
int index = row.getColumnDefinitions().getIndexOf(column);
if (index > -1) {
String str;
DataType type = row.getColumnDefinitions().get(index).getType();
DataType type = row.getColumnDefinitions().getType(index);
try {
if (row.isNull(index)) {
return null;
} else if (type.getProtocolCode() == ProtocolConstants.DataType.DOUBLE) {
} else if (type == DataType.cdouble()) {
str = new Double(row.getDouble(index)).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.INT) {
} else if (type == DataType.cint()) {
str = new Integer(row.getInt(index)).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.BIGINT) {
} else if (type == DataType.bigint()) {
str = new Long(row.getLong(index)).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) {
str = row.getUuid(index).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) {
str = row.getUuid(index).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.FLOAT) {
} else if (type == DataType.uuid()) {
str = row.getUUID(index).toString();
} else if (type == DataType.timeuuid()) {
str = row.getUUID(index).toString();
} else if (type == DataType.cfloat()) {
str = new Float(row.getFloat(index)).toString();
} else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) {
str = ""+row.getInstant(index).toEpochMilli();
} else if (type == DataType.timestamp()) {
str = ""+row.getTimestamp(index).getTime();
} else {
str = row.getString(index);
}
@ -188,27 +186,27 @@ public class CassandraDbHelper {
}
private static void setColumnValue(TableMetadata tableMetadata, String column,
CSVRecord record, BoundStatementBuilder boundStatementBuilder) {
CSVRecord record, BoundStatement boundStatement) {
String value = record.get(column);
DataType type = tableMetadata.getColumn(column).get().getType();
DataType type = tableMetadata.getColumn(column).getType();
if (value == null) {
boundStatementBuilder.setToNull(column);
} else if (type.getProtocolCode() == ProtocolConstants.DataType.DOUBLE) {
boundStatementBuilder.setDouble(column, Double.valueOf(value));
} else if (type.getProtocolCode() == ProtocolConstants.DataType.INT) {
boundStatementBuilder.setInt(column, Integer.valueOf(value));
} else if (type.getProtocolCode() == ProtocolConstants.DataType.BIGINT) {
boundStatementBuilder.setLong(column, Long.valueOf(value));
} else if (type.getProtocolCode() == ProtocolConstants.DataType.UUID) {
boundStatementBuilder.setUuid(column, UUID.fromString(value));
} else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMEUUID) {
boundStatementBuilder.setUuid(column, UUID.fromString(value));
} else if (type.getProtocolCode() == ProtocolConstants.DataType.FLOAT) {
boundStatementBuilder.setFloat(column, Float.valueOf(value));
} else if (type.getProtocolCode() == ProtocolConstants.DataType.TIMESTAMP) {
boundStatementBuilder.setInstant(column, Instant.ofEpochMilli(Long.valueOf(value)));
boundStatement.setToNull(column);
} else if (type == DataType.cdouble()) {
boundStatement.setDouble(column, Double.valueOf(value));
} else if (type == DataType.cint()) {
boundStatement.setInt(column, Integer.valueOf(value));
} else if (type == DataType.bigint()) {
boundStatement.setLong(column, Long.valueOf(value));
} else if (type == DataType.uuid()) {
boundStatement.setUUID(column, UUID.fromString(value));
} else if (type == DataType.timeuuid()) {
boundStatement.setUUID(column, UUID.fromString(value));
} else if (type == DataType.cfloat()) {
boundStatement.setFloat(column, Float.valueOf(value));
} else if (type == DataType.timestamp()) {
boundStatement.setTimestamp(column, new Date(Long.valueOf(value)));
} else {
boundStatementBuilder.setString(column, value);
boundStatement.setString(column, value);
}
}

329
application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraEntitiesToSqlMigrateService.java

@ -1,329 +0,0 @@
/**
* Copyright © 2016-2020 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.install.migrate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.dao.cassandra.CassandraCluster;
import org.thingsboard.server.dao.util.NoSqlAnyDao;
import org.thingsboard.server.dao.util.SqlDao;
import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Arrays;
import java.util.List;
import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.bigintColumn;
import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.booleanColumn;
import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.doubleColumn;
import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.enumToIntColumn;
import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.idColumn;
import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.jsonColumn;
import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.stringColumn;
@Service
@Profile("install")
@SqlDao
@NoSqlAnyDao
@Slf4j
public class CassandraEntitiesToSqlMigrateService implements EntitiesMigrateService {
@Autowired
private EntityDatabaseSchemaService entityDatabaseSchemaService;
@Autowired
protected CassandraCluster cluster;
@Value("${spring.datasource.url}")
protected String dbUrl;
@Value("${spring.datasource.username}")
protected String dbUserName;
@Value("${spring.datasource.password}")
protected String dbPassword;
@Override
public void migrate() throws Exception {
log.info("Performing migration of entities data from cassandra to SQL database ...");
entityDatabaseSchemaService.createDatabaseSchema(false);
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
conn.setAutoCommit(false);
for (CassandraToSqlTable table: tables) {
table.migrateToSql(cluster.getSession(), conn);
}
} catch (Exception e) {
log.error("Unexpected error during ThingsBoard entities data migration!", e);
throw e;
}
entityDatabaseSchemaService.createDatabaseIndexes();
}
private static List<CassandraToSqlTable> tables = Arrays.asList(
new CassandraToSqlTable("admin_settings",
idColumn("id"),
stringColumn("key"),
stringColumn("json_value")),
new CassandraToSqlTable("alarm",
idColumn("id"),
idColumn("tenant_id"),
stringColumn("type"),
idColumn("originator_id"),
enumToIntColumn("originator_type", EntityType.class),
stringColumn("severity"),
stringColumn("status"),
bigintColumn("start_ts"),
bigintColumn("end_ts"),
bigintColumn("ack_ts"),
bigintColumn("clear_ts"),
stringColumn("details", "additional_info"),
booleanColumn("propagate"),
stringColumn("propagate_relation_types")),
new CassandraToSqlTable("asset",
idColumn("id"),
idColumn("tenant_id"),
idColumn("customer_id"),
stringColumn("name"),
stringColumn("type"),
stringColumn("label"),
stringColumn("search_text"),
stringColumn("additional_info")) {
@Override
protected boolean onConstraintViolation(List<CassandraToSqlColumnData[]> batchData,
CassandraToSqlColumnData[] data, String constraint) {
if (constraint.equalsIgnoreCase("asset_name_unq_key")) {
this.handleUniqueNameViolation(data, "asset");
return true;
}
return super.onConstraintViolation(batchData, data, constraint);
}
},
new CassandraToSqlTable("audit_log_by_tenant_id", "audit_log",
idColumn("id"),
idColumn("tenant_id"),
idColumn("customer_id"),
idColumn("entity_id"),
stringColumn("entity_type"),
stringColumn("entity_name"),
idColumn("user_id"),
stringColumn("user_name"),
stringColumn("action_type"),
stringColumn("action_data"),
stringColumn("action_status"),
stringColumn("action_failure_details")),
new CassandraToSqlTable("attributes_kv_cf", "attribute_kv",
idColumn("entity_id"),
stringColumn("entity_type"),
stringColumn("attribute_type"),
stringColumn("attribute_key"),
booleanColumn("bool_v"),
stringColumn("str_v"),
bigintColumn("long_v"),
doubleColumn("dbl_v"),
jsonColumn("json_v"),
bigintColumn("last_update_ts")),
new CassandraToSqlTable("component_descriptor",
idColumn("id"),
stringColumn("type"),
stringColumn("scope"),
stringColumn("name"),
stringColumn("search_text"),
stringColumn("clazz"),
stringColumn("configuration_descriptor"),
stringColumn("actions")) {
@Override
protected boolean onConstraintViolation(List<CassandraToSqlColumnData[]> batchData,
CassandraToSqlColumnData[] data, String constraint) {
if (constraint.equalsIgnoreCase("component_descriptor_clazz_key")) {
String clazz = this.getColumnData(data, "clazz").getValue();
log.warn("Found component_descriptor record with duplicate clazz [{}]. Record will be ignored!", clazz);
this.ignoreRecord(batchData, data);
return true;
}
return super.onConstraintViolation(batchData, data, constraint);
}
},
new CassandraToSqlTable("customer",
idColumn("id"),
idColumn("tenant_id"),
stringColumn("title"),
stringColumn("search_text"),
stringColumn("country"),
stringColumn("state"),
stringColumn("city"),
stringColumn("address"),
stringColumn("address2"),
stringColumn("zip"),
stringColumn("phone"),
stringColumn("email"),
stringColumn("additional_info")),
new CassandraToSqlTable("dashboard",
idColumn("id"),
idColumn("tenant_id"),
stringColumn("title"),
stringColumn("search_text"),
stringColumn("assigned_customers"),
stringColumn("configuration")),
new CassandraToSqlTable("device",
idColumn("id"),
idColumn("tenant_id"),
idColumn("customer_id"),
stringColumn("name"),
stringColumn("type"),
stringColumn("label"),
stringColumn("search_text"),
stringColumn("additional_info")) {
@Override
protected boolean onConstraintViolation(List<CassandraToSqlColumnData[]> batchData,
CassandraToSqlColumnData[] data, String constraint) {
if (constraint.equalsIgnoreCase("device_name_unq_key")) {
this.handleUniqueNameViolation(data, "device");
return true;
}
return super.onConstraintViolation(batchData, data, constraint);
}
},
new CassandraToSqlTable("device_credentials",
idColumn("id"),
idColumn("device_id"),
stringColumn("credentials_type"),
stringColumn("credentials_id"),
stringColumn("credentials_value")),
new CassandraToSqlTable("event",
idColumn("id"),
idColumn("tenant_id"),
idColumn("entity_id"),
stringColumn("entity_type"),
stringColumn("event_type"),
stringColumn("event_uid"),
stringColumn("body"),
new CassandraToSqlEventTsColumn()),
new CassandraToSqlTable("relation",
idColumn("from_id"),
stringColumn("from_type"),
idColumn("to_id"),
stringColumn("to_type"),
stringColumn("relation_type_group"),
stringColumn("relation_type"),
stringColumn("additional_info")),
new CassandraToSqlTable("user", "tb_user",
idColumn("id"),
idColumn("tenant_id"),
idColumn("customer_id"),
stringColumn("email"),
stringColumn("search_text"),
stringColumn("authority"),
stringColumn("first_name"),
stringColumn("last_name"),
stringColumn("additional_info")) {
@Override
protected boolean onConstraintViolation(List<CassandraToSqlColumnData[]> batchData,
CassandraToSqlColumnData[] data, String constraint) {
if (constraint.equalsIgnoreCase("tb_user_email_key")) {
this.handleUniqueEmailViolation(data);
return true;
}
return super.onConstraintViolation(batchData, data, constraint);
}
},
new CassandraToSqlTable("tenant",
idColumn("id"),
stringColumn("title"),
stringColumn("search_text"),
stringColumn("region"),
stringColumn("country"),
stringColumn("state"),
stringColumn("city"),
stringColumn("address"),
stringColumn("address2"),
stringColumn("zip"),
stringColumn("phone"),
stringColumn("email"),
stringColumn("additional_info"),
booleanColumn("isolated_tb_core"),
booleanColumn("isolated_tb_rule_engine")),
new CassandraToSqlTable("user_credentials",
idColumn("id"),
idColumn("user_id"),
booleanColumn("enabled"),
stringColumn("password"),
stringColumn("activate_token"),
stringColumn("reset_token")) {
@Override
protected boolean onConstraintViolation(List<CassandraToSqlColumnData[]> batchData,
CassandraToSqlColumnData[] data, String constraint) {
if (constraint.equalsIgnoreCase("user_credentials_user_id_key")) {
String id = UUIDConverter.fromString(this.getColumnData(data, "id").getValue()).toString();
log.warn("Found user credentials record with duplicate user_id [id:[{}]]. Record will be ignored!", id);
this.ignoreRecord(batchData, data);
return true;
}
return super.onConstraintViolation(batchData, data, constraint);
}
},
new CassandraToSqlTable("widget_type",
idColumn("id"),
idColumn("tenant_id"),
stringColumn("bundle_alias"),
stringColumn("alias"),
stringColumn("name"),
stringColumn("descriptor")),
new CassandraToSqlTable("widgets_bundle",
idColumn("id"),
idColumn("tenant_id"),
stringColumn("alias"),
stringColumn("title"),
stringColumn("search_text")),
new CassandraToSqlTable("rule_chain",
idColumn("id"),
idColumn("tenant_id"),
stringColumn("name"),
stringColumn("search_text"),
idColumn("first_rule_node_id"),
booleanColumn("root"),
booleanColumn("debug_mode"),
stringColumn("configuration"),
stringColumn("additional_info")),
new CassandraToSqlTable("rule_node",
idColumn("id"),
idColumn("rule_chain_id"),
stringColumn("type"),
stringColumn("name"),
booleanColumn("debug_mode"),
stringColumn("search_text"),
stringColumn("configuration"),
stringColumn("additional_info")),
new CassandraToSqlTable("entity_view",
idColumn("id"),
idColumn("tenant_id"),
idColumn("customer_id"),
idColumn("entity_id"),
stringColumn("entity_type"),
stringColumn("name"),
stringColumn("type"),
stringColumn("keys"),
bigintColumn("start_ts"),
bigintColumn("end_ts"),
stringColumn("search_text"),
stringColumn("additional_info"))
);
}

172
application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlColumn.java

@ -1,172 +0,0 @@
/**
* Copyright © 2016-2020 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.install.migrate;
import com.datastax.oss.driver.api.core.cql.Row;
import lombok.Data;
import org.thingsboard.server.common.data.UUIDConverter;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.regex.Pattern;
@Data
public class CassandraToSqlColumn {
private static final ThreadLocal<Pattern> PATTERN_THREAD_LOCAL = ThreadLocal.withInitial(() -> Pattern.compile(String.valueOf(Character.MIN_VALUE)));
private static final String EMPTY_STR = "";
private int index;
private int sqlIndex;
private String cassandraColumnName;
private String sqlColumnName;
private CassandraToSqlColumnType type;
private int sqlType;
private int size;
private Class<? extends Enum> enumClass;
public static CassandraToSqlColumn idColumn(String name) {
return new CassandraToSqlColumn(name, CassandraToSqlColumnType.ID);
}
public static CassandraToSqlColumn stringColumn(String name) {
return new CassandraToSqlColumn(name, CassandraToSqlColumnType.STRING);
}
public static CassandraToSqlColumn stringColumn(String cassandraColumnName, String sqlColumnName) {
return new CassandraToSqlColumn(cassandraColumnName, sqlColumnName);
}
public static CassandraToSqlColumn bigintColumn(String name) {
return new CassandraToSqlColumn(name, CassandraToSqlColumnType.BIGINT);
}
public static CassandraToSqlColumn doubleColumn(String name) {
return new CassandraToSqlColumn(name, CassandraToSqlColumnType.DOUBLE);
}
public static CassandraToSqlColumn booleanColumn(String name) {
return new CassandraToSqlColumn(name, CassandraToSqlColumnType.BOOLEAN);
}
public static CassandraToSqlColumn jsonColumn(String name) {
return new CassandraToSqlColumn(name, CassandraToSqlColumnType.JSON);
}
public static CassandraToSqlColumn enumToIntColumn(String name, Class<? extends Enum> enumClass) {
return new CassandraToSqlColumn(name, CassandraToSqlColumnType.ENUM_TO_INT, enumClass);
}
public CassandraToSqlColumn(String columnName) {
this(columnName, columnName, CassandraToSqlColumnType.STRING, null);
}
public CassandraToSqlColumn(String columnName, CassandraToSqlColumnType type) {
this(columnName, columnName, type, null);
}
public CassandraToSqlColumn(String columnName, CassandraToSqlColumnType type, Class<? extends Enum> enumClass) {
this(columnName, columnName, type, enumClass);
}
public CassandraToSqlColumn(String cassandraColumnName, String sqlColumnName) {
this(cassandraColumnName, sqlColumnName, CassandraToSqlColumnType.STRING, null);
}
public CassandraToSqlColumn(String cassandraColumnName, String sqlColumnName, CassandraToSqlColumnType type,
Class<? extends Enum> enumClass) {
this.cassandraColumnName = cassandraColumnName;
this.sqlColumnName = sqlColumnName;
this.type = type;
this.enumClass = enumClass;
}
public String getColumnValue(Row row) {
if (row.isNull(index)) {
if (this.type == CassandraToSqlColumnType.BOOLEAN) {
return Boolean.toString(false);
} else {
return null;
}
} else {
switch (this.type) {
case ID:
return UUIDConverter.fromTimeUUID(row.getUuid(index));
case DOUBLE:
return Double.toString(row.getDouble(index));
case INTEGER:
return Integer.toString(row.getInt(index));
case FLOAT:
return Float.toString(row.getFloat(index));
case BIGINT:
return Long.toString(row.getLong(index));
case BOOLEAN:
return Boolean.toString(row.getBoolean(index));
case STRING:
case JSON:
case ENUM_TO_INT:
default:
String value = row.getString(index);
return this.replaceNullChars(value);
}
}
}
public void setColumnValue(PreparedStatement sqlInsertStatement, String value) throws SQLException {
if (value == null) {
sqlInsertStatement.setNull(this.sqlIndex, this.sqlType);
} else {
switch (this.type) {
case DOUBLE:
sqlInsertStatement.setDouble(this.sqlIndex, Double.parseDouble(value));
break;
case INTEGER:
sqlInsertStatement.setInt(this.sqlIndex, Integer.parseInt(value));
break;
case FLOAT:
sqlInsertStatement.setFloat(this.sqlIndex, Float.parseFloat(value));
break;
case BIGINT:
sqlInsertStatement.setLong(this.sqlIndex, Long.parseLong(value));
break;
case BOOLEAN:
sqlInsertStatement.setBoolean(this.sqlIndex, Boolean.parseBoolean(value));
break;
case ENUM_TO_INT:
Enum enumVal = Enum.valueOf(this.enumClass, value);
int intValue = enumVal.ordinal();
sqlInsertStatement.setInt(this.sqlIndex, intValue);
break;
case JSON:
case STRING:
case ID:
default:
sqlInsertStatement.setString(this.sqlIndex, value);
break;
}
}
}
private String replaceNullChars(String strValue) {
if (strValue != null) {
return PATTERN_THREAD_LOCAL.get().matcher(strValue).replaceAll(EMPTY_STR);
}
return strValue;
}
}

64
application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlColumnData.java

@ -1,64 +0,0 @@
/**
* Copyright © 2016-2020 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.install.migrate;
import lombok.Data;
@Data
public class CassandraToSqlColumnData {
private String value;
private String originalValue;
private int constraintCounter = 0;
public CassandraToSqlColumnData(String value) {
this.value = value;
this.originalValue = value;
}
public int nextContraintCounter() {
return ++constraintCounter;
}
public String getNextConstraintStringValue(CassandraToSqlColumn column) {
int counter = this.nextContraintCounter();
String newValue = this.originalValue + counter;
int overflow = newValue.length() - column.getSize();
if (overflow > 0) {
newValue = this.originalValue.substring(0, this.originalValue.length()-overflow) + counter;
}
return newValue;
}
public String getNextConstraintEmailValue(CassandraToSqlColumn column) {
int counter = this.nextContraintCounter();
String[] emailValues = this.originalValue.split("@");
String newValue = emailValues[0] + "+" + counter + "@" + emailValues[1];
int overflow = newValue.length() - column.getSize();
if (overflow > 0) {
newValue = emailValues[0].substring(0, emailValues[0].length()-overflow) + "+" + counter + "@" + emailValues[1];
}
return newValue;
}
public String getLogValue() {
if (this.value != null && this.value.length() > 255) {
return this.value.substring(0, 255) + "...[truncated " + (this.value.length() - 255) + " symbols]";
}
return this.value;
}
}

28
application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlColumnType.java

@ -1,28 +0,0 @@
/**
* Copyright © 2016-2020 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.install.migrate;
public enum CassandraToSqlColumnType {
ID,
DOUBLE,
INTEGER,
FLOAT,
BIGINT,
BOOLEAN,
STRING,
JSON,
ENUM_TO_INT
}

40
application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlEventTsColumn.java

@ -1,40 +0,0 @@
/**
* Copyright © 2016-2020 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.install.migrate;
import com.datastax.oss.driver.api.core.cql.Row;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.EPOCH_DIFF;
public class CassandraToSqlEventTsColumn extends CassandraToSqlColumn {
CassandraToSqlEventTsColumn() {
super("id", "ts", CassandraToSqlColumnType.BIGINT, null);
}
@Override
public String getColumnValue(Row row) {
UUID id = row.getUuid(getIndex());
long ts = getTs(id);
return ts + "";
}
private long getTs(UUID uuid) {
return (uuid.timestamp() - EPOCH_DIFF) / 10000;
}
}

304
application/src/main/java/org/thingsboard/server/service/install/migrate/CassandraToSqlTable.java

@ -1,304 +0,0 @@
/**
* Copyright © 2016-2020 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.install.migrate;
import com.datastax.oss.driver.api.core.cql.ResultSet;
import com.datastax.oss.driver.api.core.cql.Row;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.postgresql.util.PSQLException;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.dao.cassandra.guava.GuavaSession;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
@Data
@Slf4j
public class CassandraToSqlTable {
private static final int DEFAULT_BATCH_SIZE = 10000;
private String cassandraCf;
private String sqlTableName;
private List<CassandraToSqlColumn> columns;
private int batchSize = DEFAULT_BATCH_SIZE;
private PreparedStatement sqlInsertStatement;
public CassandraToSqlTable(String tableName, CassandraToSqlColumn... columns) {
this(tableName, tableName, DEFAULT_BATCH_SIZE, columns);
}
public CassandraToSqlTable(String tableName, String sqlTableName, CassandraToSqlColumn... columns) {
this(tableName, sqlTableName, DEFAULT_BATCH_SIZE, columns);
}
public CassandraToSqlTable(String tableName, int batchSize, CassandraToSqlColumn... columns) {
this(tableName, tableName, batchSize, columns);
}
public CassandraToSqlTable(String cassandraCf, String sqlTableName, int batchSize, CassandraToSqlColumn... columns) {
this.cassandraCf = cassandraCf;
this.sqlTableName = sqlTableName;
this.batchSize = batchSize;
this.columns = Arrays.asList(columns);
for (int i=0;i<columns.length;i++) {
this.columns.get(i).setIndex(i);
this.columns.get(i).setSqlIndex(i+1);
}
}
public void migrateToSql(GuavaSession session, Connection conn) throws SQLException {
log.info("[{}] Migrating data from cassandra '{}' Column Family to '{}' SQL table...", this.sqlTableName, this.cassandraCf, this.sqlTableName);
DatabaseMetaData metadata = conn.getMetaData();
java.sql.ResultSet resultSet = metadata.getColumns(null, null, this.sqlTableName, null);
while (resultSet.next()) {
String name = resultSet.getString("COLUMN_NAME");
int sqlType = resultSet.getInt("DATA_TYPE");
int size = resultSet.getInt("COLUMN_SIZE");
CassandraToSqlColumn column = this.getColumn(name);
column.setSize(size);
column.setSqlType(sqlType);
}
this.sqlInsertStatement = createSqlInsertStatement(conn);
Statement cassandraSelectStatement = createCassandraSelectStatement();
cassandraSelectStatement.setPageSize(100);
ResultSet rs = session.execute(cassandraSelectStatement);
Iterator<Row> iter = rs.iterator();
int rowCounter = 0;
List<CassandraToSqlColumnData[]> batchData;
boolean hasNext;
do {
batchData = this.extractBatchData(iter);
hasNext = batchData.size() == this.batchSize;
this.batchInsert(batchData, conn);
rowCounter += batchData.size();
log.info("[{}] {} records migrated so far...", this.sqlTableName, rowCounter);
} while (hasNext);
this.sqlInsertStatement.close();
log.info("[{}] {} total records migrated.", this.sqlTableName, rowCounter);
log.info("[{}] Finished migration data from cassandra '{}' Column Family to '{}' SQL table.",
this.sqlTableName, this.cassandraCf, this.sqlTableName);
}
private List<CassandraToSqlColumnData[]> extractBatchData(Iterator<Row> iter) {
List<CassandraToSqlColumnData[]> batchData = new ArrayList<>();
while (iter.hasNext() && batchData.size() < this.batchSize) {
Row row = iter.next();
if (row != null) {
CassandraToSqlColumnData[] data = this.extractRowData(row);
batchData.add(data);
}
}
return batchData;
}
private CassandraToSqlColumnData[] extractRowData(Row row) {
CassandraToSqlColumnData[] data = new CassandraToSqlColumnData[this.columns.size()];
for (CassandraToSqlColumn column: this.columns) {
String value = column.getColumnValue(row);
data[column.getIndex()] = new CassandraToSqlColumnData(value);
}
return this.validateColumnData(data);
}
private CassandraToSqlColumnData[] validateColumnData(CassandraToSqlColumnData[] data) {
for (int i=0;i<data.length;i++) {
CassandraToSqlColumn column = this.columns.get(i);
if (column.getType() == CassandraToSqlColumnType.STRING) {
CassandraToSqlColumnData columnData = data[i];
String value = columnData.getValue();
if (value != null && value.length() > column.getSize()) {
log.warn("[{}] Value size [{}] exceeds maximum size [{}] of column [{}] and will be truncated!",
this.sqlTableName,
value.length(), column.getSize(), column.getSqlColumnName());
log.warn("[{}] Affected data:\n{}", this.sqlTableName, this.dataToString(data));
value = value.substring(0, column.getSize());
columnData.setOriginalValue(value);
columnData.setValue(value);
}
}
}
return data;
}
private void batchInsert(List<CassandraToSqlColumnData[]> batchData, Connection conn) throws SQLException {
boolean retry = false;
for (CassandraToSqlColumnData[] data : batchData) {
for (CassandraToSqlColumn column: this.columns) {
column.setColumnValue(this.sqlInsertStatement, data[column.getIndex()].getValue());
}
try {
this.sqlInsertStatement.executeUpdate();
} catch (SQLException e) {
if (this.handleInsertException(batchData, data, conn, e)) {
retry = true;
break;
} else {
throw e;
}
}
}
if (retry) {
this.batchInsert(batchData, conn);
} else {
conn.commit();
}
}
private boolean handleInsertException(List<CassandraToSqlColumnData[]> batchData,
CassandraToSqlColumnData[] data,
Connection conn, SQLException ex) throws SQLException {
conn.commit();
String constraint = extractConstraintName(ex).orElse(null);
if (constraint != null) {
if (this.onConstraintViolation(batchData, data, constraint)) {
return true;
} else {
log.error("[{}] Unhandled constraint violation [{}] during insert!", this.sqlTableName, constraint);
log.error("[{}] Affected data:\n{}", this.sqlTableName, this.dataToString(data));
}
} else {
log.error("[{}] Unhandled exception during insert!", this.sqlTableName);
log.error("[{}] Affected data:\n{}", this.sqlTableName, this.dataToString(data));
}
return false;
}
private String dataToString(CassandraToSqlColumnData[] data) {
StringBuffer stringData = new StringBuffer("{\n");
for (int i=0;i<data.length;i++) {
String columnName = this.columns.get(i).getSqlColumnName();
String value = data[i].getLogValue();
stringData.append("\"").append(columnName).append("\": ").append("[").append(value).append("]\n");
}
stringData.append("}");
return stringData.toString();
}
protected boolean onConstraintViolation(List<CassandraToSqlColumnData[]> batchData,
CassandraToSqlColumnData[] data, String constraint) {
return false;
}
protected void handleUniqueNameViolation(CassandraToSqlColumnData[] data, String entityType) {
CassandraToSqlColumn nameColumn = this.getColumn("name");
CassandraToSqlColumn searchTextColumn = this.getColumn("search_text");
CassandraToSqlColumnData nameColumnData = data[nameColumn.getIndex()];
CassandraToSqlColumnData searchTextColumnData = data[searchTextColumn.getIndex()];
String prevName = nameColumnData.getValue();
String newName = nameColumnData.getNextConstraintStringValue(nameColumn);
nameColumnData.setValue(newName);
searchTextColumnData.setValue(searchTextColumnData.getNextConstraintStringValue(searchTextColumn));
String id = UUIDConverter.fromString(this.getColumnData(data, "id").getValue()).toString();
log.warn("Found {} with duplicate name [id:[{}]]. Attempting to rename {} from '{}' to '{}'...", entityType, id, entityType, prevName, newName);
}
protected void handleUniqueEmailViolation(CassandraToSqlColumnData[] data) {
CassandraToSqlColumn emailColumn = this.getColumn("email");
CassandraToSqlColumn searchTextColumn = this.getColumn("search_text");
CassandraToSqlColumnData emailColumnData = data[emailColumn.getIndex()];
CassandraToSqlColumnData searchTextColumnData = data[searchTextColumn.getIndex()];
String prevEmail = emailColumnData.getValue();
String newEmail = emailColumnData.getNextConstraintEmailValue(emailColumn);
emailColumnData.setValue(newEmail);
searchTextColumnData.setValue(searchTextColumnData.getNextConstraintEmailValue(searchTextColumn));
String id = UUIDConverter.fromString(this.getColumnData(data, "id").getValue()).toString();
log.warn("Found user with duplicate email [id:[{}]]. Attempting to rename email from '{}' to '{}'...", id, prevEmail, newEmail);
}
protected void ignoreRecord(List<CassandraToSqlColumnData[]> batchData, CassandraToSqlColumnData[] data) {
log.warn("[{}] Affected data:\n{}", this.sqlTableName, this.dataToString(data));
int index = batchData.indexOf(data);
if (index > 0) {
batchData.remove(index);
}
}
protected CassandraToSqlColumn getColumn(String sqlColumnName) {
return this.columns.stream().filter(col -> col.getSqlColumnName().equals(sqlColumnName)).findFirst().get();
}
protected CassandraToSqlColumnData getColumnData(CassandraToSqlColumnData[] data, String sqlColumnName) {
CassandraToSqlColumn column = this.getColumn(sqlColumnName);
return data[column.getIndex()];
}
private Optional<String> extractConstraintName(SQLException ex) {
final String sqlState = JdbcExceptionHelper.extractSqlState( ex );
if (sqlState != null) {
String sqlStateClassCode = JdbcExceptionHelper.determineSqlStateClassCode( sqlState );
if ( sqlStateClassCode != null ) {
if (Arrays.asList(
"23", // "integrity constraint violation"
"27", // "triggered data change violation"
"44" // "with check option violation"
).contains(sqlStateClassCode)) {
if (ex instanceof PSQLException) {
return Optional.of(((PSQLException)ex).getServerErrorMessage().getConstraint());
}
}
}
}
return Optional.empty();
}
private Statement createCassandraSelectStatement() {
StringBuilder selectStatementBuilder = new StringBuilder();
selectStatementBuilder.append("SELECT ");
for (CassandraToSqlColumn column : columns) {
selectStatementBuilder.append(column.getCassandraColumnName()).append(",");
}
selectStatementBuilder.deleteCharAt(selectStatementBuilder.length() - 1);
selectStatementBuilder.append(" FROM ").append(cassandraCf);
return SimpleStatement.newInstance(selectStatementBuilder.toString());
}
private PreparedStatement createSqlInsertStatement(Connection conn) throws SQLException {
StringBuilder insertStatementBuilder = new StringBuilder();
insertStatementBuilder.append("INSERT INTO ").append(this.sqlTableName).append(" (");
for (CassandraToSqlColumn column : columns) {
insertStatementBuilder.append(column.getSqlColumnName()).append(",");
}
insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1);
insertStatementBuilder.append(") VALUES (");
for (CassandraToSqlColumn column : columns) {
if (column.getType() == CassandraToSqlColumnType.JSON) {
insertStatementBuilder.append("cast(? AS json)");
} else {
insertStatementBuilder.append("?");
}
insertStatementBuilder.append(",");
}
insertStatementBuilder.deleteCharAt(insertStatementBuilder.length() - 1);
insertStatementBuilder.append(")");
return conn.prepareStatement(insertStatementBuilder.toString());
}
}

6
application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java

@ -22,8 +22,8 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
@ -59,7 +59,7 @@ public class DefaultDataUpdateService implements DataUpdateService {
new PaginatedUpdater<String, Tenant>() {
@Override
protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
protected TextPageData<Tenant> findEntities(String region, TextPageLink pageLink) {
return tenantService.findTenants(pageLink);
}

12
application/src/main/java/org/thingsboard/server/service/install/update/PaginatedUpdater.java

@ -17,29 +17,29 @@ package org.thingsboard.server.service.install.update;
import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
public abstract class PaginatedUpdater<I, D extends SearchTextBased<? extends UUIDBased>> {
private static final int DEFAULT_LIMIT = 100;
public void updateEntities(I id) {
PageLink pageLink = new PageLink(DEFAULT_LIMIT);
TextPageLink pageLink = new TextPageLink(DEFAULT_LIMIT);
boolean hasNext = true;
while (hasNext) {
PageData<D> entities = findEntities(id, pageLink);
TextPageData<D> entities = findEntities(id, pageLink);
for (D entity : entities.getData()) {
updateEntity(entity);
}
hasNext = entities.hasNext();
if (hasNext) {
pageLink = pageLink.nextPageLink();
pageLink = entities.getNextPageLink();
}
}
}
protected abstract PageData<D> findEntities(I id, PageLink pageLink);
protected abstract TextPageData<D> findEntities(I id, TextPageLink pageLink);
protected abstract void updateEntity(D entity);

7
application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java

@ -112,11 +112,8 @@ public class DefaultMailService implements MailService {
}
}
javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", enableTls);
if (enableTls && jsonConfig.has("tlsVersion") && !jsonConfig.get("tlsVersion").isNull()) {
String tlsVersion = jsonConfig.get("tlsVersion").asText();
if (StringUtils.isNoneEmpty(tlsVersion)) {
javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", tlsVersion);
}
if (enableTls && jsonConfig.has("tlsVersion") && StringUtils.isNoneEmpty(jsonConfig.get("tlsVersion").asText())) {
javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", jsonConfig.get("tlsVersion").asText());
}
return javaMailProperties;
}

4
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java

@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.customer.CustomerService;
@ -116,7 +116,7 @@ public abstract class AbstractOAuth2ClientMapper {
}
private TenantId getTenantId(String tenantName) throws IOException {
List<Tenant> tenants = tenantService.findTenants(new PageLink(1, 0, tenantName)).getData();
List<Tenant> tenants = tenantService.findTenants(new TextPageLink(1, tenantName)).getData();
Tenant tenant;
if (tenants == null || tenants.isEmpty()) {
tenant = new Tenant();

12
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java

@ -35,13 +35,13 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
@ -329,13 +329,13 @@ public class DefaultDeviceStateService implements DeviceStateService {
//TODO 3.0: replace this dummy search with new functionality to search by partitions using SQL capabilities.
// Adding only devices that are in new partitions
List<Tenant> tenants = tenantService.findTenants(new PageLink(Integer.MAX_VALUE)).getData();
List<Tenant> tenants = tenantService.findTenants(new TextPageLink(Integer.MAX_VALUE)).getData();
for (Tenant tenant : tenants) {
PageLink pageLink = new PageLink(initFetchPackSize);
TextPageLink pageLink = new TextPageLink(initFetchPackSize);
while (pageLink != null) {
List<ListenableFuture<Void>> fetchFutures = new ArrayList<>();
PageData<Device> page = deviceService.findDevicesByTenantId(tenant.getId(), pageLink);
pageLink = page.hasNext() ? pageLink.nextPageLink() : null;
TextPageData<Device> page = deviceService.findDevicesByTenantId(tenant.getId(), pageLink);
pageLink = page.getNextPageLink();
for (Device device : page.getData()) {
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenant.getId(), device.getId());
if (addedPartitions.contains(tpi)) {

7
application/src/main/resources/thingsboard.yml

@ -160,6 +160,8 @@ dashboard:
database:
ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records
entities:
type: "${DATABASE_ENTITIES_TYPE:sql}" # cassandra OR sql
ts:
type: "${DATABASE_TS_TYPE:sql}" # cassandra, sql, or timescale (for hybrid mode, DATABASE_TS_TYPE value should be cassandra, or timescale)
@ -176,9 +178,9 @@ cassandra:
# Enable/disable secure connection
ssl: "${CASSANDRA_USE_SSL:false}"
# Enable/disable JMX
jmx: "${CASSANDRA_USE_JMX:false}"
jmx: "${CASSANDRA_USE_JMX:true}"
# Enable/disable metrics collection.
metrics: "${CASSANDRA_USE_METRICS:false}"
metrics: "${CASSANDRA_DISABLE_METRICS:true}"
# NONE SNAPPY LZ4
compression: "${CASSANDRA_COMPRESSION:none}"
# Specify cassandra cluster initialization timeout in milliseconds (if no hosts available during startup)
@ -411,7 +413,6 @@ spring.resources.chain:
enabled: "true"
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation: "true"
spring.jpa.properties.hibernate.order_by.default_null_ordering: "last"
# SQL DAO Configuration
spring:

39
application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java

@ -66,8 +66,7 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
@ -315,20 +314,22 @@ public abstract class AbstractControllerTest {
}
protected <T> T doGetTypedWithPageLink(String urlTemplate, TypeReference<T> responseType,
PageLink pageLink,
TextPageLink pageLink,
Object... urlVariables) throws Exception {
List<Object> pageLinkVariables = new ArrayList<>();
urlTemplate += "pageSize={pageSize}&page={page}";
pageLinkVariables.add(pageLink.getPageSize());
pageLinkVariables.add(pageLink.getPage());
urlTemplate += "limit={limit}";
pageLinkVariables.add(pageLink.getLimit());
if (StringUtils.isNotEmpty(pageLink.getTextSearch())) {
urlTemplate += "&textSearch={textSearch}";
pageLinkVariables.add(pageLink.getTextSearch());
}
if (pageLink.getSortOrder() != null) {
urlTemplate += "&sortProperty={sortProperty}&sortOrder={sortOrder}";
pageLinkVariables.add(pageLink.getSortOrder().getProperty());
pageLinkVariables.add(pageLink.getSortOrder().getDirection().name());
if (pageLink.getIdOffset() != null) {
urlTemplate += "&idOffset={idOffset}";
pageLinkVariables.add(pageLink.getIdOffset().toString());
}
if (StringUtils.isNotEmpty(pageLink.getTextOffset())) {
urlTemplate += "&textOffset={textOffset}";
pageLinkVariables.add(pageLink.getTextOffset());
}
Object[] vars = new Object[urlVariables.length + pageLinkVariables.size()];
@ -342,9 +343,8 @@ public abstract class AbstractControllerTest {
TimePageLink pageLink,
Object... urlVariables) throws Exception {
List<Object> pageLinkVariables = new ArrayList<>();
urlTemplate += "pageSize={pageSize}&page={page}";
pageLinkVariables.add(pageLink.getPageSize());
pageLinkVariables.add(pageLink.getPage());
urlTemplate += "limit={limit}";
pageLinkVariables.add(pageLink.getLimit());
if (pageLink.getStartTime() != null) {
urlTemplate += "&startTime={startTime}";
pageLinkVariables.add(pageLink.getStartTime());
@ -353,14 +353,13 @@ public abstract class AbstractControllerTest {
urlTemplate += "&endTime={endTime}";
pageLinkVariables.add(pageLink.getEndTime());
}
if (StringUtils.isNotEmpty(pageLink.getTextSearch())) {
urlTemplate += "&textSearch={textSearch}";
pageLinkVariables.add(pageLink.getTextSearch());
if (pageLink.getIdOffset() != null) {
urlTemplate += "&offset={offset}";
pageLinkVariables.add(pageLink.getIdOffset().toString());
}
if (pageLink.getSortOrder() != null) {
urlTemplate += "&sortProperty={sortProperty}&sortOrder={sortOrder}";
pageLinkVariables.add(pageLink.getSortOrder().getProperty());
pageLinkVariables.add(pageLink.getSortOrder().getDirection().name());
if (pageLink.isAscOrder()) {
urlTemplate += "&ascOrder={ascOrder}";
pageLinkVariables.add(pageLink.isAscOrder());
}
Object[] vars = new Object[urlVariables.length + pageLinkVariables.size()];
System.arraycopy(urlVariables, 0, vars, 0, urlVariables.length);

6
application/src/test/java/org/thingsboard/server/controller/AbstractRuleEngineControllerTest.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.Event;
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.page.PageData;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
@ -56,10 +56,10 @@ public class AbstractRuleEngineControllerTest extends AbstractControllerTest {
return doGet("/api/ruleChain/metadata/" + ruleChainId.getId().toString(), RuleChainMetaData.class);
}
protected PageData<Event> getDebugEvents(TenantId tenantId, EntityId entityId, int limit) throws Exception {
protected TimePageData<Event> getDebugEvents(TenantId tenantId, EntityId entityId, int limit) throws Exception {
TimePageLink pageLink = new TimePageLink(limit);
return doGetTypedWithTimePageLink("/api/events/{entityType}/{entityId}/{eventType}?tenantId={tenantId}&",
new TypeReference<PageData<Event>>() {
new TypeReference<TimePageData<Event>>() {
}, pageLink, entityId.getEntityType(), entityId.getId(), DataConstants.DEBUG_RULE_NODE, tenantId.getId());
}

130
application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java

@ -15,7 +15,7 @@
*/
package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
@ -28,8 +28,8 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.stats.DefaultRuleEngineStatisticsService;
@ -206,7 +206,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
doPost("/api/customer/" + Uuids.timeBased().toString()
doPost("/api/customer/" + UUIDs.timeBased().toString()
+ "/asset/" + savedAsset.getId().getId().toString())
.andExpect(status().isNotFound());
}
@ -260,14 +260,15 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
assets.add(doPost("/api/asset", asset, Asset.class));
}
List<Asset> loadedAssets = new ArrayList<>();
PageLink pageLink = new PageLink(23);
PageData<Asset> pageData = null;
TextPageLink pageLink = new TextPageLink(23);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/assets?",
new TypeReference<PageData<Asset>>(){}, pageLink);
new TypeReference<TextPageData<Asset>>() {
}, pageLink);
loadedAssets.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -305,14 +306,15 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
}
List<Asset> loadedAssetsTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Asset> pageData = null;
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/assets?",
new TypeReference<PageData<Asset>>(){}, pageLink);
new TypeReference<TextPageData<Asset>>() {
}, pageLink);
loadedAssetsTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -322,13 +324,14 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
List<Asset> loadedAssetsTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
do {
pageData = doGetTypedWithPageLink("/api/tenant/assets?",
new TypeReference<PageData<Asset>>(){}, pageLink);
new TypeReference<TextPageData<Asset>>() {
}, pageLink);
loadedAssetsTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -342,9 +345,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageLink = new TextPageLink(4, title1);
pageData = doGetTypedWithPageLink("/api/tenant/assets?",
new TypeReference<PageData<Asset>>(){}, pageLink);
new TypeReference<TextPageData<Asset>>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -353,9 +357,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
pageData = doGetTypedWithPageLink("/api/tenant/assets?",
new TypeReference<PageData<Asset>>(){}, pageLink);
new TypeReference<TextPageData<Asset>>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -388,14 +393,15 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
}
List<Asset> loadedAssetsType1 = new ArrayList<>();
PageLink pageLink = new PageLink(15);
PageData<Asset> pageData = null;
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
new TypeReference<PageData<Asset>>(){}, pageLink, type1);
new TypeReference<TextPageData<Asset>>() {
}, pageLink, type1);
loadedAssetsType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -405,13 +411,14 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Assert.assertEquals(assetsType1, loadedAssetsType1);
List<Asset> loadedAssetsType2 = new ArrayList<>();
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
new TypeReference<PageData<Asset>>(){}, pageLink, type2);
new TypeReference<TextPageData<Asset>>() {
}, pageLink, type2);
loadedAssetsType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -425,9 +432,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
new TypeReference<PageData<Asset>>(){}, pageLink, type1);
new TypeReference<TextPageData<Asset>>() {
}, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -436,9 +444,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
new TypeReference<PageData<Asset>>(){}, pageLink, type2);
new TypeReference<TextPageData<Asset>>() {
}, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -461,14 +470,15 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
}
List<Asset> loadedAssets = new ArrayList<>();
PageLink pageLink = new PageLink(23);
PageData<Asset> pageData = null;
TextPageLink pageLink = new TextPageLink(23);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>(){}, pageLink);
new TypeReference<TextPageData<Asset>>() {
}, pageLink);
loadedAssets.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -513,14 +523,15 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
}
List<Asset> loadedAssetsTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Asset> pageData = null;
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>(){}, pageLink);
new TypeReference<TextPageData<Asset>>() {
}, pageLink);
loadedAssetsTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -530,13 +541,14 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
List<Asset> loadedAssetsTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>(){}, pageLink);
new TypeReference<TextPageData<Asset>>() {
}, pageLink);
loadedAssetsTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -550,9 +562,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageLink = new TextPageLink(4, title1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>(){}, pageLink);
new TypeReference<TextPageData<Asset>>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -561,9 +574,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>(){}, pageLink);
new TypeReference<TextPageData<Asset>>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -605,14 +619,15 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
}
List<Asset> loadedAssetsType1 = new ArrayList<>();
PageLink pageLink = new PageLink(15);
PageData<Asset> pageData = null;
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
new TypeReference<PageData<Asset>>(){}, pageLink, type1);
new TypeReference<TextPageData<Asset>>() {
}, pageLink, type1);
loadedAssetsType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -622,13 +637,14 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Assert.assertEquals(assetsType1, loadedAssetsType1);
List<Asset> loadedAssetsType2 = new ArrayList<>();
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
new TypeReference<PageData<Asset>>(){}, pageLink, type2);
new TypeReference<TextPageData<Asset>>() {
}, pageLink, type2);
loadedAssetsType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -642,9 +658,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
new TypeReference<PageData<Asset>>(){}, pageLink, type1);
new TypeReference<TextPageData<Asset>>() {
}, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -653,9 +670,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
new TypeReference<PageData<Asset>>(){}, pageLink, type2);
new TypeReference<TextPageData<Asset>>() {
}, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}

22
application/src/test/java/org/thingsboard/server/controller/BaseAuditLogControllerTest.java

@ -24,7 +24,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.AuditLog;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.model.ModelConstants;
@ -77,14 +77,14 @@ public abstract class BaseAuditLogControllerTest extends AbstractControllerTest
List<AuditLog> loadedAuditLogs = new ArrayList<>();
TimePageLink pageLink = new TimePageLink(23);
PageData<AuditLog> pageData;
TimePageData<AuditLog> pageData;
do {
pageData = doGetTypedWithTimePageLink("/api/audit/logs?",
new TypeReference<PageData<AuditLog>>() {
new TypeReference<TimePageData<AuditLog>>() {
}, pageLink);
loadedAuditLogs.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -94,11 +94,11 @@ public abstract class BaseAuditLogControllerTest extends AbstractControllerTest
pageLink = new TimePageLink(23);
do {
pageData = doGetTypedWithTimePageLink("/api/audit/logs/customer/" + ModelConstants.NULL_UUID + "?",
new TypeReference<PageData<AuditLog>>() {
new TypeReference<TimePageData<AuditLog>>() {
}, pageLink);
loadedAuditLogs.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -108,11 +108,11 @@ public abstract class BaseAuditLogControllerTest extends AbstractControllerTest
pageLink = new TimePageLink(23);
do {
pageData = doGetTypedWithTimePageLink("/api/audit/logs/user/" + tenantAdmin.getId().getId().toString() + "?",
new TypeReference<PageData<AuditLog>>() {
new TypeReference<TimePageData<AuditLog>>() {
}, pageLink);
loadedAuditLogs.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -132,14 +132,14 @@ public abstract class BaseAuditLogControllerTest extends AbstractControllerTest
List<AuditLog> loadedAuditLogs = new ArrayList<>();
TimePageLink pageLink = new TimePageLink(23);
PageData<AuditLog> pageData;
TimePageData<AuditLog> pageData;
do {
pageData = doGetTypedWithTimePageLink("/api/audit/logs/entity/DEVICE/" + savedDevice.getId().getId() + "?",
new TypeReference<PageData<AuditLog>>() {
new TypeReference<TimePageData<AuditLog>>() {
}, pageLink);
loadedAuditLogs.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());

34
application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java

@ -27,8 +27,8 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
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.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.junit.Assert;
import org.junit.Test;
@ -241,13 +241,13 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
}
List<Customer> loadedCustomers = new ArrayList<>();
PageLink pageLink = new PageLink(23);
PageData<Customer> pageData = null;
TextPageLink pageLink = new TextPageLink(23);
TextPageData<Customer> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<PageData<Customer>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>(){}, pageLink);
loadedCustomers.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -307,13 +307,13 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
}
List<Customer> loadedCustomersTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Customer> pageData = null;
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<Customer> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<PageData<Customer>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>(){}, pageLink);
loadedCustomersTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -323,12 +323,12 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Assert.assertEquals(customersTitle1, loadedCustomersTitle1);
List<Customer> loadedCustomersTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
do {
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<PageData<Customer>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>(){}, pageLink);
loadedCustomersTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -342,8 +342,8 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<PageData<Customer>>(){}, pageLink);
pageLink = new TextPageLink(4, title1);
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -352,8 +352,8 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<PageData<Customer>>(){}, pageLink);
pageLink = new TextPageLink(4, title2);
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<TextPageData<Customer>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());

49
application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java

@ -24,12 +24,13 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.datastax.driver.core.utils.UUIDs;
import org.apache.commons.lang3.RandomStringUtils;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.model.ModelConstants;
@ -158,7 +159,7 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
dashboard.setTitle("My dashboard");
Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
doPost("/api/customer/" + Uuids.timeBased().toString()
doPost("/api/customer/" + UUIDs.timeBased().toString()
+ "/dashboard/" + savedDashboard.getId().getId().toString())
.andExpect(status().isNotFound());
}
@ -210,14 +211,14 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
dashboards.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class)));
}
List<DashboardInfo> loadedDashboards = new ArrayList<>();
PageLink pageLink = new PageLink(24);
PageData<DashboardInfo> pageData = null;
TextPageLink pageLink = new TextPageLink(24);
TextPageData<DashboardInfo> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/dashboards?",
new TypeReference<PageData<DashboardInfo>>(){}, pageLink);
new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink);
loadedDashboards.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -251,14 +252,14 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
}
List<DashboardInfo> loadedDashboardsTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<DashboardInfo> pageData = null;
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<DashboardInfo> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/dashboards?",
new TypeReference<PageData<DashboardInfo>>(){}, pageLink);
new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink);
loadedDashboardsTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -268,13 +269,13 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
Assert.assertEquals(dashboardsTitle1, loadedDashboardsTitle1);
List<DashboardInfo> loadedDashboardsTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
do {
pageData = doGetTypedWithPageLink("/api/tenant/dashboards?",
new TypeReference<PageData<DashboardInfo>>(){}, pageLink);
new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink);
loadedDashboardsTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -288,9 +289,9 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageLink = new TextPageLink(4, title1);
pageData = doGetTypedWithPageLink("/api/tenant/dashboards?",
new TypeReference<PageData<DashboardInfo>>(){}, pageLink);
new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -299,9 +300,9 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
pageData = doGetTypedWithPageLink("/api/tenant/dashboards?",
new TypeReference<PageData<DashboardInfo>>(){}, pageLink);
new TypeReference<TextPageData<DashboardInfo>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -323,14 +324,14 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest
}
List<DashboardInfo> loadedDashboards = new ArrayList<>();
PageLink pageLink = new PageLink(21);
PageData<DashboardInfo> pageData = null;
TimePageLink pageLink = new TimePageLink(21);
TimePageData<DashboardInfo> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?",
new TypeReference<PageData<DashboardInfo>>(){}, pageLink);
pageData = doGetTypedWithTimePageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?",
new TypeReference<TimePageData<DashboardInfo>>(){}, pageLink);
loadedDashboards.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());

116
application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java

@ -23,14 +23,14 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.datastax.driver.core.utils.UUIDs;
import org.apache.commons.lang3.RandomStringUtils;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceCredentialsId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
@ -215,7 +215,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/customer/" + Uuids.timeBased().toString()
doPost("/api/customer/" + UUIDs.timeBased().toString()
+ "/device/" + savedDevice.getId().getId().toString())
.andExpect(status().isNotFound());
}
@ -333,7 +333,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
DeviceCredentials newDeviceCredentials = new DeviceCredentials(new DeviceCredentialsId(Uuids.timeBased()));
DeviceCredentials newDeviceCredentials = new DeviceCredentials(new DeviceCredentialsId(UUIDs.timeBased()));
newDeviceCredentials.setCreatedTime(deviceCredentials.getCreatedTime());
newDeviceCredentials.setDeviceId(deviceCredentials.getDeviceId());
newDeviceCredentials.setCredentialsType(deviceCredentials.getCredentialsType());
@ -351,7 +351,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
deviceCredentials.setDeviceId(new DeviceId(Uuids.timeBased()));
deviceCredentials.setDeviceId(new DeviceId(UUIDs.timeBased()));
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isNotFound());
}
@ -366,14 +366,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
devices.add(doPost("/api/device", device, Device.class));
}
List<Device> loadedDevices = new ArrayList<>();
PageLink pageLink = new PageLink(23);
PageData<Device> pageData = null;
TextPageLink pageLink = new TextPageLink(23);
TextPageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
new TypeReference<TextPageData<Device>>(){}, pageLink);
loadedDevices.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -409,14 +409,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
}
List<Device> loadedDevicesTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Device> pageData = null;
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
new TypeReference<TextPageData<Device>>(){}, pageLink);
loadedDevicesTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -426,13 +426,13 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
List<Device> loadedDevicesTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
new TypeReference<TextPageData<Device>>(){}, pageLink);
loadedDevicesTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -446,9 +446,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageLink = new TextPageLink(4, title1);
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
new TypeReference<TextPageData<Device>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -457,9 +457,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
new TypeReference<TextPageData<Device>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -492,14 +492,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
}
List<Device> loadedDevicesType1 = new ArrayList<>();
PageLink pageLink = new PageLink(15);
PageData<Device> pageData = null;
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type1);
new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
loadedDevicesType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -509,13 +509,13 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(devicesType1, loadedDevicesType1);
List<Device> loadedDevicesType2 = new ArrayList<>();
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type2);
new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
loadedDevicesType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -529,9 +529,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type1);
new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -540,9 +540,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type2);
new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -565,14 +565,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
}
List<Device> loadedDevices = new ArrayList<>();
PageLink pageLink = new PageLink(23);
PageData<Device> pageData = null;
TextPageLink pageLink = new TextPageLink(23);
TextPageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
new TypeReference<TextPageData<Device>>(){}, pageLink);
loadedDevices.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -617,14 +617,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
}
List<Device> loadedDevicesTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Device> pageData = null;
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
new TypeReference<TextPageData<Device>>(){}, pageLink);
loadedDevicesTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -634,13 +634,13 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
List<Device> loadedDevicesTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
new TypeReference<TextPageData<Device>>(){}, pageLink);
loadedDevicesTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -654,9 +654,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageLink = new TextPageLink(4, title1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
new TypeReference<TextPageData<Device>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -665,9 +665,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
new TypeReference<TextPageData<Device>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -709,14 +709,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
}
List<Device> loadedDevicesType1 = new ArrayList<>();
PageLink pageLink = new PageLink(15);
PageData<Device> pageData = null;
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type1);
new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
loadedDevicesType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -726,13 +726,13 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(devicesType1, loadedDevicesType1);
List<Device> loadedDevicesType2 = new ArrayList<>();
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type2);
new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
loadedDevicesType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -746,9 +746,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type1);
new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -757,9 +757,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4);
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type2);
new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}

87
application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java

@ -15,7 +15,7 @@
*/
package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
@ -27,12 +27,16 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.objects.AttributesEntityView;
import org.thingsboard.server.common.data.objects.TelemetryEntityView;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.model.ModelConstants;
@ -176,7 +180,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testAssignEntityViewToNonExistentCustomer() throws Exception {
EntityView savedView = getNewSavedEntityView("Test entity view");
doPost("/api/customer/" + Uuids.timeBased().toString() + "/device/" + savedView.getId().getId().toString())
doPost("/api/customer/" + UUIDs.timeBased().toString() + "/device/" + savedView.getId().getId().toString())
.andExpect(status().isNotFound());
}
@ -214,20 +218,16 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testGetCustomerEntityViews() throws Exception {
Customer customer = doPost("/api/customer", getNewCustomer("Test customer"), Customer.class);
CustomerId customerId = customer.getId();
String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViewInfos?";
CustomerId customerId = doPost("/api/customer", getNewCustomer("Test customer"), Customer.class).getId();
String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViews?";
List<EntityViewInfo> views = new ArrayList<>();
List<EntityView> views = new ArrayList<>();
for (int i = 0; i < 128; i++) {
views.add(
new EntityViewInfo(doPost("/api/customer/" + customerId.getId().toString() + "/entityView/"
+ getNewSavedEntityView("Test entity view " + i).getId().getId().toString(), EntityView.class),
customer.getTitle(), customer.isPublic())
);
views.add(doPost("/api/customer/" + customerId.getId().toString() + "/entityView/"
+ getNewSavedEntityView("Test entity view " + i).getId().getId().toString(), EntityView.class));
}
List<EntityViewInfo> loadedViews = loadListOfInfo(new PageLink(23), urlTemplate);
List<EntityView> loadedViews = loadListOf(new TextPageLink(23), urlTemplate);
Collections.sort(views, idComparator);
Collections.sort(loadedViews, idComparator);
@ -243,7 +243,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
String name1 = "Entity view name1";
List<EntityView> namesOfView1 = fillListOf(125, name1, "/api/customer/" + customerId.getId().toString()
+ "/entityView/");
List<EntityView> loadedNamesOfView1 = loadListOf(new PageLink(15, 0, name1), urlTemplate);
List<EntityView> loadedNamesOfView1 = loadListOf(new TextPageLink(15, name1), urlTemplate);
Collections.sort(namesOfView1, idComparator);
Collections.sort(loadedNamesOfView1, idComparator);
assertEquals(namesOfView1, loadedNamesOfView1);
@ -251,7 +251,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
String name2 = "Entity view name2";
List<EntityView> NamesOfView2 = fillListOf(143, name2, "/api/customer/" + customerId.getId().toString()
+ "/entityView/");
List<EntityView> loadedNamesOfView2 = loadListOf(new PageLink(4, 0, name2), urlTemplate);
List<EntityView> loadedNamesOfView2 = loadListOf(new TextPageLink(4, name2), urlTemplate);
Collections.sort(NamesOfView2, idComparator);
Collections.sort(loadedNamesOfView2, idComparator);
assertEquals(NamesOfView2, loadedNamesOfView2);
@ -259,18 +259,18 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
for (EntityView view : loadedNamesOfView1) {
doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
}
PageData<EntityView> pageData = doGetTypedWithPageLink(urlTemplate,
new TypeReference<PageData<EntityView>>() {
}, new PageLink(4, 0, name1));
TextPageData<EntityView> pageData = doGetTypedWithPageLink(urlTemplate,
new TypeReference<TextPageData<EntityView>>() {
}, new TextPageLink(4, name1));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
for (EntityView view : loadedNamesOfView2) {
doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
}
pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference<PageData<EntityView>>() {
pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference<TextPageData<EntityView>>() {
},
new PageLink(4, 0, name2));
new TextPageLink(4, name2));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
}
@ -278,11 +278,11 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testGetTenantEntityViews() throws Exception {
List<EntityViewInfo> views = new ArrayList<>();
List<EntityView> views = new ArrayList<>();
for (int i = 0; i < 178; i++) {
views.add(new EntityViewInfo(getNewSavedEntityView("Test entity view" + i), null, false));
views.add(getNewSavedEntityView("Test entity view" + i));
}
List<EntityViewInfo> loadedViews = loadListOfInfo(new PageLink(23), "/api/tenant/entityViewInfos?");
List<EntityView> loadedViews = loadListOf(new TextPageLink(23), "/api/tenant/entityViews?");
Collections.sort(views, idComparator);
Collections.sort(loadedViews, idComparator);
@ -294,14 +294,14 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
public void testGetTenantEntityViewsByName() throws Exception {
String name1 = "Entity view name1";
List<EntityView> namesOfView1 = fillListOf(143, name1);
List<EntityView> loadedNamesOfView1 = loadListOf(new PageLink(15, 0, name1), "/api/tenant/entityViews?");
List<EntityView> loadedNamesOfView1 = loadListOf(new TextPageLink(15, name1), "/api/tenant/entityViews?");
Collections.sort(namesOfView1, idComparator);
Collections.sort(loadedNamesOfView1, idComparator);
assertEquals(namesOfView1, loadedNamesOfView1);
String name2 = "Entity view name2";
List<EntityView> NamesOfView2 = fillListOf(75, name2);
List<EntityView> loadedNamesOfView2 = loadListOf(new PageLink(4, 0, name2), "/api/tenant/entityViews?");
List<EntityView> loadedNamesOfView2 = loadListOf(new TextPageLink(4, name2), "/api/tenant/entityViews?");
Collections.sort(NamesOfView2, idComparator);
Collections.sort(loadedNamesOfView2, idComparator);
assertEquals(NamesOfView2, loadedNamesOfView2);
@ -309,18 +309,18 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
for (EntityView view : loadedNamesOfView1) {
doDelete("/api/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
}
PageData<EntityView> pageData = doGetTypedWithPageLink("/api/tenant/entityViews?",
new TypeReference<PageData<EntityView>>() {
}, new PageLink(4, 0, name1));
TextPageData<EntityView> pageData = doGetTypedWithPageLink("/api/tenant/entityViews?",
new TypeReference<TextPageData<EntityView>>() {
}, new TextPageLink(4, name1));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
for (EntityView view : loadedNamesOfView2) {
doDelete("/api/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
}
pageData = doGetTypedWithPageLink("/api/tenant/entityViews?", new TypeReference<PageData<EntityView>>() {
pageData = doGetTypedWithPageLink("/api/tenant/entityViews?", new TypeReference<TextPageData<EntityView>>() {
},
new PageLink(4, 0, name2));
new TextPageLink(4, name2));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
}
@ -530,30 +530,15 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
return viewNames;
}
private List<EntityView> loadListOf(PageLink pageLink, String urlTemplate) throws Exception {
private List<EntityView> loadListOf(TextPageLink pageLink, String urlTemplate) throws Exception {
List<EntityView> loadedItems = new ArrayList<>();
PageData<EntityView> pageData;
do {
pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference<PageData<EntityView>>() {
}, pageLink);
loadedItems.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
return loadedItems;
}
private List<EntityViewInfo> loadListOfInfo(PageLink pageLink, String urlTemplate) throws Exception {
List<EntityViewInfo> loadedItems = new ArrayList<>();
PageData<EntityViewInfo> pageData;
TextPageData<EntityView> pageData;
do {
pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference<PageData<EntityViewInfo>>() {
pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference<TextPageData<EntityView>>() {
}, pageLink);
loadedItems.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());

40
application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java

@ -24,8 +24,8 @@ import java.util.List;
import org.apache.commons.lang3.RandomStringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.junit.Assert;
import org.junit.Test;
@ -102,8 +102,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
public void testFindTenants() throws Exception {
loginSysAdmin();
List<Tenant> tenants = new ArrayList<>();
PageLink pageLink = new PageLink(17);
PageData<Tenant> pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
TextPageLink pageLink = new TextPageLink(17);
TextPageData<Tenant> pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<TextPageData<Tenant>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getData().size());
tenants.addAll(pageData.getData());
@ -115,12 +115,12 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
}
List<Tenant> loadedTenants = new ArrayList<>();
pageLink = new PageLink(17);
pageLink = new TextPageLink(17);
do {
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<TextPageData<Tenant>>(){}, pageLink);
loadedTenants.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -136,8 +136,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
}
}
pageLink = new PageLink(17);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageLink = new TextPageLink(17);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<TextPageData<Tenant>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getData().size());
}
@ -167,13 +167,13 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
}
List<Tenant> loadedTenantsTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Tenant> pageData = null;
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<Tenant> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<TextPageData<Tenant>>(){}, pageLink);
loadedTenantsTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -183,12 +183,12 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
Assert.assertEquals(tenantsTitle1, loadedTenantsTitle1);
List<Tenant> loadedTenantsTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
pageLink = new TextPageLink(4, title2);
do {
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<TextPageData<Tenant>>(){}, pageLink);
loadedTenantsTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -202,8 +202,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageLink = new TextPageLink(4, title1);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<TextPageData<Tenant>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -212,8 +212,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageLink = new TextPageLink(4, title2);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<TextPageData<Tenant>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}

68
application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java

@ -27,8 +27,8 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
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.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.service.mail.TestMailService;
@ -326,14 +326,14 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
}
List<User> loadedTenantAdmins = new ArrayList<>();
PageLink pageLink = new PageLink(33);
PageData<User> pageData = null;
TextPageLink pageLink = new TextPageLink(33);
TextPageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
loadedTenantAdmins.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -345,9 +345,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
.andExpect(status().isOk());
pageLink = new PageLink(33);
pageLink = new TextPageLink(33);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertTrue(pageData.getData().isEmpty());
}
@ -393,14 +393,14 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
}
List<User> loadedTenantAdminsEmail1 = new ArrayList<>();
PageLink pageLink = new PageLink(33, 0, email1);
PageData<User> pageData = null;
TextPageLink pageLink = new TextPageLink(33, email1);
TextPageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
loadedTenantAdminsEmail1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -410,13 +410,13 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
Assert.assertEquals(tenantAdminsEmail1, loadedTenantAdminsEmail1);
List<User> loadedTenantAdminsEmail2 = new ArrayList<>();
pageLink = new PageLink(16, 0, email2);
pageLink = new TextPageLink(16, email2);
do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
loadedTenantAdminsEmail2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -430,9 +430,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, email1);
pageLink = new TextPageLink(4, email1);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -441,9 +441,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, email2);
pageLink = new TextPageLink(4, email2);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -486,14 +486,14 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
}
List<User> loadedCustomerUsers = new ArrayList<>();
PageLink pageLink = new PageLink(33);
PageData<User> pageData = null;
TextPageLink pageLink = new TextPageLink(33);
TextPageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
loadedCustomerUsers.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -565,14 +565,14 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
}
List<User> loadedCustomerUsersEmail1 = new ArrayList<>();
PageLink pageLink = new PageLink(33, 0, email1);
PageData<User> pageData = null;
TextPageLink pageLink = new TextPageLink(33, email1);
TextPageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
loadedCustomerUsersEmail1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -582,13 +582,13 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
Assert.assertEquals(customerUsersEmail1, loadedCustomerUsersEmail1);
List<User> loadedCustomerUsersEmail2 = new ArrayList<>();
pageLink = new PageLink(16, 0, email2);
pageLink = new TextPageLink(16, email2);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
loadedCustomerUsersEmail2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -602,9 +602,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, email1);
pageLink = new TextPageLink(4, email1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -613,9 +613,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, email2);
pageLink = new TextPageLink(4, email2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<TextPageData<User>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());

26
application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java

@ -22,8 +22,8 @@ import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
@ -150,14 +150,14 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
widgetsBundles.addAll(sysWidgetsBundles);
List<WidgetsBundle> loadedWidgetsBundles = new ArrayList<>();
PageLink pageLink = new PageLink(14);
PageData<WidgetsBundle> pageData;
TextPageLink pageLink = new TextPageLink(14);
TextPageData<WidgetsBundle> pageData;
do {
pageData = doGetTypedWithPageLink("/api/widgetsBundles?",
new TypeReference<PageData<WidgetsBundle>>(){}, pageLink);
new TypeReference<TextPageData<WidgetsBundle>>(){}, pageLink);
loadedWidgetsBundles.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -186,14 +186,14 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
widgetsBundles.addAll(sysWidgetsBundles);
List<WidgetsBundle> loadedWidgetsBundles = new ArrayList<>();
PageLink pageLink = new PageLink(14);
PageData<WidgetsBundle> pageData;
TextPageLink pageLink = new TextPageLink(14);
TextPageData<WidgetsBundle> pageData;
do {
pageData = doGetTypedWithPageLink("/api/widgetsBundles?",
new TypeReference<PageData<WidgetsBundle>>(){}, pageLink);
new TypeReference<TextPageData<WidgetsBundle>>(){}, pageLink);
loadedWidgetsBundles.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
@ -207,14 +207,14 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
.andExpect(status().isOk());
}
pageLink = new PageLink(17);
pageLink = new TextPageLink(17);
loadedWidgetsBundles.clear();
do {
pageData = doGetTypedWithPageLink("/api/widgetsBundles?",
new TypeReference<PageData<WidgetsBundle>>(){}, pageLink);
new TypeReference<TextPageData<WidgetsBundle>>(){}, pageLink);
loadedWidgetsBundles.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());

47
application/src/test/java/org/thingsboard/server/controller/ControllerNoSqlTestSuite.java

@ -0,0 +1,47 @@
/**
* Copyright © 2016-2020 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 org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomCassandraCQLUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.controller.nosql.*Test"})
public class ControllerNoSqlTestSuite {
@ClassRule
public static CustomCassandraCQLUnit cassandraUnit =
new CustomCassandraCQLUnit(
Arrays.asList(
new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
new ClassPathCQLDataSet("cassandra/schema-entities.cql", false, false),
new ClassPathCQLDataSet("cassandra/system-data.cql", false, false),
new ClassPathCQLDataSet("cassandra/system-test.cql", false, false)),
"cassandra-test.yaml", 30000l);
@BeforeClass
public static void cleanupInMemStorage(){
InMemoryStorage.getInstance().cleanup();
}
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/AdminControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseAdminControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class AdminControllerNoSqlTest extends BaseAdminControllerTest {
}

27
application/src/test/java/org/thingsboard/server/controller/nosql/AssetControllerNoSqlTest.java

@ -0,0 +1,27 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseAssetControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
import org.thingsboard.server.dao.util.NoSqlDao;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class AssetControllerNoSqlTest extends BaseAssetControllerTest {
}

23
application/src/test/java/org/thingsboard/server/controller/nosql/AuditLogControllerNoSqlTest.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseAuditLogControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
@DaoNoSqlTest
public class AuditLogControllerNoSqlTest extends BaseAuditLogControllerTest {
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/AuthControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseAuthControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class AuthControllerNoSqlTest extends BaseAuthControllerTest {
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/ComponentDescriptorControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseComponentDescriptorControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class ComponentDescriptorControllerNoSqlTest extends BaseComponentDescriptorControllerTest {
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/CustomerControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseCustomerControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class CustomerControllerNoSqlTest extends BaseCustomerControllerTest {
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/DashboardControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseDashboardControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class DashboardControllerNoSqlTest extends BaseDashboardControllerTest {
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/DeviceControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseDeviceControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class DeviceControllerNoSqlTest extends BaseDeviceControllerTest {
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/EntityViewControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseEntityViewControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Victor Basanets on 8/27/2017.
*/
@DaoNoSqlTest
public class EntityViewControllerNoSqlTest extends BaseEntityViewControllerTest {
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/TenantControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseTenantControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class TenantControllerNoSqlTest extends BaseTenantControllerTest {
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/UserControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseUserControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class UserControllerNoSqlTest extends BaseUserControllerTest {
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/WidgetTypeControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseWidgetTypeControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class WidgetTypeControllerNoSqlTest extends BaseWidgetTypeControllerTest {
}

26
application/src/test/java/org/thingsboard/server/controller/nosql/WidgetsBundleControllerNoSqlTest.java

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2020 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.nosql;
import org.thingsboard.server.controller.BaseWidgetsBundleControllerTest;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoNoSqlTest
public class WidgetsBundleControllerNoSqlTest extends BaseWidgetsBundleControllerTest {
}

11
application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java

@ -21,7 +21,6 @@ import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomCassandraCQLUnit;
import org.thingsboard.server.dao.CustomSqlUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@ -31,17 +30,13 @@ import java.util.Arrays;
"org.thingsboard.server.mqtt.*.nosql.*Test"})
public class MqttNoSqlTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-entities-hsql.sql", "sql/system-data.sql"),
"sql/hsql/drop-all-tables.sql",
"nosql-test.properties");
@ClassRule
public static CustomCassandraCQLUnit cassandraUnit =
new CustomCassandraCQLUnit(
Arrays.asList(
new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false)),
new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
new ClassPathCQLDataSet("cassandra/schema-entities.cql", false, false),
new ClassPathCQLDataSet("cassandra/system-data.cql", false, false)),
"cassandra-test.yaml", 30000l);
@BeforeClass

6
application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java

@ -15,7 +15,7 @@
*/
package org.thingsboard.server.mqtt.rpc;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.datastax.driver.core.utils.UUIDs;
import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -143,7 +143,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
@Test
public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception {
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}";
String nonExistentDeviceId = Uuids.timeBased().toString();
String nonExistentDeviceId = UUIDs.timeBased().toString();
String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class,
status().isNotFound());
@ -200,7 +200,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
@Test
public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception {
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}";
String nonExistentDeviceId = Uuids.timeBased().toString();
String nonExistentDeviceId = UUIDs.timeBased().toString();
String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class,
status().isNotFound());

50
application/src/test/java/org/thingsboard/server/rules/RuleEngineNoSqlTestSuite.java

@ -0,0 +1,50 @@
/**
* Copyright © 2016-2020 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.rules;
import org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomCassandraCQLUnit;
import org.thingsboard.server.dao.CustomSqlUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.rules.flow.nosql.*Test",
"org.thingsboard.server.rules.lifecycle.nosql.*Test"
})
public class RuleEngineNoSqlTestSuite {
@ClassRule
public static CustomCassandraCQLUnit cassandraUnit =
new CustomCassandraCQLUnit(
Arrays.asList(
new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
new ClassPathCQLDataSet("cassandra/schema-entities.cql", false, false),
new ClassPathCQLDataSet("cassandra/system-data.cql", false, false)),
"cassandra-test.yaml", 30000l);
@BeforeClass
public static void cleanupInMemStorage(){
InMemoryStorage.getInstance().cleanup();
}
}

7
application/src/test/java/org/thingsboard/server/rules/flow/AbstractRuleEngineFlowIntegrationTest.java

@ -16,6 +16,7 @@
package org.thingsboard.server.rules.flow;
import akka.actor.ActorRef;
import com.datastax.driver.core.utils.UUIDs;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Assert;
@ -29,7 +30,7 @@ import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode;
@ -153,7 +154,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
actorSystem.tell(qMsg, ActorRef.noSender());
Mockito.verify(tbMsgCallback, Mockito.timeout(10000)).onSuccess();
PageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000);
TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000);
List<Event> events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList());
Assert.assertEquals(2, events.size());
@ -266,7 +267,7 @@ public abstract class AbstractRuleEngineFlowIntegrationTest extends AbstractRule
Mockito.verify(tbMsgCallback, Mockito.timeout(10000)).onSuccess();
PageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), rootRuleChain.getFirstRuleNodeId(), 1000);
TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), rootRuleChain.getFirstRuleNodeId(), 1000);
List<Event> events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList());
Assert.assertEquals(2, events.size());

16
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/DefaultGuavaSession.java → application/src/test/java/org/thingsboard/server/rules/flow/nosql/RuleEngineFlowNoSqlIntegrationTest.java

@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.cassandra.guava;
package org.thingsboard.server.rules.flow.nosql;
import com.datastax.oss.driver.api.core.session.Session;
import com.datastax.oss.driver.internal.core.session.SessionWrapper;
import org.thingsboard.server.dao.service.DaoNoSqlTest;
import org.thingsboard.server.rules.flow.AbstractRuleEngineFlowIntegrationTest;
public class DefaultGuavaSession extends SessionWrapper implements GuavaSession {
public DefaultGuavaSession(Session delegate) {
super(delegate);
}
/**
* Created by Valerii Sosliuk on 8/22/2017.
*/
@DaoNoSqlTest
public class RuleEngineFlowNoSqlIntegrationTest extends AbstractRuleEngineFlowIntegrationTest {
}

5
application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java

@ -16,6 +16,7 @@
package org.thingsboard.server.rules.lifecycle;
import akka.actor.ActorRef;
import com.datastax.driver.core.utils.UUIDs;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Assert;
@ -33,7 +34,7 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode;
@ -145,7 +146,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
Mockito.verify(tbMsgCallback, Mockito.timeout(3000)).onSuccess();
PageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000);
TimePageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000);
List<Event> events = eventsPage.getData().stream().filter(filterByCustomEvent()).collect(Collectors.toList());
Assert.assertEquals(2, events.size());

25
ui-ngx/src/app/modules/home/components/alias/aliases-entity-select-panel.component.scss → application/src/test/java/org/thingsboard/server/rules/lifecycle/nosql/RuleEngineLifecycleNoSqlIntegrationTest.java

@ -13,23 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
:host {
min-width: 300px;
max-height: 150px;
overflow-x: hidden;
overflow-y: auto;
background: #fff;
border-radius: 4px;
box-shadow:
0 7px 8px -4px rgba(0, 0, 0, .2),
0 13px 19px 2px rgba(0, 0, 0, .14),
0 5px 24px 4px rgba(0, 0, 0, .12);
package org.thingsboard.server.rules.lifecycle.nosql;
@media (min-height: 350px) {
max-height: 250px;
}
import org.thingsboard.server.dao.service.DaoNoSqlTest;
import org.thingsboard.server.rules.lifecycle.AbstractRuleEngineLifecycleIntegrationTest;
.mat-content {
background-color: #fff;
}
/**
* Created by Valerii Sosliuk on 8/22/2017.
*/
@DaoNoSqlTest
public class RuleEngineLifecycleNoSqlIntegrationTest extends AbstractRuleEngineLifecycleIntegrationTest {
}

4
application/src/test/java/org/thingsboard/server/service/cluster/routing/HashPartitionServiceTest.java

@ -15,7 +15,7 @@
*/
package org.thingsboard.server.service.cluster.routing;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.datastax.driver.core.utils.UUIDs;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Before;
@ -101,7 +101,7 @@ public class HashPartitionServiceTest {
public void testDispersionOnMillionDevices() {
List<DeviceId> devices = new ArrayList<>();
for (int i = 0; i < ITERATIONS; i++) {
devices.add(new DeviceId(Uuids.timeBased()));
devices.add(new DeviceId(UUIDs.timeBased()));
}
testDevicesDispersion(devices);
}

6
application/src/test/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngineTest.java

@ -15,7 +15,7 @@
*/
package org.thingsboard.server.service.script;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.datastax.driver.core.utils.UUIDs;
import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Before;
@ -41,7 +41,7 @@ public class RuleNodeJsScriptEngineTest {
private ScriptEngine scriptEngine;
private TestNashornJsInvokeService jsSandboxService;
private EntityId ruleNodeId = new RuleNodeId(Uuids.timeBased());
private EntityId ruleNodeId = new RuleNodeId(UUIDs.timeBased());
@Before
public void beforeTest() throws Exception {
@ -247,4 +247,4 @@ public class RuleNodeJsScriptEngineTest {
}
}
}
}

48
application/src/test/java/org/thingsboard/server/system/SystemNoSqlTestSuite.java

@ -0,0 +1,48 @@
/**
* Copyright © 2016-2020 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.system;
import org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomCassandraCQLUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
/**
* @author Andrew Shvayka
*/
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.system.*NoSqlTest"})
public class SystemNoSqlTestSuite {
@ClassRule
public static CustomCassandraCQLUnit cassandraUnit =
new CustomCassandraCQLUnit(
Arrays.asList(
new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
new ClassPathCQLDataSet("cassandra/schema-entities.cql", false, false),
new ClassPathCQLDataSet("cassandra/system-data.cql", false, false)),
"cassandra-test.yaml", 30000l);
@BeforeClass
public static void cleanupInMemStorage(){
InMemoryStorage.getInstance().cleanup();
}
}

24
ui-ngx/src/app/modules/home/components/widget/lib/date-range-navigator/date-range-navigator-panel.component.scss → application/src/test/java/org/thingsboard/server/system/nosql/DeviceApiNoSqlTest.java

@ -13,21 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.tb-date-range-navigator-panel {
overflow: auto;
background: #fff;
border-radius: 4px;
box-shadow:
0 7px 8px -4px rgba(0, 0, 0, .2),
0 13px 19px 2px rgba(0, 0, 0, .14),
0 5px 24px 4px rgba(0, 0, 0, .12);
package org.thingsboard.server.system.nosql;
.mat-content {
overflow: hidden;
background-color: #fff;
}
import org.thingsboard.server.dao.service.DaoNoSqlTest;
import org.thingsboard.server.dao.util.NoSqlDao;
import org.thingsboard.server.system.BaseHttpDeviceApiTest;
.mat-padding {
padding: 16px;
}
/**
* Created by Valerii Sosliuk on 6/27/2017.
*/
@DaoNoSqlTest
public class DeviceApiNoSqlTest extends BaseHttpDeviceApiTest {
}

15
common/dao-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.0.0-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>
@ -78,13 +78,18 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-core</artifactId>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-jmx</artifactId>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-mapping</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-extras</artifactId>
<scope>provided</scope>
</dependency>
<dependency>

4
common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java

@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageData;
/**
* Created by ashvayka on 11.05.17.
@ -45,7 +45,7 @@ public interface AlarmService {
ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId);
ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query);
ListenableFuture<TimePageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query);
AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
AlarmStatus alarmStatus);

23
common/dao-api/src/main/java/org/thingsboard/server/dao/asset/AssetService.java

@ -18,21 +18,18 @@ package org.thingsboard.server.dao.asset;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.asset.AssetSearchQuery;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
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.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import java.util.List;
import java.util.Optional;
public interface AssetService {
AssetInfo findAssetInfoById(TenantId tenantId, AssetId assetId);
Asset findAssetById(TenantId tenantId, AssetId assetId);
ListenableFuture<Asset> findAssetByIdAsync(TenantId tenantId, AssetId assetId);
@ -47,25 +44,17 @@ public interface AssetService {
void deleteAsset(TenantId tenantId, AssetId assetId);
PageData<Asset> findAssetsByTenantId(TenantId tenantId, PageLink pageLink);
PageData<AssetInfo> findAssetInfosByTenantId(TenantId tenantId, PageLink pageLink);
PageData<Asset> findAssetsByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
TextPageData<Asset> findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink);
PageData<AssetInfo> findAssetInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
TextPageData<Asset> findAssetsByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink);
ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List<AssetId> assetIds);
void deleteAssetsByTenantId(TenantId tenantId);
PageData<Asset> findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink);
PageData<AssetInfo> findAssetInfosByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, PageLink pageLink);
PageData<Asset> findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink);
TextPageData<Asset> findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
PageData<AssetInfo> findAssetInfosByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, PageLink pageLink);
TextPageData<Asset> findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink);
ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<AssetId> assetIds);

10
common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java

@ -25,20 +25,20 @@ import org.thingsboard.server.common.data.id.EntityId;
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.page.PageData;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import java.util.List;
public interface AuditLogService {
PageData<AuditLog> findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, List<ActionType> actionTypes, TimePageLink pageLink);
TimePageData<AuditLog> findAuditLogsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, List<ActionType> actionTypes, TimePageLink pageLink);
PageData<AuditLog> findAuditLogsByTenantIdAndUserId(TenantId tenantId, UserId userId, List<ActionType> actionTypes, TimePageLink pageLink);
TimePageData<AuditLog> findAuditLogsByTenantIdAndUserId(TenantId tenantId, UserId userId, List<ActionType> actionTypes, TimePageLink pageLink);
PageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, List<ActionType> actionTypes, TimePageLink pageLink);
TimePageData<AuditLog> findAuditLogsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId, List<ActionType> actionTypes, TimePageLink pageLink);
PageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, List<ActionType> actionTypes, TimePageLink pageLink);
TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, List<ActionType> actionTypes, TimePageLink pageLink);
<E extends HasName, I extends EntityId> ListenableFuture<List<Void>> logEntityAction(
TenantId tenantId,

157
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/AbstractCassandraCluster.java

@ -16,50 +16,117 @@
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 com.datastax.driver.core.Cluster;
import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.HostDistance;
import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.ProtocolOptions.Compression;
import com.datastax.driver.core.Session;
import com.datastax.driver.mapping.DefaultPropertyMapper;
import com.datastax.driver.mapping.Mapper;
import com.datastax.driver.mapping.MappingConfiguration;
import com.datastax.driver.mapping.MappingManager;
import com.datastax.driver.mapping.PropertyAccessStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
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 javax.annotation.PreDestroy;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Slf4j
public abstract class AbstractCassandraCluster {
private static final String COMMA = ",";
private static final String COLON = ":";
@Value("${cassandra.cluster_name}")
private String clusterName;
@Value("${cassandra.url}")
private String url;
@Value("${cassandra.compression}")
private String compression;
@Value("${cassandra.ssl}")
private Boolean ssl;
@Value("${cassandra.jmx}")
private Boolean jmx;
@Value("${cassandra.metrics}")
private Boolean metrics;
@Value("${cassandra.credentials}")
private Boolean credentials;
@Value("${cassandra.username}")
private String username;
@Value("${cassandra.password}")
private String password;
@Value("${cassandra.init_timeout_ms}")
private long initTimeout;
@Value("${cassandra.init_retry_interval_ms}")
private long initRetryInterval;
@Value("${cassandra.max_requests_per_connection_local:32768}")
private int max_requests_local;
@Value("${cassandra.max_requests_per_connection_remote:32768}")
private int max_requests_remote;
@Autowired
private CassandraDriverOptions driverOptions;
private CassandraSocketOptions socketOpts;
@Autowired
private CassandraQueryOptions queryOpts;
@Autowired
private Environment environment;
private GuavaSessionBuilder sessionBuilder;
private Cluster cluster;
private Cluster.Builder clusterBuilder;
private GuavaSession session;
private Session session;
private JmxReporter reporter;
private MappingManager mappingManager;
public <T> Mapper<T> getMapper(Class<T> clazz) {
return mappingManager.mapper(clazz);
}
private String keyspaceName;
protected void init(String keyspaceName) {
this.keyspaceName = keyspaceName;
this.sessionBuilder = GuavaSessionUtils.builder().withConfigLoader(this.driverOptions.getLoader());
this.clusterBuilder = Cluster.builder()
.addContactPointsWithPorts(getContactPoints(url))
.withClusterName(clusterName)
.withSocketOptions(socketOpts.getOpts())
.withPoolingOptions(new PoolingOptions()
.setMaxRequestsPerConnection(HostDistance.LOCAL, max_requests_local)
.setMaxRequestsPerConnection(HostDistance.REMOTE, max_requests_remote));
this.clusterBuilder.withQueryOptions(queryOpts.getOpts());
this.clusterBuilder.withCompression(StringUtils.isEmpty(compression) ? Compression.NONE : Compression.valueOf(compression.toUpperCase()));
if (ssl) {
this.clusterBuilder.withSSL();
}
if (!jmx) {
this.clusterBuilder.withoutJMXReporting();
}
if (!metrics) {
this.clusterBuilder.withoutMetrics();
}
if (credentials) {
this.clusterBuilder.withCredentials(username, password);
}
if (!isInstall()) {
initSession();
}
}
public GuavaSession getSession() {
public Cluster getCluster() {
return cluster;
}
public Session getSession() {
if (!isInstall()) {
return session;
} else {
@ -79,40 +146,64 @@ public abstract class AbstractCassandraCluster {
}
private void initSession() {
if (this.keyspaceName != null) {
this.sessionBuilder.withKeyspace(this.keyspaceName);
}
this.sessionBuilder.withLocalDatacenter("datacenter1");
session = sessionBuilder.build();
if (this.metrics && this.jmx) {
MetricRegistry registry =
session.getMetrics().orElseThrow(
() -> new IllegalStateException("Metrics are disabled"))
.getRegistry();
this.reporter =
JmxReporter.forRegistry(registry)
.inDomain("com.datastax.oss.driver")
.build();
this.reporter.start();
long endTime = System.currentTimeMillis() + initTimeout;
while (System.currentTimeMillis() < endTime) {
try {
cluster = clusterBuilder.build();
cluster.init();
if (this.keyspaceName != null) {
session = cluster.connect(keyspaceName);
} else {
session = cluster.connect();
}
// For Cassandra Driver version 3.5.0
DefaultPropertyMapper propertyMapper = new DefaultPropertyMapper();
propertyMapper.setPropertyAccessStrategy(PropertyAccessStrategy.FIELDS);
MappingConfiguration configuration = MappingConfiguration.builder().withPropertyMapper(propertyMapper).build();
mappingManager = new MappingManager(session, configuration);
// For Cassandra Driver version 3.0.0
// mappingManager = new MappingManager(session);
break;
} catch (Exception e) {
log.warn("Failed to initialize cassandra cluster due to {}. Will retry in {} ms", e.getMessage(), initRetryInterval);
try {
Thread.sleep(initRetryInterval);
} catch (InterruptedException ie) {
log.warn("Failed to wait until retry", ie);
Thread.currentThread().interrupt();
}
}
}
}
@PreDestroy
public void close() {
if (reporter != null) {
reporter.stop();
if (cluster != null) {
cluster.close();
}
if (session != null) {
session.close();
}
private List<InetSocketAddress> getContactPoints(String url) {
List<InetSocketAddress> result;
if (StringUtils.isBlank(url)) {
result = Collections.emptyList();
} else {
result = new ArrayList<>();
for (String hostPort : url.split(COMMA)) {
String host = hostPort.split(COLON)[0];
Integer port = Integer.valueOf(hostPort.split(COLON)[1]);
result.add(new InetSocketAddress(host, port));
}
}
return result;
}
public ConsistencyLevel getDefaultReadConsistencyLevel() {
return driverOptions.getDefaultReadConsistencyLevel();
return queryOpts.getDefaultReadConsistencyLevel();
}
public ConsistencyLevel getDefaultWriteConsistencyLevel() {
return driverOptions.getDefaultWriteConsistencyLevel();
return queryOpts.getDefaultWriteConsistencyLevel();
}
}

232
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraDriverOptions.java

@ -1,232 +0,0 @@
/**
* Copyright © 2016-2020 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.dao.cassandra;
import com.datastax.oss.driver.api.core.ConsistencyLevel;
import com.datastax.oss.driver.api.core.DefaultConsistencyLevel;
import com.datastax.oss.driver.api.core.config.DefaultDriverOption;
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.DefaultSessionMetric;
import com.datastax.oss.driver.api.core.metrics.DefaultNodeMetric;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.thingsboard.server.dao.util.NoSqlAnyDao;
import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Component
@Configuration
@Data
@NoSqlAnyDao
public class CassandraDriverOptions {
private static final String COMMA = ",";
@Value("${cassandra.cluster_name}")
private String clusterName;
@Value("${cassandra.url}")
private String url;
@Value("${cassandra.socket.connect_timeout}")
private int connectTimeoutMillis;
@Value("${cassandra.socket.read_timeout}")
private int readTimeoutMillis;
@Value("${cassandra.socket.keep_alive}")
private Boolean keepAlive;
@Value("${cassandra.socket.reuse_address}")
private Boolean reuseAddress;
@Value("${cassandra.socket.so_linger}")
private Integer soLinger;
@Value("${cassandra.socket.tcp_no_delay}")
private Boolean tcpNoDelay;
@Value("${cassandra.socket.receive_buffer_size}")
private Integer receiveBufferSize;
@Value("${cassandra.socket.send_buffer_size}")
private Integer sendBufferSize;
@Value("${cassandra.max_requests_per_connection_local:32768}")
private int max_requests_local;
@Value("${cassandra.max_requests_per_connection_remote:32768}")
private int max_requests_remote;
@Value("${cassandra.query.default_fetch_size}")
private Integer defaultFetchSize;
@Value("${cassandra.query.read_consistency_level}")
private String readConsistencyLevel;
@Value("${cassandra.query.write_consistency_level}")
private String writeConsistencyLevel;
@Value("${cassandra.compression}")
private String compression;
@Value("${cassandra.ssl}")
private Boolean ssl;
@Value("${cassandra.metrics}")
private Boolean metrics;
@Value("${cassandra.credentials}")
private Boolean credentials;
@Value("${cassandra.username}")
private String username;
@Value("${cassandra.password}")
private String password;
@Value("${cassandra.init_timeout_ms}")
private long initTimeout;
@Value("${cassandra.init_retry_interval_ms}")
private long initRetryInterval;
private DriverConfigLoader loader;
private ConsistencyLevel defaultReadConsistencyLevel;
private ConsistencyLevel defaultWriteConsistencyLevel;
@PostConstruct
public void initLoader() {
ProgrammaticDriverConfigLoaderBuilder driverConfigBuilder =
DriverConfigLoader.programmaticBuilder();
driverConfigBuilder
.withStringList(DefaultDriverOption.CONTACT_POINTS, getContactPoints(url))
.withString(DefaultDriverOption.SESSION_NAME, clusterName);
this.initSocketOptions(driverConfigBuilder);
this.initPoolingOptions(driverConfigBuilder);
this.initQueryOptions(driverConfigBuilder);
driverConfigBuilder.withString(DefaultDriverOption.PROTOCOL_COMPRESSION,
StringUtils.isEmpty(this.compression) ? "none" : this.compression.toLowerCase());
if (this.ssl) {
driverConfigBuilder.withString(DefaultDriverOption.SSL_ENGINE_FACTORY_CLASS,
"DefaultSslEngineFactory");
}
if (this.metrics) {
driverConfigBuilder.withStringList(DefaultDriverOption.METRICS_SESSION_ENABLED,
Arrays.asList(DefaultSessionMetric.CONNECTED_NODES.getPath(),
DefaultSessionMetric.CQL_REQUESTS.getPath()));
driverConfigBuilder.withStringList(DefaultDriverOption.METRICS_NODE_ENABLED,
Arrays.asList(DefaultNodeMetric.OPEN_CONNECTIONS.getPath(),
DefaultNodeMetric.IN_FLIGHT.getPath()));
}
if (this.credentials) {
driverConfigBuilder.withString(DefaultDriverOption.AUTH_PROVIDER_CLASS,
"PlainTextAuthProvider");
driverConfigBuilder.withString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME,
this.username);
driverConfigBuilder.withString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD,
this.password);
}
driverConfigBuilder.withBoolean(DefaultDriverOption.RECONNECT_ON_INIT,
true);
driverConfigBuilder.withString(DefaultDriverOption.RECONNECTION_POLICY_CLASS,
"ExponentialReconnectionPolicy");
driverConfigBuilder.withDuration(DefaultDriverOption.RECONNECTION_BASE_DELAY,
Duration.ofMillis(this.initRetryInterval));
driverConfigBuilder.withDuration(DefaultDriverOption.RECONNECTION_MAX_DELAY,
Duration.ofMillis(this.initTimeout));
this.loader = driverConfigBuilder.build();
}
protected ConsistencyLevel getDefaultReadConsistencyLevel() {
if (defaultReadConsistencyLevel == null) {
if (readConsistencyLevel != null) {
defaultReadConsistencyLevel = DefaultConsistencyLevel.valueOf(readConsistencyLevel.toUpperCase());
} else {
defaultReadConsistencyLevel = DefaultConsistencyLevel.ONE;
}
}
return defaultReadConsistencyLevel;
}
protected ConsistencyLevel getDefaultWriteConsistencyLevel() {
if (defaultWriteConsistencyLevel == null) {
if (writeConsistencyLevel != null) {
defaultWriteConsistencyLevel = DefaultConsistencyLevel.valueOf(writeConsistencyLevel.toUpperCase());
} else {
defaultWriteConsistencyLevel = DefaultConsistencyLevel.ONE;
}
}
return defaultWriteConsistencyLevel;
}
private void initSocketOptions(ProgrammaticDriverConfigLoaderBuilder driverConfigBuilder) {
driverConfigBuilder.withDuration(DefaultDriverOption.CONNECTION_CONNECT_TIMEOUT,
Duration.ofMillis(this.connectTimeoutMillis));
driverConfigBuilder.withDuration(DefaultDriverOption.REQUEST_TIMEOUT,
Duration.ofMillis(this.readTimeoutMillis));
if (this.keepAlive != null) {
driverConfigBuilder.withBoolean(DefaultDriverOption.SOCKET_KEEP_ALIVE,
this.keepAlive);
}
if (this.reuseAddress != null) {
driverConfigBuilder.withBoolean(DefaultDriverOption.SOCKET_REUSE_ADDRESS,
this.reuseAddress);
}
if (this.soLinger != null) {
driverConfigBuilder.withInt(DefaultDriverOption.SOCKET_LINGER_INTERVAL,
this.soLinger);
}
if (this.tcpNoDelay != null) {
driverConfigBuilder.withBoolean(DefaultDriverOption.SOCKET_TCP_NODELAY,
this.tcpNoDelay);
}
if (this.receiveBufferSize != null) {
driverConfigBuilder.withInt(DefaultDriverOption.SOCKET_RECEIVE_BUFFER_SIZE,
this.receiveBufferSize);
}
if (this.sendBufferSize != null) {
driverConfigBuilder.withInt(DefaultDriverOption.SOCKET_SEND_BUFFER_SIZE,
this.sendBufferSize);
}
}
private void initPoolingOptions(ProgrammaticDriverConfigLoaderBuilder driverConfigBuilder) {
driverConfigBuilder.withInt(DefaultDriverOption.CONNECTION_MAX_REQUESTS,
this.max_requests_local);
}
private void initQueryOptions(ProgrammaticDriverConfigLoaderBuilder driverConfigBuilder) {
driverConfigBuilder.withInt(DefaultDriverOption.REQUEST_PAGE_SIZE,
this.defaultFetchSize);
}
private List<String> getContactPoints(String url) {
List<String> result;
if (StringUtils.isBlank(url)) {
result = Collections.emptyList();
} else {
result = new ArrayList<>();
for (String hostPort : url.split(COMMA)) {
result.add(hostPort);
}
}
return result;
}
}

73
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraQueryOptions.java

@ -0,0 +1,73 @@
/**
* Copyright © 2016-2020 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.dao.cassandra;
import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.QueryOptions;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.thingsboard.server.dao.util.NoSqlAnyDao;
import javax.annotation.PostConstruct;
@Component
@Configuration
@Data
@NoSqlAnyDao
public class CassandraQueryOptions {
@Value("${cassandra.query.default_fetch_size}")
private Integer defaultFetchSize;
@Value("${cassandra.query.read_consistency_level}")
private String readConsistencyLevel;
@Value("${cassandra.query.write_consistency_level}")
private String writeConsistencyLevel;
private QueryOptions opts;
private ConsistencyLevel defaultReadConsistencyLevel;
private ConsistencyLevel defaultWriteConsistencyLevel;
@PostConstruct
public void initOpts(){
opts = new QueryOptions();
opts.setFetchSize(defaultFetchSize);
}
protected ConsistencyLevel getDefaultReadConsistencyLevel() {
if (defaultReadConsistencyLevel == null) {
if (readConsistencyLevel != null) {
defaultReadConsistencyLevel = ConsistencyLevel.valueOf(readConsistencyLevel.toUpperCase());
} else {
defaultReadConsistencyLevel = ConsistencyLevel.ONE;
}
}
return defaultReadConsistencyLevel;
}
protected ConsistencyLevel getDefaultWriteConsistencyLevel() {
if (defaultWriteConsistencyLevel == null) {
if (writeConsistencyLevel != null) {
defaultWriteConsistencyLevel = ConsistencyLevel.valueOf(writeConsistencyLevel.toUpperCase());
} else {
defaultWriteConsistencyLevel = ConsistencyLevel.ONE;
}
}
return defaultWriteConsistencyLevel;
}
}

76
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/CassandraSocketOptions.java

@ -0,0 +1,76 @@
/**
* Copyright © 2016-2020 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.dao.cassandra;
import com.datastax.driver.core.SocketOptions;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.thingsboard.server.dao.util.NoSqlAnyDao;
import javax.annotation.PostConstruct;
@Component
@Configuration
@Data
@NoSqlAnyDao
public class CassandraSocketOptions {
@Value("${cassandra.socket.connect_timeout}")
private int connectTimeoutMillis;
@Value("${cassandra.socket.read_timeout}")
private int readTimeoutMillis;
@Value("${cassandra.socket.keep_alive}")
private Boolean keepAlive;
@Value("${cassandra.socket.reuse_address}")
private Boolean reuseAddress;
@Value("${cassandra.socket.so_linger}")
private Integer soLinger;
@Value("${cassandra.socket.tcp_no_delay}")
private Boolean tcpNoDelay;
@Value("${cassandra.socket.receive_buffer_size}")
private Integer receiveBufferSize;
@Value("${cassandra.socket.send_buffer_size}")
private Integer sendBufferSize;
private SocketOptions opts;
@PostConstruct
public void initOpts() {
opts = new SocketOptions();
opts.setConnectTimeoutMillis(connectTimeoutMillis);
opts.setReadTimeoutMillis(readTimeoutMillis);
if (keepAlive != null) {
opts.setKeepAlive(keepAlive);
}
if (reuseAddress != null) {
opts.setReuseAddress(reuseAddress);
}
if (soLinger != null) {
opts.setSoLinger(soLinger);
}
if (tcpNoDelay != null) {
opts.setTcpNoDelay(tcpNoDelay);
}
if (receiveBufferSize != null) {
opts.setReceiveBufferSize(receiveBufferSize);
}
if (sendBufferSize != null) {
opts.setSendBufferSize(sendBufferSize);
}
}
}

84
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/GuavaDriverContext.java

@ -1,84 +0,0 @@
/**
* Copyright © 2016-2020 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.dao.cassandra.guava;
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
import com.datastax.oss.driver.api.core.cql.PrepareRequest;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.metadata.Node;
import com.datastax.oss.driver.api.core.metadata.NodeStateListener;
import com.datastax.oss.driver.api.core.metadata.schema.SchemaChangeListener;
import com.datastax.oss.driver.api.core.session.ProgrammaticArguments;
import com.datastax.oss.driver.api.core.tracker.RequestTracker;
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
import com.datastax.oss.driver.internal.core.context.DefaultDriverContext;
import com.datastax.oss.driver.internal.core.cql.CqlPrepareAsyncProcessor;
import com.datastax.oss.driver.internal.core.cql.CqlPrepareSyncProcessor;
import com.datastax.oss.driver.internal.core.cql.CqlRequestAsyncProcessor;
import com.datastax.oss.driver.internal.core.cql.CqlRequestSyncProcessor;
import com.datastax.oss.driver.internal.core.session.RequestProcessorRegistry;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
/**
* A Custom {@link DefaultDriverContext} that overrides {@link #getRequestProcessorRegistry()} to
* return a {@link RequestProcessorRegistry} that includes processors for returning guava futures.
*/
public class GuavaDriverContext extends DefaultDriverContext {
public GuavaDriverContext(
DriverConfigLoader configLoader,
List<TypeCodec<?>> typeCodecs,
NodeStateListener nodeStateListener,
SchemaChangeListener schemaChangeListener,
RequestTracker requestTracker,
Map<String, String> localDatacenters,
Map<String, Predicate<Node>> nodeFilters,
ClassLoader classLoader) {
super(
configLoader,
ProgrammaticArguments.builder()
.addTypeCodecs(typeCodecs.toArray(new TypeCodec<?>[0]))
.withNodeStateListener(nodeStateListener)
.withSchemaChangeListener(schemaChangeListener)
.withRequestTracker(requestTracker)
.withLocalDatacenters(localDatacenters)
.withNodeFilters(nodeFilters)
.withClassLoader(classLoader)
.build());
}
@Override
public RequestProcessorRegistry buildRequestProcessorRegistry() {
// Register the typical request processors, except instead of the normal async processors,
// use GuavaRequestAsyncProcessor to return ListenableFutures in async methods.
CqlRequestAsyncProcessor cqlRequestAsyncProcessor = new CqlRequestAsyncProcessor();
CqlPrepareAsyncProcessor cqlPrepareAsyncProcessor = new CqlPrepareAsyncProcessor();
CqlRequestSyncProcessor cqlRequestSyncProcessor =
new CqlRequestSyncProcessor(cqlRequestAsyncProcessor);
return new RequestProcessorRegistry(
getSessionName(),
cqlRequestSyncProcessor,
new CqlPrepareSyncProcessor(cqlPrepareAsyncProcessor),
new GuavaRequestAsyncProcessor<>(
cqlRequestAsyncProcessor, Statement.class, GuavaSession.ASYNC),
new GuavaRequestAsyncProcessor<>(
cqlPrepareAsyncProcessor, PrepareRequest.class, GuavaSession.ASYNC_PREPARED));
}
}

79
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/GuavaRequestAsyncProcessor.java

@ -1,79 +0,0 @@
/**
* Copyright © 2016-2020 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.dao.cassandra.guava;
import com.datastax.oss.driver.api.core.session.Request;
import com.datastax.oss.driver.api.core.type.reflect.GenericType;
import com.datastax.oss.driver.internal.core.context.InternalDriverContext;
import com.datastax.oss.driver.internal.core.session.DefaultSession;
import com.datastax.oss.driver.internal.core.session.RequestProcessor;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import java.util.concurrent.CompletionStage;
/**
* Wraps a {@link RequestProcessor} that returns {@link CompletionStage}s and converts them to a
* {@link ListenableFuture}s.
*
* @param <T> The type of request
* @param <U> The type of responses enclosed in the future response.
*/
public class GuavaRequestAsyncProcessor<T extends Request, U>
implements RequestProcessor<T, ListenableFuture<U>> {
private final RequestProcessor<T, CompletionStage<U>> subProcessor;
private final GenericType resultType;
private final Class<?> requestClass;
GuavaRequestAsyncProcessor(
RequestProcessor<T, CompletionStage<U>> subProcessor,
Class<?> requestClass,
GenericType resultType) {
this.subProcessor = subProcessor;
this.requestClass = requestClass;
this.resultType = resultType;
}
@Override
public boolean canProcess(Request request, GenericType resultType) {
return requestClass.isInstance(request) && resultType.equals(this.resultType);
}
@Override
public ListenableFuture<U> process(
T request, DefaultSession session, InternalDriverContext context, String sessionLogPrefix) {
SettableFuture<U> future = SettableFuture.create();
subProcessor
.process(request, session, context, sessionLogPrefix)
.whenComplete(
(r, ex) -> {
if (ex != null) {
future.setException(ex);
} else {
future.set(r);
}
});
return future;
}
@Override
public ListenableFuture<U> newFailure(RuntimeException error) {
return Futures.immediateFailedFuture(error);
}
}

51
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/GuavaSession.java

@ -1,51 +0,0 @@
/**
* Copyright © 2016-2020 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.dao.cassandra.guava;
import com.datastax.oss.driver.api.core.cql.AsyncResultSet;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
import com.datastax.oss.driver.api.core.cql.Statement;
import com.datastax.oss.driver.api.core.cql.SyncCqlSession;
import com.datastax.oss.driver.api.core.session.Session;
import com.datastax.oss.driver.api.core.type.reflect.GenericType;
import com.datastax.oss.driver.internal.core.cql.DefaultPrepareRequest;
import com.google.common.util.concurrent.ListenableFuture;
public interface GuavaSession extends Session, SyncCqlSession {
GenericType<ListenableFuture<AsyncResultSet>> ASYNC =
new GenericType<ListenableFuture<AsyncResultSet>>() {};
GenericType<ListenableFuture<PreparedStatement>> ASYNC_PREPARED =
new GenericType<ListenableFuture<PreparedStatement>>() {};
default ListenableFuture<AsyncResultSet> executeAsync(Statement<?> statement) {
return this.execute(statement, ASYNC);
}
default ListenableFuture<AsyncResultSet> executeAsync(String statement) {
return this.executeAsync(SimpleStatement.newInstance(statement));
}
default ListenableFuture<PreparedStatement> prepareAsync(SimpleStatement statement) {
return this.execute(new DefaultPrepareRequest(statement), ASYNC_PREPARED);
}
default ListenableFuture<PreparedStatement> prepareAsync(String statement) {
return this.prepareAsync(SimpleStatement.newInstance(statement));
}
}

59
common/dao-api/src/main/java/org/thingsboard/server/dao/cassandra/guava/GuavaSessionBuilder.java

@ -1,59 +0,0 @@
/**
* Copyright © 2016-2020 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.dao.cassandra.guava;
import com.datastax.oss.driver.api.core.CqlSession;
import com.datastax.oss.driver.api.core.config.DriverConfigLoader;
import com.datastax.oss.driver.api.core.context.DriverContext;
import com.datastax.oss.driver.api.core.metadata.Node;
import com.datastax.oss.driver.api.core.metadata.NodeStateListener;
import com.datastax.oss.driver.api.core.metadata.schema.SchemaChangeListener;
import com.datastax.oss.driver.api.core.session.SessionBuilder;
import com.datastax.oss.driver.api.core.tracker.RequestTracker;
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
public class GuavaSessionBuilder extends SessionBuilder<GuavaSessionBuilder, GuavaSession> {
@Override
protected DriverContext buildContext(
DriverConfigLoader configLoader,
List<TypeCodec<?>> typeCodecs,
NodeStateListener nodeStateListener,
SchemaChangeListener schemaChangeListener,
RequestTracker requestTracker,
Map<String, String> localDatacenters,
Map<String, Predicate<Node>> nodeFilters,
ClassLoader classLoader) {
return new GuavaDriverContext(
configLoader,
typeCodecs,
nodeStateListener,
schemaChangeListener,
requestTracker,
localDatacenters,
nodeFilters,
classLoader);
}
@Override
protected GuavaSession wrap(@NonNull CqlSession defaultSession) {
return new DefaultGuavaSession(defaultSession);
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save