32 changed files with 524 additions and 221 deletions
@ -0,0 +1,51 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.sync.ie.exporting.impl; |
|||
|
|||
import org.thingsboard.server.common.data.ExportableEntity; |
|||
import org.thingsboard.server.common.data.HasTenantId; |
|||
import org.thingsboard.server.common.data.cf.CalculatedField; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.sync.ie.CalculatedFieldExportData; |
|||
import org.thingsboard.server.dao.cf.CalculatedFieldService; |
|||
import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; |
|||
|
|||
import java.util.List; |
|||
|
|||
public abstract class BaseCalculatedFieldsExportService<ID extends EntityId, E extends ExportableEntity<ID> & HasTenantId, D extends CalculatedFieldExportData<E>> extends BaseEntityExportService<ID, E, D> { |
|||
|
|||
protected final CalculatedFieldService calculatedFieldService; |
|||
|
|||
protected BaseCalculatedFieldsExportService(CalculatedFieldService calculatedFieldService) { |
|||
this.calculatedFieldService = calculatedFieldService; |
|||
} |
|||
|
|||
protected void setCalculatedFields(EntitiesExportCtx<?> ctx, E entity, D exportData) { |
|||
if (ctx.getSettings().isExportCalculatedFields()) { |
|||
List<CalculatedField> calculatedFields = calculatedFieldService.findCalculatedFieldsByEntityId(ctx.getTenantId(), entity.getId()); |
|||
calculatedFields.forEach(calculatedField -> { |
|||
calculatedField.getConfiguration().getArguments().values().forEach(argument -> { |
|||
if (argument.getRefEntityId() != null) { |
|||
EntityId externalId = getExternalIdOrElseInternal(ctx, argument.getRefEntityId()); |
|||
argument.setRefEntityId(externalId); |
|||
} |
|||
}); |
|||
}); |
|||
exportData.setCalculatedFields(calculatedFields); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -1,49 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.sync.ie.exporting.impl; |
|||
|
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.cf.CalculatedField; |
|||
import org.thingsboard.server.common.data.id.CalculatedFieldId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.sync.ie.EntityExportData; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; |
|||
|
|||
import java.util.Set; |
|||
|
|||
@Service |
|||
@TbCoreComponent |
|||
public class CalculatedFieldExportService extends BaseEntityExportService<CalculatedFieldId, CalculatedField, EntityExportData<CalculatedField>> { |
|||
|
|||
@Override |
|||
protected void setRelatedEntities(EntitiesExportCtx<?> ctx, CalculatedField calculatedField, EntityExportData<CalculatedField> exportData) { |
|||
calculatedField.setEntityId(getExternalIdOrElseInternal(ctx, calculatedField.getEntityId())); |
|||
calculatedField.getConfiguration().getArguments().values().forEach(argument -> { |
|||
if (argument.getRefEntityId() != null) { |
|||
EntityId internalEntityId = getExternalIdOrElseInternal(ctx, argument.getRefEntityId()); |
|||
argument.setRefEntityId(internalEntityId); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
public Set<EntityType> getSupportedEntityTypes() { |
|||
return Set.of(EntityType.CALCULATED_FIELD); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,123 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.sync.ie.importing.impl; |
|||
|
|||
import org.thingsboard.server.common.data.ExportableEntity; |
|||
import org.thingsboard.server.common.data.HasTenantId; |
|||
import org.thingsboard.server.common.data.cf.CalculatedField; |
|||
import org.thingsboard.server.common.data.id.CalculatedFieldId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.sync.ie.CalculatedFieldExportData; |
|||
import org.thingsboard.server.dao.cf.CalculatedFieldService; |
|||
import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; |
|||
|
|||
import java.util.LinkedHashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.function.Function; |
|||
import java.util.stream.Collectors; |
|||
|
|||
public abstract class BaseCalculatedFieldsImportService<ID extends EntityId, E extends ExportableEntity<ID> & HasTenantId, D extends CalculatedFieldExportData<E>> extends BaseEntityImportService<ID, E, D> { |
|||
|
|||
private final CalculatedFieldService calculatedFieldService; |
|||
|
|||
protected BaseCalculatedFieldsImportService(CalculatedFieldService calculatedFieldService) { |
|||
this.calculatedFieldService = calculatedFieldService; |
|||
} |
|||
|
|||
protected E saveOrUpdateEntity(EntitiesImportCtx ctx, E entity, D exportData, IdProvider idProvider, Function<E, E> saveFunction) { |
|||
E savedEntity = saveFunction.apply(entity); |
|||
|
|||
if (ctx.isFinalImportAttempt() || ctx.getCurrentImportResult().isUpdatedAllExternalIds()) { |
|||
saveCalculatedFields(ctx, savedEntity, exportData, idProvider); |
|||
} |
|||
return savedEntity; |
|||
} |
|||
|
|||
protected void saveCalculatedFields(EntitiesImportCtx ctx, E savedEntity, D exportData, IdProvider idProvider) { |
|||
if (exportData.getCalculatedFields() == null || !ctx.isSaveCalculatedFields()) { |
|||
return; |
|||
} |
|||
|
|||
exportData.getCalculatedFields().forEach(calculatedField -> { |
|||
calculatedField.setTenantId(savedEntity.getTenantId()); |
|||
calculatedField.setExternalId(calculatedField.getId()); |
|||
calculatedField.setId(idProvider.getInternalId(calculatedField.getId(), false)); |
|||
calculatedField.setEntityId(savedEntity.getId()); |
|||
|
|||
calculatedField.getConfiguration().getArguments().values().forEach(argument -> { |
|||
if (argument.getRefEntityId() != null) { |
|||
argument.setRefEntityId(idProvider.getInternalId(argument.getRefEntityId(), false)); |
|||
} |
|||
}); |
|||
|
|||
calculatedFieldService.save(calculatedField); |
|||
}); |
|||
} |
|||
|
|||
@Override |
|||
protected boolean updateRelatedEntitiesIfUnmodified(EntitiesImportCtx ctx, E prepared, D exportData, IdProvider idProvider) { |
|||
boolean updated = super.updateRelatedEntitiesIfUnmodified(ctx, prepared, exportData, idProvider); |
|||
updated |= updateCalculatedFields(ctx, prepared, exportData, idProvider); |
|||
return updated; |
|||
} |
|||
|
|||
private boolean updateCalculatedFields(EntitiesImportCtx ctx, E prepared, D exportData, IdProvider idProvider) { |
|||
var calculatedFields = exportData.getCalculatedFields(); |
|||
if (calculatedFields == null || !ctx.isSaveCalculatedFields()) { |
|||
return false; |
|||
} |
|||
Map<CalculatedFieldId, CalculatedField> calculatedFieldMap = calculatedFields.stream() |
|||
.peek(newField -> { |
|||
newField.setTenantId(ctx.getTenantId()); |
|||
newField.setExternalId(newField.getId()); |
|||
newField.setId(idProvider.getInternalId(newField.getId(), false)); |
|||
newField.setEntityId(prepared.getId()); |
|||
newField.getConfiguration().getArguments().values().forEach(argument -> { |
|||
argument.setRefEntityId(idProvider.getInternalId(argument.getRefEntityId(), false)); |
|||
}); |
|||
}) |
|||
.collect(Collectors.toMap(CalculatedField::getId, field -> field)); |
|||
|
|||
List<CalculatedField> existingFields = calculatedFieldService.findCalculatedFieldsByEntityId(ctx.getTenantId(), prepared.getId()); |
|||
boolean updated = false; |
|||
|
|||
Map<CalculatedField, Boolean> result = new LinkedHashMap<>(); |
|||
for (CalculatedField existingField : existingFields) { |
|||
if (calculatedFieldMap.containsKey(existingField.getId())) { |
|||
CalculatedField newField = calculatedFieldMap.get(existingField.getId()); |
|||
if (!newField.equals(existingField)) { |
|||
result.put(newField, false); |
|||
} |
|||
calculatedFieldMap.remove(existingField.getId()); |
|||
} else { |
|||
updated = true; |
|||
calculatedFieldService.deleteCalculatedField(ctx.getTenantId(), existingField.getId()); |
|||
} |
|||
} |
|||
|
|||
for (CalculatedField newField : calculatedFieldMap.values()) { |
|||
result.put(newField, true); |
|||
} |
|||
|
|||
if (!result.isEmpty()) { |
|||
updated = true; |
|||
ctx.addCalculatedFields(result); |
|||
} |
|||
return updated; |
|||
} |
|||
|
|||
} |
|||
@ -1,85 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.sync.ie.importing.impl; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
import org.thingsboard.server.common.data.cf.CalculatedField; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|||
import org.thingsboard.server.common.data.id.CalculatedFieldId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.sync.ie.EntityExportData; |
|||
import org.thingsboard.server.dao.cf.CalculatedFieldService; |
|||
import org.thingsboard.server.dao.service.ConstraintValidator; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; |
|||
|
|||
@Service |
|||
@TbCoreComponent |
|||
@RequiredArgsConstructor |
|||
public class CalculatedFieldImportService extends BaseEntityImportService<CalculatedFieldId, CalculatedField, EntityExportData<CalculatedField>> { |
|||
|
|||
private final CalculatedFieldService calculatedFieldService; |
|||
|
|||
@Override |
|||
protected void setOwner(TenantId tenantId, CalculatedField calculatedField, IdProvider idProvider) { |
|||
calculatedField.setTenantId(tenantId); |
|||
} |
|||
|
|||
@Override |
|||
protected CalculatedField prepare(EntitiesImportCtx ctx, CalculatedField calculatedField, CalculatedField oldEntity, EntityExportData<CalculatedField> exportData, IdProvider idProvider) { |
|||
calculatedField.setEntityId(idProvider.getInternalId(calculatedField.getEntityId())); |
|||
calculatedField.getConfiguration().getArguments().values().forEach(argument -> { |
|||
if (argument.getRefEntityId() != null) { |
|||
EntityId internalEntityId = idProvider.getInternalId(argument.getRefEntityId()); |
|||
argument.setRefEntityId(internalEntityId); |
|||
} |
|||
}); |
|||
return calculatedField; |
|||
} |
|||
|
|||
@Override |
|||
protected CalculatedField saveOrUpdate(EntitiesImportCtx ctx, CalculatedField calculatedField, EntityExportData<CalculatedField> exportData, IdProvider idProvider) { |
|||
ConstraintValidator.validateFields(calculatedField); |
|||
return calculatedFieldService.save(calculatedField); |
|||
} |
|||
|
|||
@Override |
|||
protected CalculatedField deepCopy(CalculatedField calculatedField) { |
|||
return new CalculatedField(calculatedField); |
|||
} |
|||
|
|||
@Override |
|||
protected void onEntitySaved(User user, CalculatedField savedEntity, CalculatedField oldEntity) throws ThingsboardException { |
|||
entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, null, |
|||
oldEntity == null ? ActionType.ADDED : ActionType.UPDATED, null); |
|||
} |
|||
|
|||
@Override |
|||
protected void cleanupForComparison(CalculatedField e) { |
|||
super.cleanupForComparison(e); |
|||
} |
|||
|
|||
@Override |
|||
public EntityType getEntityType() { |
|||
return EntityType.CALCULATED_FIELD; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.sync.ie; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.asset.Asset; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Data |
|||
public class AssetExportData extends CalculatedFieldExportData<Asset> { |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.sync.ie; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.asset.AssetProfile; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Data |
|||
public class AssetProfileExportData extends CalculatedFieldExportData<AssetProfile> { |
|||
|
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.sync.ie; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.ExportableEntity; |
|||
import org.thingsboard.server.common.data.cf.CalculatedField; |
|||
|
|||
import java.util.Comparator; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
public class CalculatedFieldExportData<E extends ExportableEntity<?>> extends EntityExportData<E> { |
|||
|
|||
public static final Comparator<CalculatedField> calculatedFieldsComparator = Comparator.comparing(CalculatedField::getName); |
|||
|
|||
@JsonProperty(index = 102) |
|||
@JsonIgnoreProperties({"entityId", "createdTime", "version"}) |
|||
private List<CalculatedField> calculatedFields; |
|||
|
|||
@JsonIgnore |
|||
@Override |
|||
public boolean hasCalculatedFields() { |
|||
return calculatedFields != null; |
|||
} |
|||
|
|||
@Override |
|||
public CalculatedFieldExportData<E> sort() { |
|||
super.sort(); |
|||
if (calculatedFields != null && !calculatedFields.isEmpty()) { |
|||
calculatedFields.sort(calculatedFieldsComparator); |
|||
} |
|||
return this; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2025 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.sync.ie; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@ToString(callSuper = true) |
|||
@Data |
|||
public class DeviceProfileExportData extends CalculatedFieldExportData<DeviceProfile> { |
|||
|
|||
} |
|||
Loading…
Reference in new issue