|
|
|
@ -15,7 +15,10 @@ |
|
|
|
*/ |
|
|
|
package org.thingsboard.server.service.sync.ie.importing.impl; |
|
|
|
|
|
|
|
import com.google.common.util.concurrent.FutureCallback; |
|
|
|
import lombok.RequiredArgsConstructor; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.checkerframework.checker.nullness.qual.Nullable; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.context.annotation.Lazy; |
|
|
|
import org.springframework.transaction.annotation.Transactional; |
|
|
|
@ -29,23 +32,36 @@ import org.thingsboard.server.common.data.id.EntityId; |
|
|
|
import org.thingsboard.server.common.data.id.EntityIdFactory; |
|
|
|
import org.thingsboard.server.common.data.id.HasId; |
|
|
|
import org.thingsboard.server.common.data.id.TenantId; |
|
|
|
import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
|
|
|
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
|
|
|
import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
|
|
|
import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
|
|
|
import org.thingsboard.server.common.data.kv.JsonDataEntry; |
|
|
|
import org.thingsboard.server.common.data.kv.KvEntry; |
|
|
|
import org.thingsboard.server.common.data.kv.LongDataEntry; |
|
|
|
import org.thingsboard.server.common.data.kv.StringDataEntry; |
|
|
|
import org.thingsboard.server.common.data.relation.EntityRelation; |
|
|
|
import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
|
|
|
import org.thingsboard.server.common.data.sync.ie.AttributeExportData; |
|
|
|
import org.thingsboard.server.common.data.sync.ie.EntityExportData; |
|
|
|
import org.thingsboard.server.common.data.sync.ie.EntityImportResult; |
|
|
|
import org.thingsboard.server.common.data.sync.ie.EntityImportSettings; |
|
|
|
import org.thingsboard.server.dao.relation.RelationService; |
|
|
|
import org.thingsboard.server.service.action.EntityActionService; |
|
|
|
import org.thingsboard.server.service.security.model.SecurityUser; |
|
|
|
import org.thingsboard.server.service.security.permission.Operation; |
|
|
|
import org.thingsboard.server.common.data.sync.ie.EntityImportResult; |
|
|
|
import org.thingsboard.server.common.data.sync.ie.EntityImportSettings; |
|
|
|
import org.thingsboard.server.service.sync.ie.exporting.ExportableEntitiesService; |
|
|
|
import org.thingsboard.server.common.data.sync.ie.EntityExportData; |
|
|
|
import org.thingsboard.server.service.sync.ie.importing.EntityImportService; |
|
|
|
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Optional; |
|
|
|
import java.util.UUID; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
@Slf4j |
|
|
|
public abstract class BaseEntityImportService<I extends EntityId, E extends ExportableEntity<I>, D extends EntityExportData<E>> implements EntityImportService<I, E, D> { |
|
|
|
|
|
|
|
@Autowired @Lazy |
|
|
|
@ -53,6 +69,8 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo |
|
|
|
@Autowired |
|
|
|
private RelationService relationService; |
|
|
|
@Autowired |
|
|
|
private TelemetrySubscriptionService tsSubService; |
|
|
|
@Autowired |
|
|
|
protected EntityActionService entityActionService; |
|
|
|
@Autowired |
|
|
|
protected TbClusterService clusterService; |
|
|
|
@ -102,37 +120,41 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo |
|
|
|
onEntitySaved(user, savedEntity, oldEntity); |
|
|
|
}); |
|
|
|
|
|
|
|
importResult.addSaveReferencesCallback(() -> { |
|
|
|
if (!importSettings.isUpdateRelations() || exportData.getRelations() == null) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
List<EntityRelation> relations = new ArrayList<>(exportData.getRelations()); |
|
|
|
if (importSettings.isUpdateRelations() && exportData.getRelations() != null) { |
|
|
|
importRelations(user, exportData.getRelations(), importResult); |
|
|
|
} |
|
|
|
if (importSettings.isSaveAttributes() && exportData.getAttributes() != null) { |
|
|
|
importAttributes(user, exportData.getAttributes(), importResult); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void importRelations(SecurityUser user, List<EntityRelation> relations, EntityImportResult<E> importResult) { |
|
|
|
E entity = importResult.getSavedEntity(); |
|
|
|
importResult.addSaveReferencesCallback(() -> { |
|
|
|
for (EntityRelation relation : relations) { |
|
|
|
if (!relation.getTo().equals(savedEntity.getId())) { |
|
|
|
if (!relation.getTo().equals(entity.getId())) { |
|
|
|
HasId<EntityId> 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())) { |
|
|
|
if (!relation.getFrom().equals(entity.getId())) { |
|
|
|
HasId<EntityId> from = findInternalEntity(user.getTenantId(), relation.getFrom()); |
|
|
|
exportableEntitiesService.checkPermission(user, from, from.getId().getEntityType(), Operation.WRITE); |
|
|
|
relation.setFrom(from.getId()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (oldEntity != null) { |
|
|
|
if (importResult.getOldEntity() != null) { |
|
|
|
List<EntityRelation> existingRelations = new ArrayList<>(); |
|
|
|
existingRelations.addAll(relationService.findByTo(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)); |
|
|
|
existingRelations.addAll(relationService.findByFrom(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)); |
|
|
|
existingRelations.addAll(relationService.findByTo(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON)); |
|
|
|
existingRelations.addAll(relationService.findByFrom(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON)); |
|
|
|
|
|
|
|
for (EntityRelation existingRelation : existingRelations) { |
|
|
|
if (!relations.contains(existingRelation)) { |
|
|
|
EntityId otherEntity = null; |
|
|
|
if (!existingRelation.getTo().equals(savedEntity.getId())) { |
|
|
|
if (!existingRelation.getTo().equals(entity.getId())) { |
|
|
|
otherEntity = existingRelation.getTo(); |
|
|
|
} else if (!existingRelation.getFrom().equals(savedEntity.getId())) { |
|
|
|
} else if (!existingRelation.getFrom().equals(entity.getId())) { |
|
|
|
otherEntity = existingRelation.getFrom(); |
|
|
|
} |
|
|
|
if (otherEntity != null) { |
|
|
|
@ -161,6 +183,44 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private void importAttributes(SecurityUser user, Map<String, List<AttributeExportData>> attributes, EntityImportResult<E> importResult) { |
|
|
|
E entity = importResult.getSavedEntity(); |
|
|
|
importResult.addSaveReferencesCallback(() -> { |
|
|
|
attributes.forEach((scope, attributesExportData) -> { |
|
|
|
List<AttributeKvEntry> attributeKvEntries = attributesExportData.stream() |
|
|
|
.map(attributeExportData -> { |
|
|
|
KvEntry kvEntry; |
|
|
|
String key = attributeExportData.getKey(); |
|
|
|
if (attributeExportData.getStrValue() != null) { |
|
|
|
kvEntry = new StringDataEntry(key, attributeExportData.getStrValue()); |
|
|
|
} else if (attributeExportData.getBooleanValue() != null) { |
|
|
|
kvEntry = new BooleanDataEntry(key, attributeExportData.getBooleanValue()); |
|
|
|
} else if (attributeExportData.getDoubleValue() != null) { |
|
|
|
kvEntry = new DoubleDataEntry(key, attributeExportData.getDoubleValue()); |
|
|
|
} else if (attributeExportData.getLongValue() != null) { |
|
|
|
kvEntry = new LongDataEntry(key, attributeExportData.getLongValue()); |
|
|
|
} else if (attributeExportData.getJsonValue() != null) { |
|
|
|
kvEntry = new JsonDataEntry(key, attributeExportData.getJsonValue()); |
|
|
|
} else { |
|
|
|
throw new IllegalArgumentException("Invalid attribute export data"); |
|
|
|
} |
|
|
|
return new BaseAttributeKvEntry(kvEntry, attributeExportData.getLastUpdateTs()); |
|
|
|
}) |
|
|
|
.collect(Collectors.toList()); |
|
|
|
// fixme: attributes are saved outside the transaction
|
|
|
|
tsSubService.saveAndNotify(user.getTenantId(), entity.getId(), scope, attributeKvEntries, new FutureCallback<Void>() { |
|
|
|
@Override |
|
|
|
public void onSuccess(@Nullable Void unused) {} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onFailure(Throwable thr) { |
|
|
|
log.error("Failed to import attributes for {} {}", entity.getId().getEntityType(), entity.getId(), thr); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
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(), |
|
|
|
@ -168,6 +228,7 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
protected E findExistingEntity(TenantId tenantId, E entity, EntityImportSettings importSettings) { |
|
|
|
return (E) Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndExternalId(tenantId, entity.getId())) |
|
|
|
.or(() -> Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndId(tenantId, entity.getId()))) |
|
|
|
@ -181,6 +242,7 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo |
|
|
|
.orElse(null); |
|
|
|
} |
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
private <ID extends EntityId> HasId<ID> findInternalEntity(TenantId tenantId, ID externalId) { |
|
|
|
return (HasId<ID>) Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndExternalId(tenantId, externalId)) |
|
|
|
.or(() -> Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndId(tenantId, externalId))) |
|
|
|
@ -216,7 +278,8 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo |
|
|
|
EntityId internalId = null; |
|
|
|
try { |
|
|
|
internalId = getInternalId(externalId); |
|
|
|
} catch (Exception ignored) {} |
|
|
|
} catch (Exception ignored) { |
|
|
|
} |
|
|
|
|
|
|
|
if (internalId != null) { |
|
|
|
return Optional.of(internalId); |
|
|
|
|