diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index f384817465..99f31323f2 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -119,6 +119,11 @@ public class ThingsboardInstallService { case "2.4.0": log.info("Upgrading ThingsBoard from version 2.4.0 to 2.4.1 ..."); + case "2.4.1": + log.info("Upgrading ThingsBoard from version 2.4.1 to 2.4.2 ..."); + + databaseUpgradeService.upgradeDatabase("2.4.1"); + log.info("Updating system data..."); systemDataLoaderService.deleteSystemWidgetBundle("charts"); diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index c73c12e65f..68ca728823 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -267,6 +267,15 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { } 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 { + cluster.getSession().execute(updateAssetTableStmt); + Thread.sleep(2500); + } catch (InvalidQueryException e) {} + log.info("Schema updated."); + break; default: throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index d086e4be8b..e87fd0fce7 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -176,6 +176,15 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { log.info("Schema updated."); } break; + case "2.4.1": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema ..."); + try { + conn.createStatement().execute("ALTER TABLE asset ADD COLUMN label varchar(255)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script + } catch (Exception e) {} + log.info("Schema updated."); + } + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index 174044de28..c409cd2a37 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -31,6 +31,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements private CustomerId customerId; private String name; private String type; + private String label; public Asset() { super(); @@ -46,6 +47,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements this.customerId = asset.getCustomerId(); this.name = asset.getName(); this.type = asset.getType(); + this.label = asset.getLabel(); } public TenantId getTenantId() { @@ -81,6 +83,14 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements this.type = type; } + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + @Override public String getSearchText() { return getName(); @@ -97,6 +107,8 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements builder.append(name); builder.append(", type="); builder.append(type); + builder.append(", label="); + builder.append(label); builder.append(", additionalInfo="); builder.append(getAdditionalInfo()); builder.append(", createdTime="); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index f065443489..c466c034fc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -197,6 +197,7 @@ public class ModelConstants { public static final String ASSET_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; public static final String ASSET_NAME_PROPERTY = "name"; public static final String ASSET_TYPE_PROPERTY = "type"; + public static final String ASSET_LABEL_PROPERTY = "label"; public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java index a951b970f3..8313b44afe 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/AssetEntity.java @@ -37,6 +37,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_ import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_LABEL_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @@ -64,6 +65,9 @@ public final class AssetEntity implements SearchTextEntity { @Column(name = ASSET_NAME_PROPERTY) private String name; + @Column(name = ASSET_LABEL_PROPERTY) + private String label; + @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; @@ -86,6 +90,7 @@ public final class AssetEntity implements SearchTextEntity { } this.name = asset.getName(); this.type = asset.getType(); + this.label = asset.getLabel(); this.additionalInfo = asset.getAdditionalInfo(); } @@ -163,8 +168,9 @@ public final class AssetEntity implements SearchTextEntity { } asset.setName(name); asset.setType(type); + asset.setLabel(label); asset.setAdditionalInfo(additionalInfo); return asset; } -} \ No newline at end of file +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java index 043a02b813..bfc8020a7b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AssetEntity.java @@ -40,6 +40,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.ASSET_CUSTOMER_ID_ import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ASSET_LABEL_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Data @@ -61,6 +62,9 @@ public final class AssetEntity extends BaseSqlEntity implements SearchTex @Column(name = ASSET_TYPE_PROPERTY) private String type; + @Column(name = ASSET_LABEL_PROPERTY) + private String label; + @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; @@ -84,6 +88,7 @@ public final class AssetEntity extends BaseSqlEntity implements SearchTex } this.name = asset.getName(); this.type = asset.getType(); + this.label = asset.getLabel(); this.additionalInfo = asset.getAdditionalInfo(); } @@ -113,8 +118,9 @@ public final class AssetEntity extends BaseSqlEntity implements SearchTex } asset.setName(name); asset.setType(type); + asset.setLabel(label); asset.setAdditionalInfo(additionalInfo); return asset; } -} \ No newline at end of file +} diff --git a/dao/src/main/resources/cassandra/schema-entities.cql b/dao/src/main/resources/cassandra/schema-entities.cql index 611c08d5ef..a07e27cbe1 100644 --- a/dao/src/main/resources/cassandra/schema-entities.cql +++ b/dao/src/main/resources/cassandra/schema-entities.cql @@ -244,6 +244,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.asset ( customer_id timeuuid, name text, type text, + label text, search_text text, additional_info text, PRIMARY KEY (id, tenant_id, customer_id, type) @@ -711,4 +712,4 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_ent AND search_text IS NOT NULL AND id IS NOT NULL PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id, type) - WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); \ No newline at end of file + WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 2903ca2fb1..089ed28afc 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -42,6 +42,7 @@ CREATE TABLE IF NOT EXISTS asset ( additional_info varchar, customer_id varchar(31), name varchar(255), + label varchar(255), search_text varchar(255), tenant_id varchar(31), type varchar(255) diff --git a/ui/src/app/api/entity.service.js b/ui/src/app/api/entity.service.js index f39615c933..5ce4fd7108 100644 --- a/ui/src/app/api/entity.service.js +++ b/ui/src/app/api/entity.service.js @@ -1130,19 +1130,12 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device let statisticalInfo = {}; let newEntity = { name: entityParameters.name, - type: entityParameters.type + type: entityParameters.type, + label: entityParameters.label }; - let promise; - switch (entityType) { - case types.entityType.device: - promise = deviceService.saveDevice(newEntity, config); - break; - case types.entityType.asset: - promise = assetService.saveAsset(newEntity, true, config); - break; - } + let saveEntityPromise = getEntitySavePromise(entityType, newEntity, config); - promise.then(function success(response) { + saveEntityPromise.then(function success(response) { saveEntityRelation(entityType, response.id, entityParameters, config).then(function success() { statisticalInfo.create = { entity: 1 @@ -1166,7 +1159,15 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device break; } findIdEntity.then(function success(response) { - saveEntityRelation(entityType, response.id, entityParameters, config).then(function success() { + let promises = []; + if(response.label !== entityParameters.label || response.type !== entityParameters.type){ + response.label = entityParameters.label; + response.type = entityParameters.type; + promises.push(getEntitySavePromise(entityType, response, config)); + } + promises.push(saveEntityRelation(entityType, response.id, entityParameters, config)); + + $q.all(promises).then(function success() { statisticalInfo.update = { entity: 1 }; @@ -1193,6 +1194,19 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device return deferred.promise; } + function getEntitySavePromise(entityType, newEntity, config) { + let promise; + switch (entityType) { + case types.entityType.device: + promise = deviceService.saveDevice(newEntity, config); + break; + case types.entityType.asset: + promise = assetService.saveAsset(newEntity, true, config); + break; + } + return promise; + } + function getRelatedEntity(entityId, keys, typeTranslatePrefix) { var deferred = $q.defer(); getEntityPromise(entityId.entityType, entityId.id, {ignoreLoading: true}).then( diff --git a/ui/src/app/asset/asset-fieldset.tpl.html b/ui/src/app/asset/asset-fieldset.tpl.html index cc5b14c3cc..1514c90d8a 100644 --- a/ui/src/app/asset/asset-fieldset.tpl.html +++ b/ui/src/app/asset/asset-fieldset.tpl.html @@ -63,6 +63,10 @@ ng-model="asset.type" entity-type="types.entityType.asset"> + + + + diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js index 37ccd91302..e6e65bbecf 100644 --- a/ui/src/app/common/types.constant.js +++ b/ui/src/app/common/types.constant.js @@ -369,6 +369,10 @@ export default angular.module('thingsboard.types', []) name: 'import.column-type.type', value: 'type' }, + label: { + name: 'import.column-type.label', + value: 'label' + }, clientAttribute: { name: 'import.column-type.client-attribute', value: 'CLIENT_ATTRIBUTE' diff --git a/ui/src/app/import-export/import-dialog-csv.controller.js b/ui/src/app/import-export/import-dialog-csv.controller.js index a7b5329205..e11f59c5b4 100644 --- a/ui/src/app/import-export/import-dialog-csv.controller.js +++ b/ui/src/app/import-export/import-dialog-csv.controller.js @@ -98,7 +98,7 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo vm.columnsParam = []; var columnParam = {}; for (var i = 0; i < parseData.headers.length; i++) { - if (vm.importParameters.isHeader && parseData.headers[i].search(/^(name|type)$/im) === 0) { + if (vm.importParameters.isHeader && parseData.headers[i].search(/^(name|type|label)$/im) === 0) { columnParam = { type: types.importEntityColumnType[parseData.headers[i].toLowerCase()].value, key: parseData.headers[i].toLowerCase(), @@ -126,6 +126,7 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo var entityData = { name: "", type: "", + label: "", accessToken: "", attributes: { server: [], @@ -162,6 +163,9 @@ export default function ImportDialogCsvController($scope, $mdDialog, toast, impo case types.importEntityColumnType.type.value: entityData.type = importData.rows[i][j]; break; + case types.importEntityColumnType.label.value: + entityData.label = importData.rows[i][j]; + break; } } entitiesData.push(entityData); diff --git a/ui/src/app/import-export/table-columns-assignment.directive.js b/ui/src/app/import-export/table-columns-assignment.directive.js index a645e07392..5d96ba1c83 100644 --- a/ui/src/app/import-export/table-columns-assignment.directive.js +++ b/ui/src/app/import-export/table-columns-assignment.directive.js @@ -44,6 +44,7 @@ function TableColumnsAssignmentController($scope, types, $timeout) { vm.columnTypes.name = types.importEntityColumnType.name; vm.columnTypes.type = types.importEntityColumnType.type; + vm.columnTypes.label = types.importEntityColumnType.label; switch (vm.entityType) { case types.entityType.device: @@ -62,6 +63,7 @@ function TableColumnsAssignmentController($scope, types, $timeout) { if (newVal) { var isSelectName = false; var isSelectType = false; + var isSelectLabel = false; var isSelectCredentials = false; for (var i = 0; i < newVal.length; i++) { switch (newVal[i].type) { @@ -71,6 +73,9 @@ function TableColumnsAssignmentController($scope, types, $timeout) { case types.importEntityColumnType.type.value: isSelectType = true; break; + case types.importEntityColumnType.label.value: + isSelectLabel = true; + break; case types.importEntityColumnType.accessToken.value: isSelectCredentials = true; break; @@ -84,6 +89,7 @@ function TableColumnsAssignmentController($scope, types, $timeout) { $timeout(function () { vm.columnTypes.name.disable = isSelectName; vm.columnTypes.type.disable = isSelectType; + vm.columnTypes.label.disable = isSelectLabel; if (angular.isDefined(vm.columnTypes.accessToken)) { vm.columnTypes.accessToken.disable = isSelectCredentials; } diff --git a/ui/src/app/import-export/table-columns-assignment.tpl.html b/ui/src/app/import-export/table-columns-assignment.tpl.html index f00a08251c..b1eeafb8a7 100644 --- a/ui/src/app/import-export/table-columns-assignment.tpl.html +++ b/ui/src/app/import-export/table-columns-assignment.tpl.html @@ -41,6 +41,7 @@