diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java index 4ab6dd57b1..728d8212c4 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java @@ -43,7 +43,6 @@ public class EntityExportData> { private E entity; private EntityType entityType; - private List inboundRelations; - private List outboundRelations; + private List relations; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java index 4fe09e9322..672bc50109 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java @@ -33,6 +33,7 @@ import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; +import java.util.ArrayList; import java.util.List; @Service @@ -63,24 +64,25 @@ public class DefaultEntityExportService relations = null; if (exportSettings.isExportInboundRelations()) { List inboundRelations = relationService.findByTo(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); - if (inboundRelations != null) { - for (EntityRelation relation : inboundRelations) { - exportableEntitiesService.checkPermission(user, relation.getFrom(), Operation.READ); - } + for (EntityRelation relation : inboundRelations) { + exportableEntitiesService.checkPermission(user, relation.getFrom(), Operation.READ); } - exportData.setInboundRelations(inboundRelations); + relations = new ArrayList<>(inboundRelations); } if (exportSettings.isExportOutboundRelations()) { List outboundRelations = relationService.findByFrom(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); - if (outboundRelations != null) { - for (EntityRelation relation : outboundRelations) { - exportableEntitiesService.checkPermission(user, relation.getTo(), Operation.READ); - } + for (EntityRelation relation : outboundRelations) { + exportableEntitiesService.checkPermission(user, relation.getTo(), Operation.READ); } - exportData.setOutboundRelations(outboundRelations); + if (relations == null) { + relations = new ArrayList<>(); + } + relations.addAll(outboundRelations); } + exportData.setRelations(relations); } protected D newExportData() { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java index d4b0c46f31..a5de7d8744 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java @@ -26,8 +26,5 @@ import lombok.NoArgsConstructor; @Builder public class EntityImportSettings { private boolean findExistingByName; - - private boolean importInboundRelations; - private boolean importOutboundRelations; - private boolean removeExistingRelations; + private boolean updateRelations; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index 5802e140f3..1edd6a1ec9 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.service.sync.importing.impl; import lombok.RequiredArgsConstructor; -import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.transaction.annotation.Transactional; @@ -40,10 +39,9 @@ import org.thingsboard.server.service.sync.importing.EntityImportService; import org.thingsboard.server.service.sync.importing.data.EntityImportResult; import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; public abstract class BaseEntityImportService, D extends EntityExportData> implements EntityImportService { @@ -102,47 +100,53 @@ public abstract class BaseEntityImportService { - List newRelations = new LinkedList<>(); - - if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { - newRelations.addAll(exportData.getInboundRelations().stream() - .peek(relation -> relation.setTo(savedEntity.getId())) - .collect(Collectors.toList())); - - if (importSettings.isRemoveExistingRelations() && oldEntity != null) { - for (EntityRelation existingRelation : relationService.findByTo(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { - exportableEntitiesService.checkPermission(user, existingRelation.getFrom(), Operation.WRITE); - deleteRelation(user, existingRelation, importResult); - } - } + List relations = exportData.getRelations(); + if (relations == null || !importSettings.isUpdateRelations()) { + return; } - if (importSettings.isImportOutboundRelations() && CollectionUtils.isNotEmpty(exportData.getOutboundRelations())) { - newRelations.addAll(exportData.getOutboundRelations().stream() - .peek(relation -> relation.setFrom(savedEntity.getId())) - .collect(Collectors.toList())); + relations = new ArrayList<>(relations); - if (importSettings.isRemoveExistingRelations() && oldEntity != null) { - for (EntityRelation existingRelation : relationService.findByFrom(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { - exportableEntitiesService.checkPermission(user, existingRelation.getTo(), Operation.WRITE); - deleteRelation(user, existingRelation, importResult); - } - } - } - - for (EntityRelation relation : newRelations) { - HasId otherEntity = null; + for (EntityRelation relation : relations) { if (!relation.getTo().equals(savedEntity.getId())) { - otherEntity = findInternalEntity(user.getTenantId(), relation.getTo()); - relation.setTo(otherEntity.getId()); + HasId to = findInternalEntity(user.getTenantId(), relation.getTo()); + exportableEntitiesService.checkPermission(user, to, to.getId().getEntityType(), Operation.WRITE); + relation.setTo(to.getId()); } - if (!relation.getFrom().equals(savedEntity.getId())) { - otherEntity = findInternalEntity(user.getTenantId(), relation.getFrom()); - relation.setFrom(otherEntity.getId()); - } - if (otherEntity != null) { - exportableEntitiesService.checkPermission(user, otherEntity, otherEntity.getId().getEntityType(), Operation.WRITE); + if (!relation.getFrom().equals(savedEntity.getId())){ + HasId from = findInternalEntity(user.getTenantId(), relation.getFrom()); + exportableEntitiesService.checkPermission(user, from, from.getId().getEntityType(), Operation.WRITE); + relation.setFrom(from.getId()); } + } + if (oldEntity != null) { + List existingRelations = new ArrayList<>(); + existingRelations.addAll(relationService.findByTo(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)); + existingRelations.addAll(relationService.findByFrom(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)); + + for (EntityRelation existingRelation : existingRelations) { + if (!relations.contains(existingRelation)) { + EntityId otherEntity = null; + if (!existingRelation.getTo().equals(savedEntity.getId())) { + otherEntity = existingRelation.getTo(); + } else if (!existingRelation.getFrom().equals(savedEntity.getId())){ + otherEntity = existingRelation.getFrom(); + } + if (otherEntity != null) { + exportableEntitiesService.checkPermission(user, otherEntity, Operation.WRITE); + } + relationService.deleteRelation(user.getTenantId(), existingRelation); + importResult.addSendEventsCallback(() -> { + entityActionService.logEntityAction(user, existingRelation.getFrom(), null, null, + ActionType.RELATION_DELETED, null, existingRelation); + entityActionService.logEntityAction(user, existingRelation.getTo(), null, null, + ActionType.RELATION_DELETED, null, existingRelation); + }); + } + } + } + + for (EntityRelation relation : relations) { relationService.saveRelation(user.getTenantId(), relation); importResult.addSendEventsCallback(() -> { entityActionService.logEntityAction(user, relation.getFrom(), null, null, @@ -154,17 +158,6 @@ public abstract class BaseEntityImportService importResult) { - relationService.deleteRelation(user.getTenantId(), relation); - importResult.addSendEventsCallback(() -> { - entityActionService.logEntityAction(user, relation.getFrom(), null, null, - ActionType.RELATION_DELETED, null, relation); - entityActionService.logEntityAction(user, relation.getTo(), null, null, - ActionType.RELATION_DELETED, null, relation); - }); - } - - protected void onEntitySaved(SecurityUser user, E savedEntity, E oldEntity) throws ThingsboardException { entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : user.getCustomerId(), diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index 8a0c958b2c..8d1857f133 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -311,7 +311,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp Dashboard importedDashboard = (Dashboard) importResults.get(EntityType.DASHBOARD).get(0).getSavedEntity(); Set entityAliasEntitiesIds = Streams.stream(importedDashboard.getConfiguration() - .get("entityAliases").elements().next().get("filter").get("entityList").elements()) + .get("entityAliases").elements().next().get("filter").get("entityList").elements()) .map(JsonNode::asText).collect(Collectors.toSet()); assertThat(entityAliasEntitiesIds).doesNotContain(asset1.getId().toString(), asset2.getId().toString()); assertThat(entityAliasEntitiesIds).contains(importedAsset1.getId().toString(), importedAsset2.getId().toString()); @@ -427,8 +427,8 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp List> exportDataList = exportEntities(exportRequest); EntityExportData deviceExportData = exportDataList.stream().filter(exportData -> exportData.getEntityType() == EntityType.DEVICE).findFirst().orElse(null); - assertThat(deviceExportData.getInboundRelations()).size().isOne(); - assertThat(deviceExportData.getInboundRelations().get(0)).matches(entityRelation -> { + assertThat(deviceExportData.getRelations()).size().isOne(); + assertThat(deviceExportData.getRelations().get(0)).matches(entityRelation -> { return entityRelation.getFrom().equals(asset.getId()) && entityRelation.getTo().equals(device.getId()); }); ((DeviceExportData) deviceExportData).getCredentials().setCredentialsId("ab"); @@ -439,7 +439,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp ImportRequest importRequest = new ImportRequest(); importRequest.setExportDataList(exportDataList); importRequest.setImportSettings(EntityImportSettings.builder() - .importInboundRelations(true) + .updateRelations(true) .build()); Map> importResults = importEntities(importRequest).stream().collect(Collectors.toMap(EntityImportResult::getEntityType, r -> r)); @@ -473,7 +473,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp .build()); List> exportDataList = exportEntities(exportRequest); - assertThat(exportDataList).allMatch(exportData -> exportData.getInboundRelations().size() + exportData.getOutboundRelations().size() == 1); + assertThat(exportDataList).allMatch(exportData -> exportData.getRelations().size() == 1); EntityExportData deviceExportData = exportDataList.stream().filter(exportData -> exportData.getEntityType() == EntityType.DEVICE).findFirst().orElse(null); ((DeviceExportData) deviceExportData).getCredentials().setCredentialsId("ab"); @@ -484,8 +484,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp ImportRequest importRequest = new ImportRequest(); importRequest.setExportDataList(exportDataList); importRequest.setImportSettings(EntityImportSettings.builder() - .importInboundRelations(true) - .importOutboundRelations(true) + .updateRelations(true) .build()); Map> importResults = importEntities(importRequest).stream().collect(Collectors.toMap(EntityImportResult::getEntityType, r -> r)); @@ -515,7 +514,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp .exportOutboundRelations(true) .build()); EntityExportData assetExportData = (EntityExportData) exportEntities(exportRequest).get(0); - assertThat(assetExportData.getOutboundRelations()).size().isOne(); + assertThat(assetExportData.getRelations()).size().isOne(); Device device2 = createDevice(tenantId1, null, null, "Device 2"); EntityRelation relation2 = createRelation(asset.getId(), device2.getId()); @@ -523,13 +522,14 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp ImportRequest importRequest = new ImportRequest(); importRequest.setExportDataList(List.of(assetExportData)); importRequest.setImportSettings(EntityImportSettings.builder() - .importOutboundRelations(true) + .updateRelations(true) .build()); importEntities(importRequest); List relations = relationService.findByFrom(TenantId.SYS_TENANT_ID, asset.getId(), RelationTypeGroup.COMMON); - assertThat(relations).contains(relation1, relation2); + assertThat(relations).contains(relation1); + assertThat(relations).doesNotContain(relation2); } @Test @@ -546,7 +546,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp .exportInboundRelations(true) .build()); EntityExportData deviceExportData = exportEntities(exportRequest).get(0); - assertThat(deviceExportData.getInboundRelations()).size().isOne(); + assertThat(deviceExportData.getRelations()).size().isOne(); Asset asset2 = createAsset(tenantId1, null, "A", "Asset 2"); EntityRelation relation2 = createRelation(asset2.getId(), device.getId()); @@ -554,8 +554,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp ImportRequest importRequest = new ImportRequest(); importRequest.setExportDataList(List.of(deviceExportData)); importRequest.setImportSettings(EntityImportSettings.builder() - .importInboundRelations(true) - .removeExistingRelations(true) + .updateRelations(true) .build()); importEntities(importRequest); @@ -730,7 +729,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp verify(clusterService).sendNotificationMsgToEdgeService(any(), any(), eq(importedDeviceProfile.getId()), any(), any(), eq(EdgeEventActionType.ADDED)); verify(otaPackageStateService).update(eq(importedDeviceProfile), eq(false), eq(false)); - ((DeviceExportData)entitiesExportData.get(EntityType.DEVICE)).getCredentials().setCredentialsId("abc"); + ((DeviceExportData) entitiesExportData.get(EntityType.DEVICE)).getCredentials().setCredentialsId("abc"); Device importedDevice = (Device) importEntity(entitiesExportData.get(EntityType.DEVICE)).getSavedEntity(); verify(entityActionService).logEntityAction(any(), eq(importedDevice.getId()), eq(importedDevice), any(), eq(ActionType.ADDED), isNull());