diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 13ef646eaa..f1ff6aa3c6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -51,8 +51,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.asset.AssetBulkImportService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportResult; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.service.entitiy.asset.TbAssetService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 183b5ea048..895e711073 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -66,8 +66,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.device.DeviceBulkImportService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportResult; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.service.entitiy.device.TbDeviceService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index b3c1792b6a..5b2c6a6a9d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -53,8 +53,8 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.EdgeBulkImportService; import org.thingsboard.server.service.entitiy.edge.TbEdgeService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportResult; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java index 2e37bcbe59..65b5f10407 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -310,15 +310,13 @@ public class EntitiesVersionControlController extends BaseController { "Each entity item in the result has `externalId` property. " + "Entities order will be the same as in the repository." + TENANT_AUTHORITY_PARAGRAPH) - @GetMapping(value = "/entity/{entityType}/{versionId}", params = {"branch"}) + @GetMapping(value = "/entity/{entityType}/{versionId}") public DeferredResult> listEntitiesAtVersion(@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable EntityType entityType, @ApiParam(value = VERSION_ID_PARAM_DESCRIPTION, required = true) - @PathVariable String versionId, - @ApiParam(value = BRANCH_PARAM_DESCRIPTION, required = true) - @RequestParam String branch) throws Exception { + @PathVariable String versionId) throws Exception { accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - return wrapFuture(versionControlService.listEntitiesAtVersion(getTenantId(), branch, versionId, entityType)); + return wrapFuture(versionControlService.listEntitiesAtVersion(getTenantId(), versionId, entityType)); } @ApiOperation(value = "List all entities at version (listAllEntitiesAtVersion)", notes = "" + @@ -326,13 +324,11 @@ public class EntitiesVersionControlController extends BaseController { "Response type is the same as for listAllEntitiesAtVersion API method. \n" + "Returned entities order will be the same as in the repository." + TENANT_AUTHORITY_PARAGRAPH) - @GetMapping(value = "/entity/{versionId}", params = {"branch"}) + @GetMapping(value = "/entity/{versionId}") public DeferredResult> listAllEntitiesAtVersion(@ApiParam(value = VERSION_ID_PARAM_DESCRIPTION, required = true) - @PathVariable String versionId, - @ApiParam(value = BRANCH_PARAM_DESCRIPTION, required = true) - @RequestParam String branch) throws Exception { + @PathVariable String versionId) throws Exception { accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); - return wrapFuture(versionControlService.listAllEntitiesAtVersion(getTenantId(), branch, versionId)); + return wrapFuture(versionControlService.listAllEntitiesAtVersion(getTenantId(), versionId)); } @ApiOperation(value = "Get entity data info (getEntityDataInfo)", notes = "" + @@ -357,18 +353,16 @@ public class EntitiesVersionControlController extends BaseController { "Returns an object with current entity data and the one at a specific version. " + "Entity data structure is the same as stored in a repository. " + TENANT_AUTHORITY_PARAGRAPH) - @GetMapping(value = "/diff/{entityType}/{internalEntityUuid}", params = {"branch", "versionId"}) + @GetMapping(value = "/diff/{entityType}/{internalEntityUuid}", params = {"versionId"}) public DeferredResult compareEntityDataToVersion(@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @PathVariable EntityType entityType, @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable UUID internalEntityUuid, - @ApiParam(value = BRANCH_PARAM_DESCRIPTION) - @RequestParam String branch, @ApiParam(value = VERSION_ID_PARAM_DESCRIPTION, required = true) @RequestParam String versionId) throws Exception { accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ); EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, internalEntityUuid); - return wrapFuture(versionControlService.compareEntityDataToVersion(getCurrentUser(), branch, entityId, versionId)); + return wrapFuture(versionControlService.compareEntityDataToVersion(getCurrentUser(), entityId, versionId)); } @ApiOperation(value = "Load entities version (loadEntitiesVersion)", notes = "" + diff --git a/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java index 5eaeec0b73..ff89c3dd06 100644 --- a/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.ie.importing.csv.AbstractBulkImportService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportColumnType; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.entitiy.asset.TbAssetService; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java index c04dd5ce15..8b2126905f 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java @@ -50,7 +50,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.ie.importing.csv.AbstractBulkImportService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportColumnType; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.entitiy.device.TbDeviceService; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java index 404e5af5fd..8372e2ba0e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java @@ -29,7 +29,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.ie.importing.csv.AbstractBulkImportService; -import org.thingsboard.server.service.sync.ie.importing.csv.BulkImportColumnType; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.entitiy.edge.TbEdgeService; import org.thingsboard.server.service.security.model.SecurityUser; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java index b92e100401..becf92771f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/AbstractBulkImportService.java @@ -39,6 +39,9 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportColumnType; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.controller.BaseController; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index 88a15f7cdc..00e98cd3a1 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -241,13 +241,13 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } @Override - public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, EntityType entityType) throws Exception { - return gitServiceQueue.listEntitiesAtVersion(tenantId, branch, versionId, entityType); + public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId, EntityType entityType) throws Exception { + return gitServiceQueue.listEntitiesAtVersion(tenantId, versionId, entityType); } @Override - public ListenableFuture> listAllEntitiesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception { - return gitServiceQueue.listEntitiesAtVersion(tenantId, branch, versionId); + public ListenableFuture> listAllEntitiesAtVersion(TenantId tenantId, String versionId) throws Exception { + return gitServiceQueue.listEntitiesAtVersion(tenantId, versionId); } @SuppressWarnings({"UnstableApiUsage", "rawtypes"}) @@ -451,7 +451,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } @Override - public ListenableFuture compareEntityDataToVersion(User user, String branch, EntityId entityId, String versionId) throws Exception { + public ListenableFuture compareEntityDataToVersion(User user, EntityId entityId, String versionId) throws Exception { HasId entity = exportableEntitiesService.findEntityByTenantIdAndId(user.getTenantId(), entityId); if (!(entity instanceof ExportableEntity)) throw new IllegalArgumentException("Unsupported entity type"); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java index 6eaa2df74f..c9b620ad1c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitVersionControlQueueService.java @@ -245,7 +245,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu } @Override - public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, EntityType entityType) { + public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId, EntityType entityType) { return listEntitiesAtVersion(tenantId, ListEntitiesRequestMsg.newBuilder() .setVersionId(versionId) .setEntityType(entityType.name()) @@ -253,7 +253,7 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu } @Override - public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId) { + public ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId) { return listEntitiesAtVersion(tenantId, ListEntitiesRequestMsg.newBuilder() .setVersionId(versionId) .build()); @@ -385,66 +385,71 @@ public class DefaultGitVersionControlQueueService implements GitVersionControlQu if (!StringUtils.isEmpty(vcResponseMsg.getError())) { future.setException(new RuntimeException(vcResponseMsg.getError())); } else { - if (vcResponseMsg.hasGenericResponse()) { - future.set(null); - } else if (vcResponseMsg.hasCommitResponse()) { - var commitResponse = vcResponseMsg.getCommitResponse(); - var commitResult = new VersionCreationResult(); - if (commitResponse.getTs() > 0) { - commitResult.setVersion(new EntityVersion(commitResponse.getTs(), commitResponse.getCommitId(), commitResponse.getName(), commitResponse.getAuthor())); - } - commitResult.setAdded(commitResponse.getAdded()); - commitResult.setRemoved(commitResponse.getRemoved()); - commitResult.setModified(commitResponse.getModified()); - commitResult.setDone(true); - ((CommitGitRequest) request).getFuture().set(commitResult); - } else if (vcResponseMsg.hasListBranchesResponse()) { - var listBranchesResponse = vcResponseMsg.getListBranchesResponse(); - ((ListBranchesGitRequest) request).getFuture().set(listBranchesResponse.getBranchesList().stream().map(this::getBranchInfo).collect(Collectors.toList())); - } else if (vcResponseMsg.hasListEntitiesResponse()) { - var listEntitiesResponse = vcResponseMsg.getListEntitiesResponse(); - ((ListEntitiesGitRequest) request).getFuture().set( - listEntitiesResponse.getEntitiesList().stream().map(this::getVersionedEntityInfo).collect(Collectors.toList())); - } else if (vcResponseMsg.hasListVersionsResponse()) { - var listVersionsResponse = vcResponseMsg.getListVersionsResponse(); - ((ListVersionsGitRequest) request).getFuture().set(toPageData(listVersionsResponse)); - } else if (vcResponseMsg.hasEntityContentResponse()) { - TransportProtos.EntityContentResponseMsg responseMsg = vcResponseMsg.getEntityContentResponse(); - log.trace("[{}] received chunk {} for 'getEntity'", responseMsg.getChunkedMsgId(), responseMsg.getChunkIndex()); - var joined = joinChunks(requestId, responseMsg, 1); - if (joined.isPresent()) { - log.trace("[{}] collected all chunks for 'getEntity'", responseMsg.getChunkedMsgId()); - ((EntityContentGitRequest) request).getFuture().set(joined.get().get(0)); - } else { - completed = false; - } - } else if (vcResponseMsg.hasEntitiesContentResponse()) { - TransportProtos.EntitiesContentResponseMsg responseMsg = vcResponseMsg.getEntitiesContentResponse(); - TransportProtos.EntityContentResponseMsg item = responseMsg.getItem(); - if (responseMsg.getItemsCount() > 0) { - var joined = joinChunks(requestId, item, responseMsg.getItemsCount()); + try { + if (vcResponseMsg.hasGenericResponse()) { + future.set(null); + } else if (vcResponseMsg.hasCommitResponse()) { + var commitResponse = vcResponseMsg.getCommitResponse(); + var commitResult = new VersionCreationResult(); + if (commitResponse.getTs() > 0) { + commitResult.setVersion(new EntityVersion(commitResponse.getTs(), commitResponse.getCommitId(), commitResponse.getName(), commitResponse.getAuthor())); + } + commitResult.setAdded(commitResponse.getAdded()); + commitResult.setRemoved(commitResponse.getRemoved()); + commitResult.setModified(commitResponse.getModified()); + commitResult.setDone(true); + ((CommitGitRequest) request).getFuture().set(commitResult); + } else if (vcResponseMsg.hasListBranchesResponse()) { + var listBranchesResponse = vcResponseMsg.getListBranchesResponse(); + ((ListBranchesGitRequest) request).getFuture().set(listBranchesResponse.getBranchesList().stream().map(this::getBranchInfo).collect(Collectors.toList())); + } else if (vcResponseMsg.hasListEntitiesResponse()) { + var listEntitiesResponse = vcResponseMsg.getListEntitiesResponse(); + ((ListEntitiesGitRequest) request).getFuture().set( + listEntitiesResponse.getEntitiesList().stream().map(this::getVersionedEntityInfo).collect(Collectors.toList())); + } else if (vcResponseMsg.hasListVersionsResponse()) { + var listVersionsResponse = vcResponseMsg.getListVersionsResponse(); + ((ListVersionsGitRequest) request).getFuture().set(toPageData(listVersionsResponse)); + } else if (vcResponseMsg.hasEntityContentResponse()) { + TransportProtos.EntityContentResponseMsg responseMsg = vcResponseMsg.getEntityContentResponse(); + log.trace("[{}] received chunk {} for 'getEntity'", responseMsg.getChunkedMsgId(), responseMsg.getChunkIndex()); + var joined = joinChunks(requestId, responseMsg, 1); if (joined.isPresent()) { - ((EntitiesContentGitRequest) request).getFuture().set(joined.get()); + log.trace("[{}] collected all chunks for 'getEntity'", responseMsg.getChunkedMsgId()); + ((EntityContentGitRequest) request).getFuture().set(joined.get().get(0)); } else { completed = false; } - } else { - ((EntitiesContentGitRequest) request).getFuture().set(Collections.emptyList()); + } else if (vcResponseMsg.hasEntitiesContentResponse()) { + TransportProtos.EntitiesContentResponseMsg responseMsg = vcResponseMsg.getEntitiesContentResponse(); + TransportProtos.EntityContentResponseMsg item = responseMsg.getItem(); + if (responseMsg.getItemsCount() > 0) { + var joined = joinChunks(requestId, item, responseMsg.getItemsCount()); + if (joined.isPresent()) { + ((EntitiesContentGitRequest) request).getFuture().set(joined.get()); + } else { + completed = false; + } + } else { + ((EntitiesContentGitRequest) request).getFuture().set(Collections.emptyList()); + } + } else if (vcResponseMsg.hasVersionsDiffResponse()) { + TransportProtos.VersionsDiffResponseMsg diffResponse = vcResponseMsg.getVersionsDiffResponse(); + List entityVersionsDiffList = diffResponse.getDiffList().stream() + .map(diff -> EntityVersionsDiff.builder() + .externalId(EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(diff.getEntityType()), + new UUID(diff.getEntityIdMSB(), diff.getEntityIdLSB()))) + .entityDataAtVersion1(StringUtils.isNotEmpty(diff.getEntityDataAtVersion1()) ? + toData(diff.getEntityDataAtVersion1()) : null) + .entityDataAtVersion2(StringUtils.isNotEmpty(diff.getEntityDataAtVersion2()) ? + toData(diff.getEntityDataAtVersion2()) : null) + .rawDiff(diff.getRawDiff()) + .build()) + .collect(Collectors.toList()); + ((VersionsDiffGitRequest) request).getFuture().set(entityVersionsDiffList); } - } else if (vcResponseMsg.hasVersionsDiffResponse()) { - TransportProtos.VersionsDiffResponseMsg diffResponse = vcResponseMsg.getVersionsDiffResponse(); - List entityVersionsDiffList = diffResponse.getDiffList().stream() - .map(diff -> EntityVersionsDiff.builder() - .externalId(EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(diff.getEntityType()), - new UUID(diff.getEntityIdMSB(), diff.getEntityIdLSB()))) - .entityDataAtVersion1(StringUtils.isNotEmpty(diff.getEntityDataAtVersion1()) ? - toData(diff.getEntityDataAtVersion1()) : null) - .entityDataAtVersion2(StringUtils.isNotEmpty(diff.getEntityDataAtVersion2()) ? - toData(diff.getEntityDataAtVersion2()) : null) - .rawDiff(diff.getRawDiff()) - .build()) - .collect(Collectors.toList()); - ((VersionsDiffGitRequest) request).getFuture().set(entityVersionsDiffList); + } catch (Exception e) { + future.setException(e); + throw e; } } if (completed) { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java index 8bc2049e33..8a2410e932 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java @@ -49,15 +49,15 @@ public interface EntitiesVersionControlService { ListenableFuture> listVersions(TenantId tenantId, String branch, PageLink pageLink) throws Exception; - ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, EntityType entityType) throws Exception; + ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId, EntityType entityType) throws Exception; - ListenableFuture> listAllEntitiesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception; + ListenableFuture> listAllEntitiesAtVersion(TenantId tenantId, String versionId) throws Exception; UUID loadEntitiesVersion(User user, VersionLoadRequest request) throws Exception; VersionLoadResult getVersionLoadStatus(User user, UUID requestId) throws ThingsboardException; - ListenableFuture compareEntityDataToVersion(User user, String branch, EntityId entityId, String versionId) throws Exception; + ListenableFuture compareEntityDataToVersion(User user, EntityId entityId, String versionId) throws Exception; ListenableFuture> listBranches(TenantId tenantId) throws Exception; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlQueueService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlQueueService.java index cc83479896..feba7db76d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlQueueService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/GitVersionControlQueueService.java @@ -52,9 +52,9 @@ public interface GitVersionControlQueueService { ListenableFuture> listVersions(TenantId tenantId, String branch, EntityId entityId, PageLink pageLink); - ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, EntityType entityType); + ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId, EntityType entityType); - ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String branch, String versionId); + ListenableFuture> listEntitiesAtVersion(TenantId tenantId, String versionId); ListenableFuture> listBranches(TenantId tenantId); diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java index 2c7f9ae51a..617e0637e8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java @@ -20,9 +20,11 @@ import org.mockito.ArgumentMatcher; import org.mockito.Mockito; import org.springframework.boot.test.mock.mockito.SpyBean; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; @@ -30,9 +32,13 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.ToDeviceActorNotificationMsg; import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Objects; @@ -49,13 +55,32 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { @SpyBean protected AuditLogService auditLogService; + @SpyBean + protected GatewayNotificationsService gatewayNotificationsService; + + protected final String msgErrorPermission = "You don't have permission to perform this operation!"; + protected final String msgErrorShouldBeSpecified = "should be specified"; + + protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, Object... additionalInfo) { int cntTime = 1; testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, Object... additionalInfo) { + int cntTime = 1; + testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); + testLogEntityActionEntityEqClass(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -73,7 +98,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { int cntTime = 1; testNotificationMsgToEdgeServiceNever(entityId); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -86,8 +112,49 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, - additionalInfo); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + extractMatcherAdditionalInfo(additionalInfo)); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(HasName entity, HasName originator, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, ActionType actionTypeEdge, + int cntTime, int cntTimeEdge, int cntTimeRuleEngine, Object... additionalInfo) { + EntityId originatorId = createEntityId_NULL_UUID(originator); + testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); + ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); + ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); + testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + extractMatcherAdditionalInfo(additionalInfo)); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeRuleEngine); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(HasName entity, HasName originator, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, ActionType actionTypeEdge, int cntTime, int cntTimeEdge, int cntAdditionalInfo) { + EntityId originatorId = createEntityId_NULL_UUID(originator); + testSendNotificationMsgToEdgeServiceTimeEntityEqAny(tenantId, actionTypeEdge, cntTimeEdge); + ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); + ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); + testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + cntAdditionalInfo); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTimeEdge); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(HasName entity, HasName originator, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, int cntTime, int cntAdditionalInfo) { + EntityId entityId = createEntityId_NULL_UUID(entity); + EntityId originatorId = createEntityId_NULL_UUID(originator); + testNotificationMsgToEdgeServiceNever(entityId); + ArgumentMatcher matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass()); + ArgumentMatcher matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass()); + testLogEntityActionAdditionalInfoAny(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime, + cntAdditionalInfo); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } @@ -97,11 +164,24 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { int cntTime = 1; testNotificationMsgToEdgeServiceNever(entityId); testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); - testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testPushMsgToRuleEngineTime(matcherOriginatorId, tenantId, cntTime); testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime); Mockito.reset(tbClusterService, auditLogService); } + + protected void testNotifyEntityMsgToEdgePushMsgToCoreOneTime(HasName entity, EntityId entityId, EntityId originatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, Object... additionalInfo) { + int cntTime = 1; + testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime); + testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo); + tesPushMsgToCoreTime(cntTime); + Mockito.reset(tbClusterService, auditLogService); + } + + protected void testNotifyEntityEqualsOneTimeError(HasName entity, TenantId tenantId, UserId userId, String userName, ActionType actionType, Exception exp, Object... additionalInfo) { @@ -112,7 +192,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherError = argument -> argument.getMessage().contains(exp.getMessage()) & argument.getClass().equals(exp.getClass()); testLogEntityActionErrorAdditionalInfo(matcherEntityEquals, entity_originator_NULL_UUID, tenantId, customer_NULL_UUID, userId, - userName, actionType, 1, matcherError, additionalInfo); + userName, actionType, 1, matcherError, extractMatcherAdditionalInfo(additionalInfo)); testPushMsgToRuleEngineNever(entity_originator_NULL_UUID); } @@ -126,7 +206,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { ArgumentMatcher matcherError = argument -> argument.getMessage().contains(exp.getMessage()) & argument.getClass().equals(exp.getClass()); testLogEntityActionErrorAdditionalInfo(matcherEntityIsNull, entity_originator_NULL_UUID, tenantId, customer_NULL_UUID, - userId, userName, actionType, 1, matcherError, additionalInfo); + userId, userName, actionType, 1, matcherError, extractMatcherAdditionalInfo(additionalInfo)); testPushMsgToRuleEngineNever(entity_originator_NULL_UUID); } @@ -138,6 +218,26 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.reset(tbClusterService, auditLogService); } + protected void testNotificationUpdateGatewayOneTime(Device device, Device oldDevice) { + Mockito.verify(gatewayNotificationsService, times(1)).onDeviceUpdated(Mockito.eq(device), Mockito.eq(oldDevice)); + } + + protected void testNotificationUpdateGatewayTime(int cntTimes) { + Mockito.verify(gatewayNotificationsService, times(cntTimes)).onDeviceUpdated(Mockito.any(Device.class), Mockito.isNull()); + } + + protected void testNotificationUpdateGatewayNever() { + Mockito.verify(gatewayNotificationsService, never()).onDeviceUpdated(Mockito.any(Device.class), Mockito.any(Device.class)); + } + + protected void testNotificationDeleteGatewayOneTime(Device device) { + Mockito.verify(gatewayNotificationsService, times(1)).onDeviceDeleted(device); + } + + protected void testNotificationDeleteGatewayNever() { + Mockito.verify(gatewayNotificationsService, never()).onDeviceDeleted(Mockito.any(Device.class)); + } + private void testNotificationMsgToEdgeServiceNever(EntityId entityId) { Mockito.verify(tbClusterService, never()).sendNotificationMsgToEdge(Mockito.any(), Mockito.any(), Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any(), Mockito.any()); @@ -154,16 +254,22 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any()); } - private void testPushMsgToRuleEngineTime(EntityId originatorId, TenantId tenantId, int cntTime) { - ArgumentMatcher matcherOriginatorId = cntTime == 1 ? argument -> argument.equals(originatorId) : - argument -> argument.getClass().equals(originatorId.getClass()); + private void testPushMsgToRuleEngineTime(ArgumentMatcher matcherOriginatorId, TenantId tenantId, int cntTime) { Mockito.verify(tbClusterService, times(cntTime)).pushMsgToRuleEngine(Mockito.eq(tenantId), Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull()); } private void testSendNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) { + EdgeEventActionType edgeEventActionType = ActionType.CREDENTIALS_UPDATED.equals(actionType) ? + EdgeEventActionType.CREDENTIALS_UPDATED : edgeTypeByActionType(actionType); Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), Mockito.any(), Mockito.eq(entityId), Mockito.any(), Mockito.isNull(), + Mockito.eq(edgeEventActionType)); + } + + private void testSendNotificationMsgToEdgeServiceTimeEntityEqAny(TenantId tenantId, ActionType actionType, int cntTime) { + Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId), + Mockito.any(), Mockito.any(EntityId.class), Mockito.any(), Mockito.isNull(), Mockito.eq(edgeTypeByActionType(actionType))); } @@ -172,19 +278,88 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class)); } + private void tesPushMsgToCoreTime(int cntTime) { + Mockito.verify(tbClusterService, times(cntTime)).pushMsgToCore(Mockito.any(ToDeviceActorNotificationMsg.class), Mockito.isNull()); + } + private void testLogEntityAction(HasName entity, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, int cntTime, Object... additionalInfo) { ArgumentMatcher matcherEntityEquals = argument -> argument.equals(entity); ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, - actionType, cntTime, additionalInfo); + actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); + } + + private void testLogEntityActionEntityEqClass(HasName entity, EntityId originatorId, TenantId tenantId, + CustomerId customerId, UserId userId, String userName, + ActionType actionType, int cntTime, Object... additionalInfo) { + ArgumentMatcher matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass()); + ArgumentMatcher matcherOriginatorId = argument -> argument.equals(originatorId); + testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName, + actionType, cntTime, extractMatcherAdditionalInfo(additionalInfo)); } private void testLogEntityActionAdditionalInfo(ArgumentMatcher matcherEntity, ArgumentMatcher matcherOriginatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, - ActionType actionType, int cntTime, Object... additionalInfo) { - switch (additionalInfo.length) { + ActionType actionType, int cntTime, List> matcherAdditionalInfos) { + switch (matcherAdditionalInfos.size()) { + case 1: + Mockito.verify(auditLogService, times(cntTime)) + .logEntityAction(Mockito.eq(tenantId), + Mockito.eq(customerId), + Mockito.eq(userId), + Mockito.eq(userName), + Mockito.argThat(matcherOriginatorId), + Mockito.argThat(matcherEntity), + Mockito.eq(actionType), + Mockito.isNull(), + Mockito.argThat(matcherAdditionalInfos.get(0))); + break; + case 2: + Mockito.verify(auditLogService, times(cntTime)) + .logEntityAction(Mockito.eq(tenantId), + Mockito.eq(customerId), + Mockito.eq(userId), + Mockito.eq(userName), + Mockito.argThat(matcherOriginatorId), + Mockito.argThat(matcherEntity), + Mockito.eq(actionType), + Mockito.isNull(), + Mockito.argThat(matcherAdditionalInfos.get(0)), + Mockito.argThat(matcherAdditionalInfos.get(1))); + break; + case 3: + Mockito.verify(auditLogService, times(cntTime)) + .logEntityAction(Mockito.eq(tenantId), + Mockito.eq(customerId), + Mockito.eq(userId), + Mockito.eq(userName), + Mockito.argThat(matcherOriginatorId), + Mockito.argThat(matcherEntity), + Mockito.eq(actionType), + Mockito.isNull(), + Mockito.argThat(matcherAdditionalInfos.get(0)), + Mockito.argThat(matcherAdditionalInfos.get(1)), + Mockito.argThat(matcherAdditionalInfos.get(2))); + break; + default: + Mockito.verify(auditLogService, times(cntTime)) + .logEntityAction(Mockito.eq(tenantId), + Mockito.eq(customerId), + Mockito.eq(userId), + Mockito.eq(userName), + Mockito.argThat(matcherOriginatorId), + Mockito.argThat(matcherEntity), + Mockito.eq(actionType), + Mockito.isNull()); + } + } + + private void testLogEntityActionAdditionalInfoAny(ArgumentMatcher matcherEntity, ArgumentMatcher matcherOriginatorId, + TenantId tenantId, CustomerId customerId, UserId userId, String userName, + ActionType actionType, int cntTime, int cntAdditionalInfo) { + switch (cntAdditionalInfo) { case 1: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), @@ -195,7 +370,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.isNull(), - Mockito.eq(extractParameter(String.class, 0, additionalInfo))); + Mockito.anyString()); break; case 2: Mockito.verify(auditLogService, times(cntTime)) @@ -207,8 +382,8 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.isNull(), - Mockito.eq(extractParameter(String.class, 0, additionalInfo)), - Mockito.eq(extractParameter(String.class, 1, additionalInfo))); + Mockito.anyString(), + Mockito.anyString()); break; case 3: Mockito.verify(auditLogService, times(cntTime)) @@ -220,9 +395,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.isNull(), - Mockito.eq(extractParameter(String.class, 0, additionalInfo)), - Mockito.eq(extractParameter(String.class, 1, additionalInfo)), - Mockito.eq(extractParameter(String.class, 2, additionalInfo))); + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString()); break; default: Mockito.verify(auditLogService, times(cntTime)) @@ -239,8 +414,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { private void testLogEntityActionErrorAdditionalInfo(ArgumentMatcher matcherEntity, EntityId originatorId, TenantId tenantId, CustomerId customerId, UserId userId, String userName, ActionType actionType, - int cntTime, ArgumentMatcher matcherError, Object... additionalInfo) { - switch (additionalInfo.length) { + int cntTime, ArgumentMatcher matcherError, + List> matcherAdditionalInfos) { + switch (matcherAdditionalInfos.size()) { case 1: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), @@ -251,7 +427,7 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.argThat(matcherError), - Mockito.eq(extractParameter(String.class, 0, additionalInfo))); + Mockito.argThat(matcherAdditionalInfos.get(0))); break; case 2: Mockito.verify(auditLogService, times(cntTime)) @@ -263,9 +439,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.argThat(matcherError), - Mockito.eq(extractParameter(String.class, 0, additionalInfo)), - Mockito.eq(extractParameter(String.class, 1, additionalInfo))); - case 3: + Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(0))), + Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(1)))); + case 3: Mockito.verify(auditLogService, times(cntTime)) .logEntityAction(Mockito.eq(tenantId), Mockito.eq(customerId), @@ -275,9 +451,9 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { Mockito.argThat(matcherEntity), Mockito.eq(actionType), Mockito.argThat(matcherError), - Mockito.eq(extractParameter(String.class, 0, additionalInfo)), - Mockito.eq(extractParameter(String.class, 1, additionalInfo)), - Mockito.eq(extractParameter(String.class, 3, additionalInfo))); + Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(0))), + Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(1))), + Mockito.argThat(Mockito.eq(matcherAdditionalInfos.get(2)))); break; default: Mockito.verify(auditLogService, times(cntTime)) @@ -292,10 +468,18 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { } } - private T extractParameter(Class clazz, int index, Object... additionalInfo) { + private List> extractMatcherAdditionalInfo(Object... additionalInfos) { + List> matcherAdditionalInfos = new ArrayList<>(additionalInfos.length); + for (Object additionalInfo : additionalInfos) { + matcherAdditionalInfos.add(argument -> argument.equals(extractParameter(additionalInfo.getClass(), additionalInfo))); + } + return matcherAdditionalInfos; + } + + private T extractParameter(Class clazz, Object additionalInfo) { T result = null; - if (additionalInfo != null && additionalInfo.length > index) { - Object paramObject = additionalInfo[index]; + if (additionalInfo != null) { + Object paramObject = additionalInfo; if (clazz.isInstance(paramObject)) { result = clazz.cast(paramObject); } @@ -308,4 +492,12 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest { .substring(entity.getClass().toString().lastIndexOf(".") + 1).toUpperCase(Locale.ENGLISH)), ModelConstants.NULL_UUID); } + + protected String msgErrorFieldLength(String fieldName){ + return "length of " + fieldName + " must be equal or less than 255"; + } + + protected String msgErrorNoFound(String entityClassName, String assetIdStr){ + return entityClassName + " with id [" + assetIdStr + "] is not found"; + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java index 95c90dea30..14e260ab14 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.page.PageData; import java.util.LinkedList; import java.util.List; +import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @Slf4j @@ -132,7 +133,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm", alarm).andExpect(status().isForbidden()); + doPost("/api/alarm", alarm) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -147,7 +150,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm", alarm).andExpect(status().isForbidden()); + doPost("/api/alarm", alarm) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -187,7 +192,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden()); + doDelete("/api/alarm/" + alarm.getId()) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -201,7 +208,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isForbidden()); + doDelete("/api/alarm/" + alarm.getId()) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -265,7 +274,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden()); + doPost("/api/alarm/" + alarm.getId() + "/clear") + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -279,7 +290,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isForbidden()); + doPost("/api/alarm/" + alarm.getId() + "/clear") + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -293,7 +306,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden()); + doPost("/api/alarm/" + alarm.getId() + "/ack") + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(alarm.getId(), alarm); } @@ -307,7 +322,9 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isForbidden()); + doPost("/api/alarm/" + alarm.getId() + "/ack") + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); } @Test @@ -355,7 +372,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { loginDifferentCustomer(); doGet("/api/alarm/" + EntityType.DEVICE + "/" + customerDevice.getUuidId() + "?page=0&pageSize=" + size) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); } @Test diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java index 0423d08edd..1e8bff0ee5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java @@ -122,9 +122,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - String msgError = "length of name must be equal or less than 255"; - doPost("/api/asset", asset).andExpect(statusReason(containsString(msgError))); - + String msgError = msgErrorFieldLength("name"); + doPost("/api/asset", asset) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -132,8 +133,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { asset.setName("Normal name"); asset.setType(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of type must be equal or less than 255"; - doPost("/api/asset", asset).andExpect(statusReason(containsString(msgError))); + msgError = msgErrorFieldLength("type"); + doPost("/api/asset", asset) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -141,8 +144,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { asset.setType("default"); asset.setLabel(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of label must be equal or less than 255"; - doPost("/api/asset", asset).andExpect(statusReason(containsString(msgError))); + msgError = msgErrorFieldLength("label"); + doPost("/api/asset", asset) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -159,12 +164,15 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doPost("/api/asset", savedAsset, Asset.class, status().isForbidden()); + doPost("/api/asset", savedAsset) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(savedAsset.getId(), savedAsset); - doDelete("/api/asset" + savedAsset.getId().getId().toString()) - .andExpect(status().isNotFound()); + doDelete("/api/asset/" + savedAsset.getId().getId().toString()) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(savedAsset.getId(), savedAsset); @@ -239,8 +247,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedAsset.getId().getId().toString()); - doGet("/api/asset/" + savedAsset.getId().getId().toString()) - .andExpect(status().isNotFound()); + String assetIdStr = savedAsset.getId().getId().toString(); + doGet("/api/asset/" + assetIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Asset", assetIdStr)))); } @Test @@ -264,12 +274,12 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - doDelete("/api/asset/" + savedAsset1.getId().getId().toString()) - .andExpect(status().isBadRequest()); - String msgError = "Can't delete asset that has entity views"; + doDelete("/api/asset/" + savedAsset1.getId().getId().toString()) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); - testNotifyEntityIsNullOneTimeError(savedAsset1, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + testNotifyEntityIsNullOneTimeError(savedAsset1, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, new DataValidationException(msgError), savedAsset1.getId().getId().toString()); savedView.setEntityId(savedAsset2.getId()); @@ -279,8 +289,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { doDelete("/api/asset/" + savedAsset1.getId().getId().toString()) .andExpect(status().isOk()); - doGet("/api/asset/" + savedAsset1.getId().getId().toString()) - .andExpect(status().isNotFound()); + String assetIdStr = savedAsset1.getId().getId().toString(); + doGet("/api/asset/" + assetIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Asset", assetIdStr)))); } @Test @@ -290,7 +302,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - String msgError = "Asset type should be specified"; + String msgError = "Asset type " + msgErrorShouldBeSpecified; doPost("/api/asset", asset) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -306,7 +318,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - String msgError = "Asset name should be specified"; + String msgError = "Asset name " + msgErrorShouldBeSpecified; doPost("/api/asset", asset) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString(msgError))); @@ -362,10 +374,11 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); - String msgError = "Asset name should be specified"; - doPost("/api/customer/" + Uuids.timeBased().toString() + String customerIdStr = Uuids.timeBased().toString(); + doPost("/api/customer/" + customerIdStr + "/asset/" + savedAsset.getId().getId().toString()) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Customer", customerIdStr)))); testNotifyEntityNever(asset.getId(), asset); } @@ -401,9 +414,11 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); + String msgError = "You don't have permission to perform this operation!"; doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/asset/" + savedAsset.getId().getId().toString()) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityNever(savedAsset.getId(), savedAsset); @@ -431,7 +446,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssets.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -480,7 +496,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssetsTitle1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -496,7 +513,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/tenant/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssetsTitle2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -515,7 +533,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/tenant/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -526,7 +545,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/tenant/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } @@ -563,7 +583,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", - new TypeReference>(){}, pageLink, type1); + new TypeReference>() { + }, pageLink, type1); loadedAssetsType1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -579,7 +600,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); do { pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", - new TypeReference>(){}, pageLink, type2); + new TypeReference>() { + }, pageLink, type2); loadedAssetsType2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -598,7 +620,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", - new TypeReference>(){}, pageLink, type1); + new TypeReference>() { + }, pageLink, type1); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -609,7 +632,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", - new TypeReference>(){}, pageLink, type2); + new TypeReference>() { + }, pageLink, type2); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } @@ -636,7 +660,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssets.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -688,7 +713,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssetsTitle1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -704,7 +730,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedAssetsTitle2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -723,7 +750,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -734,7 +762,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } @@ -780,7 +809,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", - new TypeReference>(){}, pageLink, type1); + new TypeReference>() { + }, pageLink, type1); loadedAssetsType1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -796,7 +826,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", - new TypeReference>(){}, pageLink, type2); + new TypeReference>() { + }, pageLink, type2); loadedAssetsType2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -815,7 +846,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", - new TypeReference>(){}, pageLink, type1); + new TypeReference>() { + }, pageLink, type1); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); @@ -826,7 +858,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", - new TypeReference>(){}, pageLink, type2); + new TypeReference>() { + }, pageLink, type2); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } @@ -852,7 +885,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/assets?", - new TypeReference>() {}, new PageLink(100)); + new TypeReference>() { + }, new PageLink(100)); Assert.assertEquals(1, pageData.getData().size()); @@ -867,7 +901,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { ActionType.UNASSIGNED_FROM_EDGE, savedAsset.getId().getId().toString(), savedEdge.getId().getId().toString(), savedEdge.getName()); pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/assets?", - new TypeReference>() {}, new PageLink(100)); + new TypeReference>() { + }, new PageLink(100)); Assert.assertEquals(0, pageData.getData().size()); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java index bbba51bd60..817e8d4ca9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -126,8 +126,10 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest Mockito.reset(tbClusterService, auditLogService); - String msgError = "length of title must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + String msgError = msgErrorFieldLength("title"); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); customer.setTenantId(savedTenant.getId()); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), @@ -136,8 +138,10 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setTitle("Normal title"); customer.setCity(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of city must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + msgError = msgErrorFieldLength("city"); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -145,8 +149,10 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setCity("Normal city"); customer.setCountry(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of country must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + msgError = msgErrorFieldLength("country"); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -154,8 +160,10 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setCountry("Ukraine"); customer.setPhone(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of phone must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + msgError = msgErrorFieldLength("phone"); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -163,8 +171,10 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setPhone("+3892555554512"); customer.setState(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of state must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + msgError = msgErrorFieldLength("state"); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -172,8 +182,10 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest customer.setState("Normal state"); customer.setZip(RandomStringUtils.randomAlphabetic(300)); - msgError = "length of zip or postal code must be equal or less than 255"; - doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError))); + msgError = msgErrorFieldLength("zip or postal code"); + doPost("/api/customer", customer) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); @@ -195,7 +207,8 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest testNotifyEntityNever(savedCustomer.getId(), savedCustomer); doDelete("/api/customer/" + savedCustomer.getId().getId().toString()) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); testNotifyEntityNever(savedCustomer.getId(), savedCustomer); @@ -241,14 +254,16 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest savedCustomer.getId(), savedCustomer.getTenantId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedCustomer.getId().getId().toString()); - doGet("/api/customer/" + savedCustomer.getId().getId().toString()) - .andExpect(status().isNotFound()); + String customerIdStr = savedCustomer.getId().getId().toString(); + doGet("/api/customer/" + customerIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Customer", customerIdStr)))); } @Test public void testSaveCustomerWithEmptyTitle() throws Exception { Customer customer = new Customer(); - String msgError = "Customer title should be specified"; + String msgError = "Customer title " + msgErrorShouldBeSpecified; Mockito.reset(tbClusterService, auditLogService); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java index 170b3ec817..5a9a3d5578 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDashboardControllerTest.java @@ -22,16 +22,19 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DashboardInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; 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.security.Authority; +import org.thingsboard.server.dao.exception.DataValidationException; import java.util.ArrayList; import java.util.Collections; @@ -41,54 +44,66 @@ import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public abstract class BaseDashboardControllerTest extends AbstractControllerTest { - + private IdComparator idComparator = new IdComparator<>(); - + private Tenant savedTenant; private User tenantAdmin; - + @Before public void beforeTest() throws Exception { loginSysAdmin(); - + Tenant tenant = new Tenant(); tenant.setTitle("My tenant"); savedTenant = doPost("/api/tenant", tenant, Tenant.class); Assert.assertNotNull(savedTenant); - + tenantAdmin = new User(); tenantAdmin.setAuthority(Authority.TENANT_ADMIN); tenantAdmin.setTenantId(savedTenant.getId()); tenantAdmin.setEmail("tenant2@thingsboard.org"); tenantAdmin.setFirstName("Joe"); tenantAdmin.setLastName("Downs"); - + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); } - + @After public void afterTest() throws Exception { loginSysAdmin(); - - doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) - .andExpect(status().isOk()); + + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) + .andExpect(status().isOk()); } - + @Test public void testSaveDashboard() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); + + Mockito.reset(tbClusterService, auditLogService); + Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - + + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED); + Assert.assertNotNull(savedDashboard); Assert.assertNotNull(savedDashboard.getId()); Assert.assertTrue(savedDashboard.getCreatedTime() > 0); Assert.assertEquals(savedTenant.getId(), savedDashboard.getTenantId()); Assert.assertEquals(dashboard.getTitle(), savedDashboard.getTitle()); - + savedDashboard.setTitle("My new dashboard"); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/dashboard", savedDashboard, Dashboard.class); - + + testNotifyEntityAllOneTime(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED); + Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); Assert.assertEquals(foundDashboard.getTitle(), savedDashboard.getTitle()); } @@ -97,7 +112,18 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest public void testSaveDashboardInfoWithViolationOfValidation() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/dashboard", dashboard).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); + String msgError = msgErrorFieldLength("title"); + + Mockito.reset(tbClusterService, auditLogService); + + doPost("/api/dashboard", dashboard) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + dashboard.setTenantId(savedTenant.getId()); + testNotifyEntityEqualsOneTimeError(dashboard, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + Mockito.reset(tbClusterService, auditLogService); } @Test @@ -107,10 +133,16 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); loginDifferentTenant(); + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/dashboard", savedDashboard, Dashboard.class, status().isForbidden()); + + testNotifyEntityNever(savedDashboard.getId(), savedDashboard); + deleteDifferentTenant(); } - + @Test public void testFindDashboardById() throws Exception { Dashboard dashboard = new Dashboard(); @@ -120,48 +152,74 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Assert.assertNotNull(foundDashboard); Assert.assertEquals(savedDashboard, foundDashboard); } - + @Test public void testDeleteDashboard() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - - doDelete("/api/dashboard/"+savedDashboard.getId().getId().toString()) - .andExpect(status().isOk()); - doGet("/api/dashboard/"+savedDashboard.getId().getId().toString()) - .andExpect(status().isNotFound()); + Mockito.reset(tbClusterService, auditLogService); + + doDelete("/api/dashboard/" + savedDashboard.getId().getId().toString()).andExpect(status().isOk()); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), + savedDashboard.getTenantId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, + savedDashboard.getId().getId().toString()); + + String dashboardIdStr = savedDashboard.getId().getId().toString(); + doGet("/api/dashboard/" + savedDashboard.getId().getId().toString()) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Dashboard", dashboardIdStr)))); } - + @Test public void testSaveDashboardWithEmptyTitle() throws Exception { Dashboard dashboard = new Dashboard(); + String msgError = "Dashboard title " + msgErrorShouldBeSpecified;; + + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/dashboard", dashboard) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Dashboard title should be specified"))); + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeError(dashboard, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); } - + @Test public void testAssignUnassignDashboardToCustomer() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - + Customer customer = new Customer(); customer.setTitle("My customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); - - Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() + + Mockito.reset(tbClusterService, auditLogService); + + Dashboard assignedDashboard = doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); Assert.assertTrue(assignedDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); + testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(assignedDashboard, assignedDashboard.getId(), assignedDashboard.getId(), + savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_CUSTOMER, + savedDashboard.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); + Dashboard foundDashboard = doGet("/api/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); Assert.assertTrue(foundDashboard.getAssignedCustomers().contains(savedCustomer.toShortCustomerInfo())); - Dashboard unassignedDashboard = - doDelete("/api/customer/"+savedCustomer.getId().getId().toString()+"/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + Mockito.reset(tbClusterService, auditLogService); + + Dashboard unassignedDashboard = + doDelete("/api/customer/" + savedCustomer.getId().getId().toString() + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + + testNotifyEntityAllOneTimeLogEntityActionEntityEqClass(assignedDashboard, assignedDashboard.getId(), assignedDashboard.getId(), + savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, + savedDashboard.getId().getId().toString(), savedCustomer.getId().getId().toString(), savedCustomer.getTitle()); Assert.assertTrue(unassignedDashboard.getAssignedCustomers() == null || unassignedDashboard.getAssignedCustomers().isEmpty()); @@ -169,22 +227,27 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Assert.assertTrue(foundDashboard.getAssignedCustomers() == null || foundDashboard.getAssignedCustomers().isEmpty()); } - + @Test public void testAssignDashboardToNonExistentCustomer() throws Exception { Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - - doPost("/api/customer/" + Uuids.timeBased().toString() + + String customerIdStr = Uuids.timeBased().toString(); + doPost("/api/customer/" + customerIdStr + "/dashboard/" + savedDashboard.getId().getId().toString()) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Customer", customerIdStr)))); + + Mockito.reset(tbClusterService, auditLogService); + testNotifyEntityNever(savedDashboard.getId(), savedDashboard); } - + @Test public void testAssignDashboardToCustomerFromDifferentTenant() throws Exception { loginSysAdmin(); - + Tenant tenant2 = new Tenant(); tenant2.setTitle("Different tenant"); Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class); @@ -196,100 +259,123 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest tenantAdmin2.setEmail("tenant3@thingsboard.org"); tenantAdmin2.setFirstName("Joe"); tenantAdmin2.setLastName("Downs"); - - tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1"); - + + createUserAndLogin(tenantAdmin2, "testPassword1"); + Customer customer = new Customer(); customer.setTitle("Different customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); login(tenantAdmin.getEmail(), "testPassword1"); - + Dashboard dashboard = new Dashboard(); dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); - + doPost("/api/customer/" + savedCustomer.getId().getId().toString() + "/dashboard/" + savedDashboard.getId().getId().toString()) - .andExpect(status().isForbidden()); - + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); + + Mockito.reset(tbClusterService, auditLogService); + testNotifyEntityNever(savedDashboard.getId(), savedDashboard); + + doDelete("/api/tenant/" + savedTenant2.getId().getId().toString()) + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); + testNotifyEntityNever(savedDashboard.getId(), savedDashboard); + loginSysAdmin(); - - doDelete("/api/tenant/"+savedTenant2.getId().getId().toString()) - .andExpect(status().isOk()); + + doDelete("/api/tenant/" + savedTenant2.getId().getId().toString()) + .andExpect(status().isOk()); } @Test public void testFindTenantDashboards() throws Exception { List dashboards = new ArrayList<>(); - for (int i=0;i<173;i++) { + + Mockito.reset(tbClusterService, auditLogService); + + int cntEntity = 173; + for (int i = 0; i < cntEntity; i++) { Dashboard dashboard = new Dashboard(); - dashboard.setTitle("Dashboard"+i); + dashboard.setTitle("Dashboard" + i); dashboards.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class))); } + + testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Dashboard(), new Dashboard(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, cntEntity); + List loadedDashboards = new ArrayList<>(); PageLink pageLink = new PageLink(24); PageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); loadedDashboards.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - + Collections.sort(dashboards, idComparator); Collections.sort(loadedDashboards, idComparator); - + Assert.assertEquals(dashboards, loadedDashboards); } - + @Test public void testFindTenantDashboardsByTitle() throws Exception { String title1 = "Dashboard title 1"; List dashboardsTitle1 = new ArrayList<>(); - for (int i=0;i<134;i++) { + int cntEntity = 134; + for (int i = 0; i < cntEntity; i++) { Dashboard dashboard = new Dashboard(); - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); - String title = title1+suffix; + String suffix = RandomStringUtils.randomAlphanumeric((int) (Math.random() * 15)); + String title = title1 + suffix; title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); dashboard.setTitle(title); dashboardsTitle1.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class))); } String title2 = "Dashboard title 2"; List dashboardsTitle2 = new ArrayList<>(); - for (int i=0;i<112;i++) { + + for (int i = 0; i < 112; i++) { Dashboard dashboard = new Dashboard(); - String suffix = RandomStringUtils.randomAlphanumeric((int)(Math.random()*15)); - String title = title2+suffix; + String suffix = RandomStringUtils.randomAlphanumeric((int) (Math.random() * 15)); + String title = title2 + suffix; title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase(); dashboard.setTitle(title); dashboardsTitle2.add(new DashboardInfo(doPost("/api/dashboard", dashboard, Dashboard.class))); } - + List loadedDashboardsTitle1 = new ArrayList<>(); PageLink pageLink = new PageLink(15, 0, title1); PageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); loadedDashboardsTitle1.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - + Collections.sort(dashboardsTitle1, idComparator); Collections.sort(loadedDashboardsTitle1, idComparator); - + Assert.assertEquals(dashboardsTitle1, loadedDashboardsTitle1); - + List loadedDashboardsTitle2 = new ArrayList<>(); pageLink = new PageLink(4, 0, title2); do { - pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); loadedDashboardsTitle2.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -298,63 +384,79 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest Collections.sort(dashboardsTitle2, idComparator); Collections.sort(loadedDashboardsTitle2, idComparator); - + Assert.assertEquals(dashboardsTitle2, loadedDashboardsTitle2); - + + Mockito.reset(tbClusterService, auditLogService); + for (DashboardInfo dashboard : loadedDashboardsTitle1) { - doDelete("/api/dashboard/"+dashboard.getId().getId().toString()) - .andExpect(status().isOk()); + doDelete("/api/dashboard/" + dashboard.getId().getId().toString()) + .andExpect(status().isOk()); } - + + testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new Dashboard(), new Dashboard(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, cntEntity, 1); + pageLink = new PageLink(4, 0, title1); - pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); - + for (DashboardInfo dashboard : loadedDashboardsTitle2) { - doDelete("/api/dashboard/"+dashboard.getId().getId().toString()) - .andExpect(status().isOk()); + doDelete("/api/dashboard/" + dashboard.getId().getId().toString()) + .andExpect(status().isOk()); } - + pageLink = new PageLink(4, 0, title2); - pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", - new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenant/dashboards?", + new TypeReference>() { + }, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } - + @Test public void testFindCustomerDashboards() throws Exception { Customer customer = new Customer(); customer.setTitle("Test customer"); customer = doPost("/api/customer", customer, Customer.class); CustomerId customerId = customer.getId(); - + + Mockito.reset(tbClusterService, auditLogService); + + int cntEntity = 173; List dashboards = new ArrayList<>(); - for (int i=0;i<173;i++) { + for (int i = 0; i < cntEntity; i++) { Dashboard dashboard = new Dashboard(); - dashboard.setTitle("Dashboard"+i); + dashboard.setTitle("Dashboard" + i); dashboard = doPost("/api/dashboard", dashboard, Dashboard.class); dashboards.add(new DashboardInfo(doPost("/api/customer/" + customerId.getId().toString() - + "/dashboard/" + dashboard.getId().getId().toString(), Dashboard.class))); + + "/dashboard/" + dashboard.getId().getId().toString(), Dashboard.class))); } - + + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Dashboard(), new Dashboard(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity*2); + List loadedDashboards = new ArrayList<>(); PageLink pageLink = new PageLink(21); PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/dashboards?", - new TypeReference>(){}, pageLink); + new TypeReference>() { + }, pageLink); loadedDashboards.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - + Collections.sort(dashboards, idComparator); Collections.sort(loadedDashboards, idComparator); - + Assert.assertEquals(dashboards, loadedDashboards); } @@ -367,11 +469,18 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest dashboard.setTitle("My dashboard"); Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); + Mockito.reset(tbClusterService, auditLogService); + doPost("/api/edge/" + savedEdge.getId().getId().toString() + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); + testNotifyEntityAllOneTime(savedDashboard, savedDashboard.getId(), savedDashboard.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_EDGE, + savedDashboard.getId().getId().toString(), savedEdge.getId().getId().toString(), savedEdge.getName()); + PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/dashboards?", - new TypeReference>() {}, new PageLink(100)); + new TypeReference>() { + }, new PageLink(100)); Assert.assertEquals(1, pageData.getData().size()); @@ -379,7 +488,8 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest + "/dashboard/" + savedDashboard.getId().getId().toString(), Dashboard.class); pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/dashboards?", - new TypeReference>() {}, new PageLink(100)); + new TypeReference>() { + }, new PageLink(100)); Assert.assertEquals(0, pageData.getData().size()); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index d6e30c644c..96f1924d11 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -21,22 +21,24 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; -import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; 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.id.EntityId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -44,6 +46,8 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentialsType; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.dao.model.ModelConstants; import java.util.ArrayList; @@ -55,7 +59,6 @@ import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; -@Slf4j public abstract class BaseDeviceControllerTest extends AbstractControllerTest { static final TypeReference> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() { }; @@ -70,7 +73,6 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @Before public void beforeTest() throws Exception { - log.debug("beforeTest"); executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); loginSysAdmin(); @@ -92,14 +94,12 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @After public void afterTest() throws Exception { - log.debug("afterTest..."); executor.shutdownNow(); loginSysAdmin(); doDelete("/api/tenant/" + savedTenant.getId().getId()) .andExpect(status().isOk()); - log.debug("afterTest done"); } @Test @@ -107,8 +107,17 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Device device = new Device(); device.setName("My device"); device.setType("default"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + Device savedDevice = doPost("/api/device", device, Device.class); + Device oldDevice = new Device(savedDevice); + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED); + testNotificationUpdateGatewayNever(); + Assert.assertNotNull(savedDevice); Assert.assertNotNull(savedDevice.getId()); Assert.assertTrue(savedDevice.getCreatedTime() > 0); @@ -127,9 +136,15 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertNotNull(deviceCredentials.getCredentialsId()); Assert.assertEquals(20, deviceCredentials.getCredentialsId().length()); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + savedDevice.setName("My new device"); doPost("/api/device", savedDevice, Device.class); + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED); + testNotificationUpdateGatewayOneTime(savedDevice, oldDevice); + Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(foundDevice.getName(), savedDevice.getName()); } @@ -139,13 +154,41 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Device device = new Device(); device.setName(RandomStringUtils.randomAlphabetic(300)); device.setType("default"); - doPost("/api/device", device).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); - device.setName("Normal Name"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String msgError = "length of name must be equal or less than 255"; + doPost("/api/device", device) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotificationUpdateGatewayNever(); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + device.setTenantId(savedTenant.getId()); + msgError = "length of type must be equal or less than 255"; device.setType(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/device", device).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); + doPost("/api/device", device) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotificationUpdateGatewayNever(); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + msgError = "length of label must be equal or less than 255"; device.setType("Normal type"); device.setLabel(RandomStringUtils.randomAlphabetic(300)); - doPost("/api/device", device).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); + doPost("/api/device", device) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotificationUpdateGatewayNever(); } @Test @@ -155,7 +198,24 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); loginDifferentTenant(); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/device", savedDevice, Device.class, status().isNotFound()); + + testNotifyEntityNever(savedDevice.getId(), savedDevice); + testNotificationUpdateGatewayNever(); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String savedDeviceIdStr = savedDevice.getId().getId().toString(); + doDelete("/api/device/" + savedDeviceIdStr) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceIdStr)))); + + testNotifyEntityNever(savedDevice.getId(), savedDevice); + testNotificationUpdateGatewayNever(); + deleteDifferentTenant(); } @@ -173,12 +233,24 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @Test public void testFindDeviceTypesByTenantId() throws Exception { List devices = new ArrayList<>(); - for (int i = 0; i < 3; i++) { + + int cntEntity = 3; + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + for (int i = 0; i < cntEntity; i++) { Device device = new Device(); device.setName("My device B" + i); device.setType("typeB"); devices.add(doPost("/api/device", device, Device.class)); } + + testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(), + savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, cntEntity); + testNotificationUpdateGatewayNever(); + for (int i = 0; i < 7; i++) { Device device = new Device(); device.setName("My device C" + i); @@ -192,7 +264,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { devices.add(doPost("/api/device", device, Device.class)); } List deviceTypes = doGetTyped("/api/device/types", - new TypeReference>() { + new TypeReference<>() { }); Assert.assertNotNull(deviceTypes); @@ -211,28 +283,52 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doDelete("/api/device/" + savedDevice.getId().getId()) .andExpect(status().isOk()); - doGet("/api/device/" + savedDevice.getId().getId()) - .andExpect(status().isNotFound()); + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.DELETED, savedDevice.getId().getId().toString()); + testNotificationDeleteGatewayOneTime(savedDevice); + + EntityId savedDeviceId = savedDevice.getId(); + doGet("/api/device/" + savedDeviceId) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", savedDeviceId.getId().toString())))); } @Test public void testSaveDeviceWithEmptyType() throws Exception { Device device = new Device(); device.setName("My device"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + Device savedDevice = doPost("/api/device", device, Device.class); Assert.assertEquals("default", savedDevice.getType()); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(savedDevice, savedDevice.getId(), savedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED); + testNotificationUpdateGatewayNever(); } @Test public void testSaveDeviceWithEmptyName() throws Exception { Device device = new Device(); device.setType("default"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String msgError = "Device name " + msgErrorShouldBeSpecified; doPost("/api/device", device) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Device name should be specified"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityEqualsOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); + testNotificationUpdateGatewayNever(); } @Test @@ -246,17 +342,33 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { customer.setTitle("My customer"); Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId()); + testNotifyEntityAllOneTime(assignedDevice, assignedDevice.getId(), assignedDevice.getId(), savedTenant.getId(), + savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_CUSTOMER, + assignedDevice.getId().getId().toString(), savedCustomer.getId().getId().toString(), + savedCustomer.getTitle()); + testNotificationUpdateGatewayNever(); + Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(savedCustomer.getId(), foundDevice.getCustomerId()); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + Device unassignedDevice = doDelete("/api/customer/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId()); + testNotifyEntityAllOneTime(unassignedDevice, unassignedDevice.getId(), unassignedDevice.getId(), savedTenant.getId(), + savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UNASSIGNED_FROM_CUSTOMER, + unassignedDevice.getId().getId().toString(), savedCustomer.getId().getId().toString(), + savedCustomer.getTitle()); + testNotificationDeleteGatewayNever(); + foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class); Assert.assertEquals(ModelConstants.NULL_UUID, foundDevice.getCustomerId().getId()); } @@ -267,9 +379,17 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setName("My device"); device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); - doPost("/api/customer/" + Uuids.timeBased().toString() - + "/device/" + savedDevice.getId().getId()) - .andExpect(status().isNotFound()); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String customerIdStr = savedDevice.getId().toString(); + doPost("/api/customer/" + customerIdStr + + "/device/" + savedDevice.getId().getId()) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Customer", customerIdStr)))); + + testNotifyEntityNever(savedDevice.getId(), savedDevice); + testNotificationUpdateGatewayNever(); } @Test @@ -288,7 +408,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { tenantAdmin2.setFirstName("Joe"); tenantAdmin2.setLastName("Downs"); - tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1"); + createUserAndLogin(tenantAdmin2, "testPassword1"); Customer customer = new Customer(); customer.setTitle("Different customer"); @@ -301,9 +421,15 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/customer/" + savedCustomer.getId().getId() + "/device/" + savedDevice.getId().getId()) - .andExpect(status().isForbidden()); + .andExpect(status().isForbidden()) + .andExpect(statusReason(containsString(msgErrorPermission))); + + testNotifyEntityNever(savedDevice.getId(), savedDevice); + testNotificationUpdateGatewayNever(); loginSysAdmin(); @@ -333,9 +459,16 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); deviceCredentials.setCredentialsId("access_token"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/device/credentials", deviceCredentials) .andExpect(status().isOk()); + testNotifyEntityMsgToEdgePushMsgToCoreOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), savedTenant.getId(), + tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.CREDENTIALS_UPDATED, deviceCredentials); + testNotificationUpdateGatewayNever(); + DeviceCredentials foundDeviceCredentials = doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); @@ -345,8 +478,15 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @Test public void testSaveDeviceCredentialsWithEmptyDevice() throws Exception { DeviceCredentials deviceCredentials = new DeviceCredentials(); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/device/credentials", deviceCredentials) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Incorrect deviceId null"))); + + testNotifyEntityNever(deviceCredentials.getDeviceId(), new Device()); + testNotificationUpdateGatewayNever(); } @Test @@ -358,9 +498,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); deviceCredentials.setCredentialsType(null); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String msgError = "Device credentials type " + msgErrorShouldBeSpecified; doPost("/api/device/credentials", deviceCredentials) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Device credentials type should be specified"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityIsNullOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.CREDENTIALS_UPDATED, + new DataValidationException(msgError), deviceCredentials); + testNotificationUpdateGatewayNever(); } @Test @@ -372,9 +521,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); deviceCredentials.setCredentialsId(null); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String msgError = "Device credentials id " + msgErrorShouldBeSpecified; doPost("/api/device/credentials", deviceCredentials) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Device credentials id should be specified"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityIsNullOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.CREDENTIALS_UPDATED, + new DeviceCredentialsValidationException(msgError), deviceCredentials); + testNotificationUpdateGatewayNever(); } @Test @@ -390,9 +548,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { newDeviceCredentials.setDeviceId(deviceCredentials.getDeviceId()); newDeviceCredentials.setCredentialsType(deviceCredentials.getCredentialsType()); newDeviceCredentials.setCredentialsId(deviceCredentials.getCredentialsId()); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + String msgError = "Unable to update non-existent device credentials"; doPost("/api/device/credentials", newDeviceCredentials) .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString("Unable to update non-existent device credentials"))); + .andExpect(statusReason(containsString(msgError))); + + testNotifyEntityIsNullOneTimeError(device, savedTenant.getId(), + tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.CREDENTIALS_UPDATED, + new DeviceCredentialsValidationException(msgError), newDeviceCredentials); + testNotificationUpdateGatewayNever(); } @Test @@ -401,29 +568,44 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setName("My device"); device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); + DeviceId deviceTimeBasedId = new DeviceId(Uuids.timeBased()); DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class); - deviceCredentials.setDeviceId(new DeviceId(Uuids.timeBased())); + deviceCredentials.setDeviceId(deviceTimeBasedId); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/device/credentials", deviceCredentials) - .andExpect(status().isNotFound()); + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", deviceTimeBasedId.toString())))); + + testNotifyEntityNever(savedDevice.getId(), savedDevice); + testNotificationUpdateGatewayNever(); } @Test public void testFindTenantDevices() throws Exception { - log.debug("testFindTenantDevices"); - futures = new ArrayList<>(178); - for (int i = 0; i < 178; i++) { + int cntEntity = 178; + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + futures = new ArrayList<>(cntEntity); + for (int i = 0; i < cntEntity; i++) { Device device = new Device(); device.setName("Device" + i); device.setType("default"); futures.add(executor.submit(() -> doPost("/api/device", device, Device.class))); } - log.debug("await create devices"); + List devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); - log.debug("start reading"); - List loadedDevices = new ArrayList<>(178); + testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Device(), new Device(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, cntEntity); + testNotificationUpdateGatewayNever(); + + List loadedDevices = new ArrayList<>(cntEntity); PageLink pageLink = new PageLink(23); do { pageData = doGetTypedWithPageLink("/api/tenant/devices?", @@ -435,11 +617,16 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - log.debug("asserting"); assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); - log.debug("delete devices async"); + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + deleteEntitiesAsync("/api/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS); - log.debug("done"); + + testNotifyManyEntityManyTimeMsgToEdgeServiceNeverAdditionalInfoAny(new Device(), new Device(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.DELETED, cntEntity, 1); + testNotificationUpdateGatewayNever(); } @Test @@ -604,9 +791,12 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { customer.setTitle("Test customer"); customer = doPost("/api/customer", customer, Customer.class); CustomerId customerId = customer.getId(); + int cntEntity = 128; + + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); - futures = new ArrayList<>(128); - for (int i = 0; i < 128; i++) { + futures = new ArrayList<>(cntEntity); + for (int i = 0; i < cntEntity; i++) { Device device = new Device(); device.setName("Device" + i); device.setType("default"); @@ -618,7 +808,12 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { List devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); - List loadedDevices = new ArrayList<>(128); + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new Device(), new Device(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ADDED, ActionType.ASSIGNED_TO_CUSTOMER, cntEntity, cntEntity, cntEntity * 2); + testNotificationUpdateGatewayNever(); + + List loadedDevices = new ArrayList<>(cntEntity); PageLink pageLink = new PageLink(23); do { pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?", @@ -631,9 +826,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); - log.debug("delete devices async"); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS); - log.debug("done"); + + testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAnyAdditionalInfoAny(new Device(), new Device(), + savedTenant.getId(), customerId, tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UNASSIGNED_FROM_CUSTOMER, ActionType.UNASSIGNED_FROM_CUSTOMER, cntEntity, cntEntity, 3); + testNotificationUpdateGatewayNever(); } @Test @@ -843,16 +1043,30 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { createUserAndLogin(user, "testPassword1"); login("tenant2@thingsboard.org", "testPassword1"); - Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class); - doGet("/api/device/" + assignedDevice.getId().getId(), Device.class, status().isNotFound()); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + + Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + + savedDevice.getId().getId(), Device.class); + + doGet("/api/device/" + assignedDevice.getId().getId()) + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", assignedDevice.getId().getId().toString())))); + + testNotifyEntityOneTimeMsgToEdgeServiceNever(assignedDevice, assignedDevice.getId(), assignedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ASSIGNED_TO_TENANT, savedDifferentTenant.getId().getId().toString(), savedDifferentTenant.getTitle()); + testNotificationUpdateGatewayNever(); login("tenant9@thingsboard.org", "testPassword1"); Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId(), Device.class); Assert.assertNotNull(foundDevice1); - doGet("/api/relation?fromId=" + savedDevice.getId().getId() + "&fromType=DEVICE&relationType=Contains&toId=" + savedAnotherDevice.getId().getId() + "&toType=DEVICE", EntityRelation.class, status().isNotFound()); + doGet("/api/relation?fromId=" + savedDevice.getId().getId() + "&fromType=DEVICE&relationType=Contains&toId=" + + savedAnotherDevice.getId().getId() + "&toType=DEVICE") + .andExpect(status().isNotFound()) + .andExpect(statusReason(containsString(msgErrorNoFound("Device", savedAnotherDevice.getId().getId().toString())))); loginSysAdmin(); doDelete("/api/tenant/" + savedDifferentTenant.getId().getId()) @@ -869,17 +1083,32 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doPost("/api/edge/" + savedEdge.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class); + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.ASSIGNED_TO_EDGE, + savedDevice.getId().getId().toString(), savedEdge.getId().getId().toString(), savedEdge.getName()); + testNotificationUpdateGatewayNever(); + pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?", PAGE_DATA_DEVICE_TYPE_REF, new PageLink(100)); Assert.assertEquals(1, pageData.getData().size()); + Mockito.reset(tbClusterService, auditLogService, gatewayNotificationsService); + doDelete("/api/edge/" + savedEdge.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class); + testNotifyEntityAllOneTime(savedDevice, savedDevice.getId(), savedDevice.getId(), + savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), + ActionType.UNASSIGNED_FROM_EDGE, savedDevice.getId().getId().toString(), savedEdge.getId().getId().toString(), savedEdge.getName()); + testNotificationUpdateGatewayNever(); + pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?", PAGE_DATA_DEVICE_TYPE_REF, new PageLink(100)); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java b/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java index b4d65706a2..a6623be8a4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/queue/Queue.java @@ -22,11 +22,17 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.tenant.profile.TenantProfileQueueConfiguration; +import org.thingsboard.server.common.data.validation.Length; +import org.thingsboard.server.common.data.validation.NoXss; @Data public class Queue extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId { private TenantId tenantId; + @NoXss + @Length(fieldName = "name") private String name; + @NoXss + @Length(fieldName = "topic") private String topic; private int pollInterval; private int partitions; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportColumnType.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java similarity index 97% rename from application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportColumnType.java rename to common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java index 24b566e631..a0cd40251f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportColumnType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportColumnType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.ie.importing.csv; +package org.thingsboard.server.common.data.sync.ie.importing.csv; import lombok.Getter; import org.thingsboard.server.common.data.DataConstants; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportRequest.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportRequest.java rename to common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportRequest.java index e8eac6a9ed..8d7b6f9d82 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportRequest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.ie.importing.csv; +package org.thingsboard.server.common.data.sync.ie.importing.csv; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportResult.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportResult.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportResult.java rename to common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportResult.java index 0626c8e690..cced74afaa 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/csv/BulkImportResult.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/ie/importing/csv/BulkImportResult.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.ie.importing.csv; +package org.thingsboard.server.common.data.sync.ie.importing.csv; import lombok.Data; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/BranchInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/BranchInfo.java index fcf1f4470e..c4dc3d8e07 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/BranchInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/BranchInfo.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.sync.vc; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import java.util.Objects; @@ -22,6 +23,8 @@ import java.util.Objects; @Data public class BranchInfo { private final String name; + + @JsonProperty("default") private final boolean isDefault; @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java index d9d1329c8b..cb60d91b24 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/vc/request/load/VersionLoadRequest.java @@ -29,7 +29,6 @@ import static com.fasterxml.jackson.annotation.JsonSubTypes.Type; @Data public abstract class VersionLoadRequest { - private String branch; private String versionId; public abstract VersionLoadRequestType getType(); diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java index 392c686a14..7d1710a87b 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java @@ -354,7 +354,11 @@ public class GitRepository { } private ObjectId resolve(String rev) throws IOException { - return git.getRepository().resolve(rev); + ObjectId result = git.getRepository().resolve(rev); + if (result == null) { + throw new IllegalArgumentException("Failed to parse git revision string: \"" + rev + "\""); + } + return result; } private , T> T execute(C command) throws GitAPIException { diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index 1b27a2751f..81297b3594 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; @@ -36,6 +37,11 @@ public abstract class DataValidator> { private static final Pattern EMAIL_PATTERN = Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE); + private static final Pattern QUEUE_PATTERN = Pattern.compile("^[a-zA-Z0-9_.\\-]+$"); + + private static final String NAME = "name"; + private static final String TOPIC = "topic"; + // Returns old instance of the same object that is fetched during validation. public D validate(D data, Function tenantIdFunction) { try { @@ -134,4 +140,22 @@ public abstract class DataValidator> { } } + protected static void validateQueueName(String name) { + validateQueueNameOrTopic(name, NAME); + } + + protected static void validateQueueTopic(String topic) { + validateQueueNameOrTopic(topic, TOPIC); + } + + private static void validateQueueNameOrTopic(String value, String fieldName) { + if (StringUtils.isEmpty(value)) { + throw new DataValidationException(String.format("Queue %s should be specified!", fieldName)); + } + if (!QUEUE_PATTERN.matcher(value).matches()) { + throw new DataValidationException( + String.format("Queue %s contains a character other than ASCII alphanumerics, '.', '_' and '-'!", fieldName)); + } + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java index c8ce639d40..1eaa64f446 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/QueueValidator.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.dao.service.validator; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.TenantProfile; @@ -73,12 +72,9 @@ public class QueueValidator extends DataValidator { } } - if (StringUtils.isEmpty(queue.getName())) { - throw new DataValidationException("Queue name should be specified!"); - } - if (StringUtils.isBlank(queue.getTopic())) { - throw new DataValidationException("Queue topic should be non empty and without spaces!"); - } + validateQueueName(queue.getName()); + validateQueueTopic(queue.getTopic()); + if (queue.getPollInterval() < 1) { throw new DataValidationException("Queue poll interval should be more then 0!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java index ce15ec9e88..feab5078e6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/TenantProfileDataValidator.java @@ -107,12 +107,9 @@ public class TenantProfileDataValidator extends DataValidator { } private void validateQueueConfiguration(TenantProfileQueueConfiguration queue) { - if (StringUtils.isEmpty(queue.getName())) { - throw new DataValidationException("Queue name should be specified!"); - } - if (StringUtils.isBlank(queue.getTopic())) { - throw new DataValidationException("Queue topic should be non empty and without spaces!"); - } + validateQueueName(queue.getName()); + validateQueueTopic(queue.getTopic()); + if (queue.getPollInterval() < 1) { throw new DataValidationException("Queue poll interval should be more then 0!"); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java index 577193ff94..428a21299d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseQueueServiceTest.java @@ -152,6 +152,20 @@ public abstract class BaseQueueServiceTest extends AbstractServiceTest { queueService.saveQueue(queue); } + @Test(expected = DataValidationException.class) + public void testSaveQueueWithInvalidName() { + Queue queue = new Queue(); + queue.setTenantId(tenantId); + queue.setName("Test 1"); + queue.setTopic("tb_rule_engine.test"); + queue.setPollInterval(25); + queue.setPartitions(1); + queue.setPackProcessingTimeout(2000); + queue.setSubmitStrategy(createTestSubmitStrategy()); + queue.setProcessingStrategy(createTestProcessingStrategy()); + queueService.saveQueue(queue); + } + @Test(expected = DataValidationException.class) public void testSaveQueueWithEmptyTopic() { Queue queue = new Queue(); @@ -165,6 +179,20 @@ public abstract class BaseQueueServiceTest extends AbstractServiceTest { queueService.saveQueue(queue); } + @Test(expected = DataValidationException.class) + public void testSaveQueueWithInvalidTopic() { + Queue queue = new Queue(); + queue.setTenantId(tenantId); + queue.setName("Test"); + queue.setTopic("tb rule engine test"); + queue.setPollInterval(25); + queue.setPartitions(1); + queue.setPackProcessingTimeout(2000); + queue.setSubmitStrategy(createTestSubmitStrategy()); + queue.setProcessingStrategy(createTestProcessingStrategy()); + queueService.saveQueue(queue); + } + @Test(expected = DataValidationException.class) public void testSaveQueueWithEmptyPollInterval() { Queue queue = new Queue(); diff --git a/docker/compose-utils.sh b/docker/compose-utils.sh index 28601afa3c..f45339eb92 100755 --- a/docker/compose-utils.sh +++ b/docker/compose-utils.sh @@ -121,3 +121,77 @@ function additionalStartupServices() { echo $ADDITIONAL_STARTUP_SERVICES } + +function permissionList() { + PERMISSION_LIST=" + 799 799 tb-node/log + 799 799 tb-transports/coap/log + 799 799 tb-transports/lwm2m/log + 799 799 tb-transports/http/log + 799 799 tb-transports/mqtt/log + 799 799 tb-transports/snmp/log + 799 799 tb-transports/coap/log + 799 799 tb-vc-executor/log + 999 999 tb-node/postgres + " + + source .env + + if [ "$DATABASE" = "hybrid" ]; then + PERMISSION_LIST="$PERMISSION_LIST + 999 999 tb-node/cassandra + " + fi + + CACHE="${CACHE:-redis}" + case $CACHE in + redis) + PERMISSION_LIST="$PERMISSION_LIST + 1001 1001 tb-node/redis-data + " + ;; + redis-cluster) + PERMISSION_LIST="$PERMISSION_LIST + 1001 1001 tb-node/redis-cluster-data-0 + 1001 1001 tb-node/redis-cluster-data-1 + 1001 1001 tb-node/redis-cluster-data-2 + 1001 1001 tb-node/redis-cluster-data-3 + 1001 1001 tb-node/redis-cluster-data-4 + 1001 1001 tb-node/redis-cluster-data-5 + " + ;; + *) + echo "Unknown CACHE value specified in the .env file: '${CACHE}'. Should be either 'redis' or 'redis-cluster'." >&2 + exit 1 + esac + + echo "$PERMISSION_LIST" +} + +function checkFolders() { + EXIT_CODE=0 + PERMISSION_LIST=$(permissionList) || exit $? + set -e + while read -r USR GRP DIR + do + if [ -z "$DIR" ]; then # skip empty lines + continue + fi + MESSAGE="Checking user ${USR} group ${GRP} dir ${DIR}" + if [[ -d "$DIR" ]] && + [[ $(ls -ldn "$DIR" | awk '{print $3}') -eq "$USR" ]] && + [[ $(ls -ldn "$DIR" | awk '{print $4}') -eq "$GRP" ]] + then + MESSAGE="$MESSAGE OK" + else + if [ "$1" = "--create" ]; then + echo "Create and chown: user ${USR} group ${GRP} dir ${DIR}" + mkdir -p "$DIR" && sudo chown -R "$USR":"$GRP" "$DIR" + else + echo "$MESSAGE FAILED" + EXIT_CODE=1 + fi + fi + done < <(echo "$PERMISSION_LIST") + return $EXIT_CODE +} diff --git a/docker/docker-check-log-folders.sh b/docker/docker-check-log-folders.sh new file mode 100755 index 0000000000..66e7ea394c --- /dev/null +++ b/docker/docker-check-log-folders.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Copyright © 2016-2022 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. +# + +set -e +source compose-utils.sh +checkFolders || exit $? +echo "OK" diff --git a/docker/docker-create-log-folders.sh b/docker/docker-create-log-folders.sh index 6224b7040b..54a74f4812 100755 --- a/docker/docker-create-log-folders.sh +++ b/docker/docker-create-log-folders.sh @@ -15,39 +15,6 @@ # limitations under the License. # -mkdir -p tb-node/log && sudo chown -R 799:799 tb-node/log - -mkdir -p tb-transports/coap/log && sudo chown -R 799:799 tb-transports/coap/log - -mkdir -p tb-transports/lwm2m/log && sudo chown -R 799:799 tb-transports/lwm2m/log - -mkdir -p tb-transports/http/log && sudo chown -R 799:799 tb-transports/http/log - -mkdir -p tb-transports/mqtt/log && sudo chown -R 799:799 tb-transports/mqtt/log - -mkdir -p tb-transports/snmp/log && sudo chown -R 799:799 tb-transports/snmp/log - -mkdir -p tb-vc-executor/log && sudo chown -R 799:799 tb-vc-executor/log - -mkdir -p tb-node/postgres && sudo chown -R 999:999 tb-node/postgres - -mkdir -p tb-node/cassandra && sudo chown -R 999:999 tb-node/cassandra - -source .env -CACHE="${CACHE:-redis}" -case $CACHE in - redis) - mkdir -p tb-node/redis-data && sudo chown -R 1001:1001 tb-node/redis-data - ;; - redis-cluster) - mkdir -p tb-node/redis-cluster-data-0 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-0 - mkdir -p tb-node/redis-cluster-data-1 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-1 - mkdir -p tb-node/redis-cluster-data-2 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-2 - mkdir -p tb-node/redis-cluster-data-3 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-3 - mkdir -p tb-node/redis-cluster-data-4 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-4 - mkdir -p tb-node/redis-cluster-data-5 && sudo chown -R 1001:1001 tb-node/redis-cluster-data-5 - ;; - *) - echo "Unknown CACHE value specified in the .env file: '${CACHE}'. Should be either 'redis' or 'redis-cluster'." >&2 - exit 1 -esac \ No newline at end of file +set -e +source compose-utils.sh +checkFolders --create diff --git a/docker/docker-install-tb.sh b/docker/docker-install-tb.sh index 9129c72796..628b16e6ee 100755 --- a/docker/docker-install-tb.sh +++ b/docker/docker-install-tb.sh @@ -49,6 +49,8 @@ ADDITIONAL_CACHE_ARGS=$(additionalComposeCacheArgs) || exit $? ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $? +checkFolders --create || exit $? + if [ ! -z "${ADDITIONAL_STARTUP_SERVICES// }" ]; then docker-compose \ -f docker-compose.yml $ADDITIONAL_CACHE_ARGS $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS \ diff --git a/docker/docker-start-services.sh b/docker/docker-start-services.sh index 9e5d08add9..1a06946308 100755 --- a/docker/docker-start-services.sh +++ b/docker/docker-start-services.sh @@ -27,6 +27,8 @@ ADDITIONAL_CACHE_ARGS=$(additionalComposeCacheArgs) || exit $? ADDITIONAL_COMPOSE_MONITORING_ARGS=$(additionalComposeMonitoringArgs) || exit $? +checkFolders --create || exit $? + docker-compose \ -f docker-compose.yml $ADDITIONAL_CACHE_ARGS $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS $ADDITIONAL_COMPOSE_MONITORING_ARGS \ up -d diff --git a/docker/docker-upgrade-tb.sh b/docker/docker-upgrade-tb.sh index 260e14cf8b..87dff9da15 100755 --- a/docker/docker-upgrade-tb.sh +++ b/docker/docker-upgrade-tb.sh @@ -48,6 +48,8 @@ ADDITIONAL_CACHE_ARGS=$(additionalComposeCacheArgs) || exit $? ADDITIONAL_STARTUP_SERVICES=$(additionalStartupServices) || exit $? +checkFolders --create || exit $? + docker-compose \ -f docker-compose.yml $ADDITIONAL_CACHE_ARGS $ADDITIONAL_COMPOSE_ARGS $ADDITIONAL_COMPOSE_QUEUE_ARGS \ pull \ diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index fe78879125..e3c43be6f7 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -51,6 +51,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntityInfo; 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.EntityViewInfo; import org.thingsboard.server.common.data.Event; @@ -77,6 +78,7 @@ import org.thingsboard.server.common.data.audit.AuditLog; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEvent; +import org.thingsboard.server.common.data.edge.EdgeInfo; import org.thingsboard.server.common.data.edge.EdgeSearchQuery; import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; import org.thingsboard.server.common.data.id.AlarmId; @@ -134,6 +136,19 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.security.model.SecuritySettings; import org.thingsboard.server.common.data.security.model.UserPasswordPolicy; import org.thingsboard.server.common.data.sms.config.TestSmsRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest; +import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult; +import org.thingsboard.server.common.data.sync.vc.AutoCommitSettings; +import org.thingsboard.server.common.data.sync.vc.BranchInfo; +import org.thingsboard.server.common.data.sync.vc.EntityDataDiff; +import org.thingsboard.server.common.data.sync.vc.EntityDataInfo; +import org.thingsboard.server.common.data.sync.vc.EntityVersion; +import org.thingsboard.server.common.data.sync.vc.RepositorySettings; +import org.thingsboard.server.common.data.sync.vc.VersionCreationResult; +import org.thingsboard.server.common.data.sync.vc.VersionLoadResult; +import org.thingsboard.server.common.data.sync.vc.VersionedEntityInfo; +import org.thingsboard.server.common.data.sync.vc.request.create.VersionCreateRequest; +import org.thingsboard.server.common.data.sync.vc.request.load.VersionLoadRequest; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetTypeInfo; @@ -147,11 +162,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.stream.Collectors; -import static org.springframework.util.StringUtils.isEmpty; +import static org.thingsboard.server.common.data.StringUtils.isEmpty; /** * @author Andrew Shvayka @@ -267,6 +283,60 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { return restTemplate.postForEntity(baseURL + "/api/admin/securitySettings", securitySettings, SecuritySettings.class).getBody(); } + public Optional getRepositorySettings() { + try { + ResponseEntity repositorySettings = restTemplate.getForEntity(baseURL + "/api/admin/repositorySettings", RepositorySettings.class); + return Optional.ofNullable(repositorySettings.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Boolean repositorySettingsExists() { + return restTemplate.getForEntity(baseURL + "/api/admin/repositorySettings/exists", Boolean.class).getBody(); + } + + public RepositorySettings saveRepositorySettings(RepositorySettings repositorySettings) { + return restTemplate.postForEntity(baseURL + "/api/admin/repositorySettings", repositorySettings, RepositorySettings.class).getBody(); + } + + public void deleteRepositorySettings() { + restTemplate.delete(baseURL + "/api/admin/repositorySettings"); + } + + public void checkRepositoryAccess(RepositorySettings repositorySettings) { + restTemplate.postForLocation(baseURL + "/api/admin/repositorySettings/checkAccess", repositorySettings); + } + + public Optional getAutoCommitSettings() { + try { + ResponseEntity autoCommitSettings = restTemplate.getForEntity(baseURL + "/api/admin/autoCommitSettings", AutoCommitSettings.class); + return Optional.ofNullable(autoCommitSettings.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public Boolean autoCommitSettingsExists() { + return restTemplate.getForEntity(baseURL + "/api/admin/autoCommitSettings/exists", Boolean.class).getBody(); + } + + public AutoCommitSettings saveAutoCommitSettings(AutoCommitSettings autoCommitSettings) { + return restTemplate.postForEntity(baseURL + "/api/admin/autoCommitSettings", autoCommitSettings, AutoCommitSettings.class).getBody(); + } + + public void deleteAutoCommitSettings() { + restTemplate.delete(baseURL + "/api/admin/autoCommitSettings"); + } + public Optional checkUpdates() { try { ResponseEntity updateMsg = restTemplate.getForEntity(baseURL + "/api/admin/updates", UpdateMessage.class); @@ -550,6 +620,15 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { }).getBody(); } + public BulkImportResult processAssetsBulkImport(BulkImportRequest request) { + return restTemplate.exchange( + baseURL + "/api/asset/bulk_import", + HttpMethod.POST, + new HttpEntity<>(request), + new ParameterizedTypeReference>() { + }).getBody(); + } + @Deprecated public Optional findAsset(String name) { Map params = new HashMap(); @@ -1251,7 +1330,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { HttpEntity.EMPTY, Device.class, tenantId, deviceId).getBody(); } - public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(OtaPackageType otaPackageType, DeviceProfileId deviceProfileId) { + public Long countByDeviceProfileAndEmptyOtaPackage(OtaPackageType otaPackageType, DeviceProfileId deviceProfileId) { Map params = new HashMap<>(); params.put("otaPackageType", otaPackageType.name()); params.put("deviceProfileId", deviceProfileId.getId().toString()); @@ -1266,6 +1345,15 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { ).getBody(); } + public BulkImportResult processDevicesBulkImport(BulkImportRequest request) { + return restTemplate.exchange( + baseURL + "/api/device/bulk_import", + HttpMethod.POST, + new HttpEntity<>(request), + new ParameterizedTypeReference>() { + }).getBody(); + } + @Deprecated public Device createDevice(String name, String type) { Device device = new Device(); @@ -2499,6 +2587,10 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { } } + public Boolean isEdgesSupportEnabled() { + return restTemplate.getForEntity(baseURL + "/api/edges/enabled", Boolean.class).getBody(); + } + public Edge saveEdge(Edge edge) { return restTemplate.postForEntity(baseURL + "/api/edge", edge, Edge.class).getBody(); } @@ -2520,6 +2612,19 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { } } + public Optional getEdgeInfoById(EdgeId edgeId) { + try { + ResponseEntity edge = restTemplate.getForEntity(baseURL + "/api/edge/info/{edgeId}", EdgeInfo.class, edgeId.getId()); + return Optional.ofNullable(edge.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + public Optional assignEdgeToCustomer(CustomerId customerId, EdgeId edgeId) { try { ResponseEntity edge = restTemplate.postForEntity(baseURL + "/api/customer/{customerId}/edge/{edgeId}", null, Edge.class, customerId.getId(), edgeId.getId()); @@ -2546,7 +2651,7 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { } } - public Optional setRootRuleChain(EdgeId edgeId, RuleChainId ruleChainId) { + public Optional setEdgeRootRuleChain(EdgeId edgeId, RuleChainId ruleChainId) { try { ResponseEntity ruleChain = restTemplate.postForEntity(baseURL + "/api/edge/{edgeId}/{ruleChainId}/root", null, Edge.class, edgeId.getId(), ruleChainId.getId()); return Optional.ofNullable(ruleChain.getBody()); @@ -2827,6 +2932,17 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { }, params).getBody(); } + public PageData getTenantEdgeInfos(String type, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("type", type); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/tenant/edgeInfos?type={type}&" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + public Optional getTenantEdge(String edgeName) { try { ResponseEntity edge = restTemplate.getForEntity(baseURL + "/api/tenant/edges?edgeName={edgeName}", Edge.class, edgeName); @@ -2852,6 +2968,18 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { }, params).getBody(); } + public PageData getCustomerEdgeInfos(CustomerId customerId, PageLink pageLink, String edgeType) { + Map params = new HashMap<>(); + params.put("customerId", customerId.getId().toString()); + params.put("type", edgeType); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/customer/{customerId}/edgeInfos?type={type}&" + getUrlParams(pageLink), + HttpMethod.GET, HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, params).getBody(); + } + public List getEdgesByIds(List edgeIds) { return restTemplate.exchange(baseURL + "/api/edges?edgeIds={edgeIds}", HttpMethod.GET, @@ -2896,6 +3024,138 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { restTemplate.postForEntity(baseURL + "/api/edge/sync/{edgeId}", null, EdgeId.class, params); } + public String findMissingToRelatedRuleChains(EdgeId edgeId) { + return restTemplate.getForEntity(baseURL + "/api/edge/missingToRelatedRuleChains/{edgeId}", String.class, edgeId.getId()).getBody(); + } + + public BulkImportResult processEdgesBulkImport(BulkImportRequest request) { + return restTemplate.exchange( + baseURL + "/api/edge/bulk_import", + HttpMethod.POST, + new HttpEntity<>(request), + new ParameterizedTypeReference>() { + }).getBody(); + } + + public UUID saveEntitiesVersion(VersionCreateRequest request) { + return restTemplate.postForEntity(baseURL + "/api/entities/vc/version", request, UUID.class).getBody(); + } + + public Optional getVersionCreateRequestStatus(UUID requestId) { + try { + ResponseEntity versionCreateResult = restTemplate.getForEntity(baseURL + "/api/entities/vc/version/{requestId}/status", VersionCreationResult.class, requestId); + return Optional.ofNullable(versionCreateResult.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + public PageData listEntityVersions(EntityId externalEntityId, String branch, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("entityType", externalEntityId.getEntityType().name()); + params.put("externalEntityUuid", externalEntityId.getId().toString()); + params.put("branch", branch); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/entities/vc/version/{entityType}/{externalEntityUuid}?branch={branch}&" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public PageData listEntityTypeVersions(EntityType entityType, String branch, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("entityType", entityType.name()); + params.put("branch", branch); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/entities/vc/version/{entityType}?branch={branch}&" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public PageData listVersions(String branch, PageLink pageLink) { + Map params = new HashMap<>(); + params.put("branch", branch); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/entities/vc/version?branch={branch}&" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public List listEntitiesAtVersion(EntityType entityType, String versionId) { + Map params = new HashMap<>(); + params.put("entityType", entityType.name()); + params.put("versionId", versionId); + return restTemplate.exchange( + baseURL + "/api/entities/vc/entity/{entityType}/{versionId}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public List listAllEntitiesAtVersion(String versionId) { + Map params = new HashMap<>(); + params.put("versionId", versionId); + return restTemplate.exchange( + baseURL + "/api/entities/vc/entity/{versionId}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params).getBody(); + } + + public EntityDataInfo getEntityDataInfo(EntityId externalEntityId, String versionId) { + return restTemplate.getForEntity(baseURL + "/api/entities/vc/info/{versionId}/{entityType}/{externalEntityUuid}", + EntityDataInfo.class, versionId, externalEntityId.getEntityType(), externalEntityId.getId()).getBody(); + } + + public EntityDataDiff compareEntityDataToVersion(EntityId internalEntityId, String versionId) { + return restTemplate.getForEntity(baseURL + "/api/entities/vc/diff/{entityType}/{internalEntityUuid}?versionId={versionId}", + EntityDataDiff.class, internalEntityId.getEntityType(), internalEntityId.getId(), versionId).getBody(); + } + + public UUID loadEntitiesVersion(VersionLoadRequest request) { + return restTemplate.postForEntity(baseURL + "/api/entities/vc/entity", request, UUID.class).getBody(); + } + + public Optional getVersionLoadRequestStatus(UUID requestId) { + try { + ResponseEntity versionLoadResult = restTemplate.getForEntity(baseURL + "/api/entities/vc/entity/{requestId}/status", VersionLoadResult.class, requestId); + return Optional.ofNullable(versionLoadResult.getBody()); + } catch (HttpClientErrorException exception) { + if (exception.getStatusCode() == HttpStatus.NOT_FOUND) { + return Optional.empty(); + } else { + throw exception; + } + } + } + + public List listBranches() { + return restTemplate.exchange( + baseURL + "/api/entities/vc/branches", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }).getBody(); + } + public ResponseEntity downloadResource(TbResourceId resourceId) { Map params = new HashMap<>(); params.put("resourceId", resourceId.getId().toString()); diff --git a/ui-ngx/src/app/core/http/entities-version-control.service.ts b/ui-ngx/src/app/core/http/entities-version-control.service.ts index 447f252fad..b71c95b3ec 100644 --- a/ui-ngx/src/app/core/http/entities-version-control.service.ts +++ b/ui-ngx/src/app/core/http/entities-version-control.service.ts @@ -158,12 +158,10 @@ export class EntitiesVersionControlService { defaultHttpOptionsFromConfig({...config, ...{ignoreLoading: true}})); } - public compareEntityDataToVersion(branch: string, - entityId: EntityId, + public compareEntityDataToVersion(entityId: EntityId, versionId: string, config?: RequestConfig): Observable { - const encodedBranch = encodeURIComponent(branch); - return this.http.get(`/api/entities/vc/diff/${entityId.entityType}/${entityId.id}?branch=${encodedBranch}&versionId=${versionId}`, + return this.http.get(`/api/entities/vc/diff/${entityId.entityType}/${entityId.id}?versionId=${versionId}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html index 6c34e0111f..6e291e9e23 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.html @@ -26,6 +26,9 @@ {{ 'queue.name-unique' | translate }} + + {{ 'queue.name-pattern' | translate }} + diff --git a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts index b84dfc9985..3268c5b2f9 100644 --- a/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts +++ b/ui-ngx/src/app/modules/home/components/queue/queue-form.component.ts @@ -99,7 +99,7 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr ngOnInit() { this.queueFormGroup = this.fb.group( { - name: ['', [Validators.required]], + name: ['', [Validators.required, Validators.pattern(/^[a-zA-Z0-9_.\-]+$/)]], pollInterval: [25, [Validators.min(1), Validators.required]], partitions: [10, [Validators.min(1), Validators.required]], consumerPerPartition: [false, []], @@ -205,10 +205,12 @@ export class QueueFormComponent implements ControlValueAccessor, OnInit, OnDestr if (type === QueueSubmitStrategyTypes.BATCH) { batchSizeField.patchValue(1000, {emitEvent: false}); batchSizeField.setValidators([Validators.min(1), Validators.required]); + batchSizeField.updateValueAndValidity({emitEvent: false}); this.hideBatchSize = true; } else { batchSizeField.patchValue(null, {emitEvent: false}); batchSizeField.clearValidators(); + batchSizeField.updateValueAndValidity({emitEvent: false}); this.hideBatchSize = false; } } diff --git a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts index 718dc5c2e7..dda0f6b39d 100644 --- a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts @@ -41,9 +41,6 @@ import { parseHttpErrorMessage } from '@core/utils'; }) export class ComplexVersionLoadComponent extends PageComponent implements OnInit, OnDestroy { - @Input() - branch: string; - @Input() versionName: string; @@ -117,7 +114,6 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit restore(): void { const request: EntityTypeVersionLoadRequest = { - branch: this.branch, versionId: this.versionId, entityTypes: this.loadVersionFormGroup.get('entityTypes').value, type: VersionLoadRequestType.ENTITY_TYPE diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts index eb9fe8118b..8cc4a3b008 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts @@ -59,9 +59,6 @@ export class EntityVersionDiffComponent extends PageComponent implements OnInit, @ViewChild('diffViewer', {static: true}) diffViewerElmRef: ElementRef; - @Input() - branch: string; - @Input() versionName: string; @@ -108,7 +105,7 @@ export class EntityVersionDiffComponent extends PageComponent implements OnInit, ngOnInit(): void { this.entitiesVersionControlService - .compareEntityDataToVersion(this.branch, this.entityId, this.versionId).subscribe((diffData) => { + .compareEntityDataToVersion(this.entityId, this.versionId).subscribe((diffData) => { const leftContent = entityExportDataToJsonString(diffData.currentVersion); const rightContent = entityExportDataToJsonString(diffData.otherVersion); const leftLines = leftContent.split('\n').length; @@ -310,7 +307,6 @@ export class EntityVersionDiffComponent extends PageComponent implements OnInit, const restoreVersionPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, EntityVersionRestoreComponent, 'leftTop', true, null, { - branch: this.branch, versionName: this.versionName, versionId: this.versionId, externalEntityId: this.externalEntityId, diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts index 43d2452ea3..44f86129d8 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts @@ -41,9 +41,6 @@ import { parseHttpErrorMessage } from '@core/utils'; }) export class EntityVersionRestoreComponent extends PageComponent implements OnInit, OnDestroy { - @Input() - branch: string; - @Input() versionName: string; @@ -108,7 +105,6 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn restore(): void { const request: SingleEntityVersionLoadRequest = { - branch: this.branch, versionId: this.versionId, externalEntityId: this.externalEntityId, config: { diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts index 9612a1c1b3..0c4a7ca477 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts @@ -260,7 +260,6 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni const diffVersionPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, EntityVersionDiffComponent, 'leftTop', true, null, { - branch: this.branch, versionName: entityVersion.name, versionId: entityVersion.id, entityId: this.entityId, @@ -284,7 +283,6 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni const restoreVersionPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, EntityVersionRestoreComponent, 'leftTop', true, null, { - branch: this.branch, versionName: entityVersion.name, versionId: entityVersion.id, externalEntityId: this.externalEntityIdValue, @@ -310,7 +308,6 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni const restoreEntitiesVersionPopover = this.popoverService.displayPopover(trigger, this.renderer, this.viewContainerRef, ComplexVersionLoadComponent, 'leftTop', true, null, { - branch: this.branch, versionName: entityVersion.name, versionId: entityVersion.id, onClose: (result: VersionLoadResult | null) => { diff --git a/ui-ngx/src/app/shared/models/vc.models.ts b/ui-ngx/src/app/shared/models/vc.models.ts index c8184a195a..432332fcee 100644 --- a/ui-ngx/src/app/shared/models/vc.models.ts +++ b/ui-ngx/src/app/shared/models/vc.models.ts @@ -113,7 +113,6 @@ export enum VersionLoadRequestType { } export interface VersionLoadRequest { - branch: string; versionId: string; type: VersionLoadRequestType; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 0286b5f9d2..747a12c336 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2928,6 +2928,7 @@ "name": "Name", "name-required": "Queue name is required!", "name-unique": "Queue name is not unique!", + "name-pattern": "Queue name contains a character other than ASCII alphanumerics, '.', '_' and '-'!", "queue-required": "Queue is required!", "topic-required": "Queue topic is required!", "poll-interval-required": "Poll interval is required!",