Browse Source

added support of housekeeper deletion

pull/12009/head
IrynaMatveieva 2 years ago
parent
commit
ac20551bf4
  1. 13
      application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java
  2. 43
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/CalculatedFieldsDeletionTaskProcessor.java
  3. 31
      application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java
  4. 2
      application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java
  5. 21
      application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java
  6. 1
      application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java
  7. 5
      common/dao-api/src/main/java/org/thingsboard/server/dao/calculated_field/CalculatedFieldService.java
  8. 4
      common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java
  9. 3
      common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java
  10. 4
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java
  11. 4
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  12. 31
      dao/src/main/java/org/thingsboard/server/dao/calculated_field/BaseCalculatedFieldService.java
  13. 9
      dao/src/main/java/org/thingsboard/server/dao/calculated_field/CalculatedFieldDao.java
  14. 4
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  15. 4
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  16. 5
      dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java
  17. 1
      dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java
  18. 6
      dao/src/main/java/org/thingsboard/server/dao/sql/calculated_field/CalculatedFieldRepository.java
  19. 16
      dao/src/main/java/org/thingsboard/server/dao/sql/calculated_field/JpaCalculatedFieldDao.java
  20. 21
      dao/src/test/java/org/thingsboard/server/dao/service/AssetProfileServiceTest.java
  21. 77
      dao/src/test/java/org/thingsboard/server/dao/service/AssetServiceTest.java
  22. 5
      dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java
  23. 25
      dao/src/test/java/org/thingsboard/server/dao/service/DeviceProfileServiceTest.java
  24. 20
      dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java
  25. 2
      dao/src/test/java/org/thingsboard/server/dao/service/validator/CalculatedFieldLinkDataValidatorTest.java

13
application/src/main/java/org/thingsboard/server/controller/CalculatedFieldController.java

@ -30,11 +30,11 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.calculated_field.CalculatedField;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.calculated_field.CalculatedFieldService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK;
@ -63,7 +63,7 @@ public class CalculatedFieldController extends BaseController {
public CalculatedField saveCalculatedField(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the calculated field.")
@RequestBody CalculatedField calculatedField) throws Exception {
calculatedField.setTenantId(getTenantId());
checkEntity(calculatedField.getId(), calculatedField, Resource.CALCULATED_FIELD);
checkEntityId(calculatedField.getEntityId(), Operation.WRITE_CALCULATED_FIELD);
return calculatedFieldService.save(calculatedField);
}
@ -76,7 +76,10 @@ public class CalculatedFieldController extends BaseController {
public CalculatedField getCalculatedFieldById(@Parameter @PathVariable(CALCULATED_FIELD_ID) String strCalculatedFieldId) throws ThingsboardException {
checkParameter(CALCULATED_FIELD_ID, strCalculatedFieldId);
CalculatedFieldId calculatedFieldId = new CalculatedFieldId(toUUID(strCalculatedFieldId));
return checkCalculatedFieldId(calculatedFieldId, Operation.READ);
CalculatedField calculatedField = calculatedFieldService.findById(getTenantId(), calculatedFieldId);
checkNotNull(calculatedField);
checkEntityId(calculatedField.getEntityId(), Operation.READ_CALCULATED_FIELD);
return calculatedField;
}
@ -88,7 +91,9 @@ public class CalculatedFieldController extends BaseController {
public void deleteCalculatedField(@PathVariable(CALCULATED_FIELD_ID) String strCalculatedField) throws Exception {
checkParameter(CALCULATED_FIELD_ID, strCalculatedField);
CalculatedFieldId calculatedFieldId = new CalculatedFieldId(toUUID(strCalculatedField));
checkCalculatedFieldId(calculatedFieldId, Operation.DELETE);
TenantId tenantId = getTenantId();
CalculatedField calculatedField = calculatedFieldService.findById(tenantId, calculatedFieldId);
checkEntityId(calculatedField.getEntityId(), Operation.WRITE_CALCULATED_FIELD);
calculatedFieldService.deleteCalculatedField(getTenantId(), calculatedFieldId);
}

43
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/CalculatedFieldsDeletionTaskProcessor.java

@ -0,0 +1,43 @@
/**
* Copyright © 2016-2024 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.housekeeper.processor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
import org.thingsboard.server.dao.calculated_field.CalculatedFieldService;
@Component
@RequiredArgsConstructor
@Slf4j
public class CalculatedFieldsDeletionTaskProcessor extends HousekeeperTaskProcessor<HousekeeperTask> {
private final CalculatedFieldService calculatedFieldService;
@Override
public void process(HousekeeperTask task) throws Exception {
int deletedCount = calculatedFieldService.deleteAllCalculatedFieldsByEntityId(task.getTenantId(), task.getEntityId());
log.debug("[{}][{}][{}] Deleted {} calculated fields", task.getTenantId(), task.getEntityId().getEntityType(), task.getEntityId(), deletedCount);
}
@Override
public HousekeeperTaskType getTaskType() {
return HousekeeperTaskType.DELETE_CALCULATED_FIELDS;
}
}

31
application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java

@ -34,8 +34,8 @@ public class CustomerUserPermissions extends AbstractPermissions {
public CustomerUserPermissions() {
super();
put(Resource.ALARM, customerAlarmPermissionChecker);
put(Resource.ASSET, customerEntityPermissionChecker);
put(Resource.DEVICE, customerEntityPermissionChecker);
put(Resource.ASSET, customerEntityWithCalculatedFieldPermissionChecker);
put(Resource.DEVICE, customerEntityWithCalculatedFieldPermissionChecker);
put(Resource.CUSTOMER, customerPermissionChecker);
put(Resource.DASHBOARD, customerDashboardPermissionChecker);
put(Resource.ENTITY_VIEW, customerEntityPermissionChecker);
@ -85,6 +85,29 @@ public class CustomerUserPermissions extends AbstractPermissions {
}
};
private static final PermissionChecker customerEntityWithCalculatedFieldPermissionChecker =
new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_CREDENTIALS,
Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES,
Operation.WRITE, Operation.WRITE_ATTRIBUTES, Operation.WRITE_TELEMETRY, Operation.READ_CALCULATED_FIELD,
Operation.WRITE_CALCULATED_FIELD) {
@Override
@SuppressWarnings("unchecked")
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!super.hasPermission(user, operation, entityId, entity)) {
return false;
}
if (!user.getTenantId().equals(entity.getTenantId())) {
return false;
}
if (!(entity instanceof HasCustomerId)) {
return false;
}
return operation.equals(Operation.CLAIM_DEVICES) || user.getCustomerId().equals(((HasCustomerId) entity).getCustomerId());
}
};
private static final PermissionChecker customerPermissionChecker =
new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) {
@ -188,7 +211,8 @@ public class CustomerUserPermissions extends AbstractPermissions {
}
};
private static final PermissionChecker profilePermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ) {
private static final PermissionChecker profilePermissionChecker = new PermissionChecker.GenericPermissionChecker(
Operation.READ, Operation.READ_CALCULATED_FIELD, Operation.WRITE_CALCULATED_FIELD) {
@Override
@SuppressWarnings("unchecked")
@ -202,4 +226,5 @@ public class CustomerUserPermissions extends AbstractPermissions {
return user.getTenantId().equals(entity.getTenantId());
}
};
}

2
application/src/main/java/org/thingsboard/server/service/security/permission/Operation.java

@ -19,6 +19,6 @@ public enum Operation {
ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL,
READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES,
ASSIGN_TO_TENANT
ASSIGN_TO_TENANT, READ_CALCULATED_FIELD, WRITE_CALCULATED_FIELD
}

21
application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java

@ -23,15 +23,15 @@ import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.service.security.model.SecurityUser;
@Component(value="tenantAdminPermissions")
@Component(value = "tenantAdminPermissions")
public class TenantAdminPermissions extends AbstractPermissions {
public TenantAdminPermissions() {
super();
put(Resource.ADMIN_SETTINGS, PermissionChecker.allowAllPermissionChecker);
put(Resource.ALARM, tenantEntityPermissionChecker);
put(Resource.ASSET, tenantEntityPermissionChecker);
put(Resource.DEVICE, tenantEntityPermissionChecker);
put(Resource.ASSET, tenantEntityWithCalculatedFieldPermissionChecker);
put(Resource.DEVICE, tenantEntityWithCalculatedFieldPermissionChecker);
put(Resource.CUSTOMER, tenantEntityPermissionChecker);
put(Resource.DASHBOARD, tenantEntityPermissionChecker);
put(Resource.ENTITY_VIEW, tenantEntityPermissionChecker);
@ -40,8 +40,8 @@ public class TenantAdminPermissions extends AbstractPermissions {
put(Resource.USER, userPermissionChecker);
put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker);
put(Resource.WIDGET_TYPE, widgetsPermissionChecker);
put(Resource.DEVICE_PROFILE, tenantEntityPermissionChecker);
put(Resource.ASSET_PROFILE, tenantEntityPermissionChecker);
put(Resource.DEVICE_PROFILE, tenantEntityWithCalculatedFieldPermissionChecker);
put(Resource.ASSET_PROFILE, tenantEntityWithCalculatedFieldPermissionChecker);
put(Resource.API_USAGE_STATE, tenantEntityPermissionChecker);
put(Resource.TB_RESOURCE, tbResourcePermissionChecker);
put(Resource.OTA_PACKAGE, tenantEntityPermissionChecker);
@ -51,14 +51,23 @@ public class TenantAdminPermissions extends AbstractPermissions {
put(Resource.VERSION_CONTROL, PermissionChecker.allowAllPermissionChecker);
put(Resource.NOTIFICATION, tenantEntityPermissionChecker);
put(Resource.MOBILE_APP_SETTINGS, new PermissionChecker.GenericPermissionChecker(Operation.READ));
put(Resource.CALCULATED_FIELD, tenantEntityPermissionChecker);
}
public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!user.getTenantId().equals(entity.getTenantId())) {
return false;
}
return !Operation.READ_CALCULATED_FIELD.equals(operation) && !Operation.WRITE_CALCULATED_FIELD.equals(operation);
}
};
public static final PermissionChecker tenantEntityWithCalculatedFieldPermissionChecker = new PermissionChecker() {
@Override
public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) {
if (!user.getTenantId().equals(entity.getTenantId())) {
return false;
}

1
application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java

@ -115,7 +115,6 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest {
doDelete("/api/calculatedField/" + savedCalculatedField.getId().getId().toString())
.andExpect(status().isOk());
doGet("/api/calculatedField/" + savedCalculatedField.getId().getId()).andExpect(status().isNotFound());
}
private CalculatedField getCalculatedField(DeviceId deviceId) {

5
common/dao-api/src/main/java/org/thingsboard/server/dao/calculated_field/CalculatedFieldService.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.calculated_field;
import org.thingsboard.server.common.data.calculated_field.CalculatedField;
import org.thingsboard.server.common.data.calculated_field.CalculatedFieldLink;
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.dao.entity.EntityDaoService;
@ -29,6 +30,10 @@ public interface CalculatedFieldService extends EntityDaoService {
void deleteCalculatedField(TenantId tenantId, CalculatedFieldId calculatedFieldId);
int deleteAllCalculatedFieldsByEntityId(TenantId tenantId, EntityId entityId);
CalculatedFieldLink saveCalculatedFieldLink(TenantId tenantId, CalculatedFieldLink calculatedFieldLink);
boolean existsByEntityId(TenantId tenantId, EntityId entityId);
}

4
common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java

@ -81,6 +81,10 @@ public class HousekeeperTask implements Serializable {
return new TenantEntitiesDeletionHousekeeperTask(tenantId, entityType);
}
public static HousekeeperTask deleteCalculatedFields(TenantId tenantId, EntityId entityId) {
return new HousekeeperTask(tenantId, entityId, HousekeeperTaskType.DELETE_CALCULATED_FIELDS);
}
@JsonIgnore
public String getDescription() {
return taskType.getDescription() + " for " + entityId.getEntityType().getNormalName().toLowerCase() + " " + entityId.getId();

3
common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java

@ -30,7 +30,8 @@ public enum HousekeeperTaskType {
DELETE_ALARMS("alarms deletion"),
UNASSIGN_ALARMS("alarms unassigning"),
DELETE_TENANT_ENTITIES("tenant entities deletion"),
DELETE_ENTITIES("entities deletion");
DELETE_ENTITIES("entities deletion"),
DELETE_CALCULATED_FIELDS("calculated fields deletion");
private final String description;

4
dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java

@ -192,8 +192,8 @@ public class AssetProfileServiceImpl extends CachedVersionedEntityService<AssetP
if (assetProfile == null) {
return;
}
if (!force && assetProfile.isDefault()) {
throw new DataValidationException("Deletion of Default Asset Profile is prohibited!");
if (!force && (assetProfile.isDefault() || calculatedFieldService.existsByEntityId(tenantId, id))) {
throw new DataValidationException("Deletion of Asset Profile is prohibited!");
}
removeAssetProfile(tenantId, assetProfile);
}

4
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -222,8 +222,8 @@ public class BaseAssetService extends AbstractCachedEntityService<AssetCacheKey,
@Override
@Transactional
public void deleteEntity(TenantId tenantId, EntityId id, boolean force) {
if (!force && entityViewService.existsByTenantIdAndEntityId(tenantId, id)) {
throw new DataValidationException("Can't delete asset that has entity views!");
if (!force && (entityViewService.existsByTenantIdAndEntityId(tenantId, id) || calculatedFieldService.existsByEntityId(tenantId, id))) {
throw new DataValidationException("Can't delete asset that has entity views or calculated fields!");
}
Asset asset = assetDao.findById(tenantId, id.getId());

31
dao/src/main/java/org/thingsboard/server/dao/calculated_field/BaseCalculatedFieldService.java

@ -22,15 +22,20 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.calculated_field.CalculatedField;
import org.thingsboard.server.common.data.calculated_field.CalculatedFieldLink;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.asset.AssetProfileService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.service.DataValidator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@ -49,6 +54,8 @@ public class BaseCalculatedFieldService implements CalculatedFieldService {
private final CalculatedFieldLinkDao calculatedFieldLinkDao;
private final DeviceService deviceService;
private final AssetService assetService;
private final DeviceProfileService deviceProfileService;
private final AssetProfileService assetProfileService;
private final DataValidator<CalculatedField> calculatedFieldDataValidator;
private final DataValidator<CalculatedFieldLink> calculatedFieldLinkDataValidator;
@ -72,7 +79,7 @@ public class BaseCalculatedFieldService implements CalculatedFieldService {
@Override
public CalculatedField findById(TenantId tenantId, CalculatedFieldId calculatedFieldId) {
log.trace("Executing findById, tenantId [{}], rpcId [{}]", tenantId, calculatedFieldId);
log.trace("Executing findById, tenantId [{}], calculatedFieldId [{}]", tenantId, calculatedFieldId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(calculatedFieldId, id -> INCORRECT_CALCULATED_FIELD_ID + id);
return calculatedFieldDao.findById(tenantId, calculatedFieldId.getId());
@ -80,12 +87,21 @@ public class BaseCalculatedFieldService implements CalculatedFieldService {
@Override
public void deleteCalculatedField(TenantId tenantId, CalculatedFieldId calculatedFieldId) {
log.trace("Executing deleteRpc, tenantId [{}], rpcId [{}]", tenantId, calculatedFieldId);
log.trace("Executing deleteCalculatedField, tenantId [{}], calculatedFieldId [{}]", tenantId, calculatedFieldId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(calculatedFieldId, id -> INCORRECT_CALCULATED_FIELD_ID + id);
calculatedFieldDao.removeById(tenantId, calculatedFieldId.getId());
}
@Override
public int deleteAllCalculatedFieldsByEntityId(TenantId tenantId, EntityId entityId) {
log.trace("Executing deleteAllCalculatedFieldsByEntityId, tenantId [{}], entityId [{}]", tenantId, entityId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
validateId(entityId.getId(), id -> "Incorrect entityId " + id);
List<CalculatedField> calculatedFields = calculatedFieldDao.removeAllByEntityId(tenantId, entityId);
return calculatedFields.size();
}
@Override
public CalculatedFieldLink saveCalculatedFieldLink(TenantId tenantId, CalculatedFieldLink calculatedFieldLink) {
calculatedFieldLinkDataValidator.validate(calculatedFieldLink, CalculatedFieldLink::getTenantId);
@ -98,6 +114,11 @@ public class BaseCalculatedFieldService implements CalculatedFieldService {
}
}
@Override
public boolean existsByEntityId(TenantId tenantId, EntityId entityId) {
return calculatedFieldDao.existsByTenantIdAndEntityId(tenantId, entityId);
}
@Override
public Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) {
return Optional.ofNullable(findById(tenantId, new CalculatedFieldId(entityId.getId())));
@ -114,6 +135,12 @@ public class BaseCalculatedFieldService implements CalculatedFieldService {
.orElseThrow(() -> new IllegalArgumentException("Asset with id [" + entityId.getId() + "] does not exist."));
case DEVICE -> Optional.ofNullable(deviceService.findDeviceById(tenantId, (DeviceId) entityId))
.orElseThrow(() -> new IllegalArgumentException("Device with id [" + entityId.getId() + "] does not exist."));
case ASSET_PROFILE ->
Optional.ofNullable(assetProfileService.findAssetProfileById(tenantId, (AssetProfileId) entityId))
.orElseThrow(() -> new IllegalArgumentException("Asset Profile with id [" + entityId.getId() + "] does not exist."));
case DEVICE_PROFILE ->
Optional.ofNullable(deviceProfileService.findDeviceProfileById(tenantId, (DeviceProfileId) entityId))
.orElseThrow(() -> new IllegalArgumentException("Device Profile with id [" + entityId.getId() + "] does not exist."));
default ->
throw new IllegalArgumentException("Entity type '" + entityId.getEntityType() + "' is not supported.");
}

9
dao/src/main/java/org/thingsboard/server/dao/calculated_field/CalculatedFieldDao.java

@ -16,7 +16,16 @@
package org.thingsboard.server.dao.calculated_field;
import org.thingsboard.server.common.data.calculated_field.CalculatedField;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.Dao;
import java.util.List;
public interface CalculatedFieldDao extends Dao<CalculatedField> {
boolean existsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId);
List<CalculatedField> removeAllByEntityId(TenantId tenantId, EntityId entityId);
}

4
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -230,8 +230,8 @@ public class DeviceProfileServiceImpl extends CachedVersionedEntityService<Devic
if (deviceProfile == null) {
return;
}
if (!force && deviceProfile.isDefault()) {
throw new DataValidationException("Deletion of Default Device Profile is prohibited!");
if (!force && (deviceProfile.isDefault() || calculatedFieldService.existsByEntityId(tenantId, id))) {
throw new DataValidationException("Deletion of Device Profile is prohibited!");
}
removeDeviceProfile(tenantId, deviceProfile);
}

4
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -337,8 +337,8 @@ public class DeviceServiceImpl extends CachedVersionedEntityService<DeviceCacheK
@Override
@Transactional
public void deleteEntity(TenantId tenantId, EntityId id, boolean force) {
if (!force && entityViewService.existsByTenantIdAndEntityId(tenantId, id)) {
throw new DataValidationException("Can't delete device that has entity views!");
if (!force && (entityViewService.existsByTenantIdAndEntityId(tenantId, id) || calculatedFieldService.existsByEntityId(tenantId, id))) {
throw new DataValidationException("Can't delete device that has entity views or calculated fields!");
}
Device device = deviceDao.findById(tenantId, id.getId());

5
dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java

@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.calculated_field.CalculatedFieldService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -60,6 +61,10 @@ public abstract class AbstractEntityService {
@Autowired
protected EntityViewService entityViewService;
@Lazy
@Autowired
protected CalculatedFieldService calculatedFieldService;
@Lazy
@Autowired(required = false)
protected EdgeService edgeService;

1
dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java

@ -75,6 +75,7 @@ public class CleanUpService {
submitTask(HousekeeperTask.deleteTelemetry(tenantId, entityId));
submitTask(HousekeeperTask.deleteEvents(tenantId, entityId));
submitTask(HousekeeperTask.deleteAlarms(tenantId, entityId));
submitTask(HousekeeperTask.deleteCalculatedFields(tenantId, entityId));
}
public void removeTenantEntities(TenantId tenantId, EntityType... entityTypes) {

6
dao/src/main/java/org/thingsboard/server/dao/sql/calculated_field/CalculatedFieldRepository.java

@ -18,7 +18,13 @@ package org.thingsboard.server.dao.sql.calculated_field;
import org.springframework.data.jpa.repository.JpaRepository;
import org.thingsboard.server.dao.model.sql.CalculatedFieldEntity;
import java.util.List;
import java.util.UUID;
public interface CalculatedFieldRepository extends JpaRepository<CalculatedFieldEntity, UUID> {
boolean existsByTenantIdAndEntityId(UUID tenantId, UUID entityId);
List<CalculatedFieldEntity> removeAllByTenantIdAndEntityId(UUID tenantId, UUID entityId);
}

16
dao/src/main/java/org/thingsboard/server/dao/sql/calculated_field/JpaCalculatedFieldDao.java

@ -15,16 +15,21 @@
*/
package org.thingsboard.server.dao.sql.calculated_field;
import jakarta.transaction.Transactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.calculated_field.CalculatedField;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.calculated_field.CalculatedFieldDao;
import org.thingsboard.server.dao.model.sql.CalculatedFieldEntity;
import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao;
import java.util.List;
import java.util.UUID;
@Slf4j
@ -35,6 +40,17 @@ public class JpaCalculatedFieldDao extends JpaAbstractDao<CalculatedFieldEntity,
private final CalculatedFieldRepository calculatedFieldRepository;
@Override
public boolean existsByTenantIdAndEntityId(TenantId tenantId, EntityId entityId) {
return calculatedFieldRepository.existsByTenantIdAndEntityId(tenantId.getId(), entityId.getId());
}
@Override
@Transactional
public List<CalculatedField> removeAllByEntityId(TenantId tenantId, EntityId entityId) {
return DaoUtil.convertDataList(calculatedFieldRepository.removeAllByTenantIdAndEntityId(tenantId.getId(), entityId.getId()));
}
@Override
protected Class<CalculatedFieldEntity> getEntityClass() {
return CalculatedFieldEntity.class;

21
dao/src/test/java/org/thingsboard/server/dao/service/AssetProfileServiceTest.java

@ -28,10 +28,12 @@ import org.thingsboard.server.common.data.EntityInfo;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.asset.AssetProfileInfo;
import org.thingsboard.server.common.data.calculated_field.CalculatedField;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.asset.AssetProfileService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.calculated_field.CalculatedFieldService;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList;
@ -43,6 +45,7 @@ import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@DaoSqlTest
public class AssetProfileServiceTest extends AbstractServiceTest {
@ -54,6 +57,8 @@ public class AssetProfileServiceTest extends AbstractServiceTest {
AssetProfileService assetProfileService;
@Autowired
AssetService assetService;
@Autowired
private CalculatedFieldService calculatedFieldService;
@Test
public void testSaveAssetProfile() {
@ -380,5 +385,21 @@ public class AssetProfileServiceTest extends AbstractServiceTest {
assertThat(assetProfileInfos).isEqualTo(expected);
}
@Test
public void testDeleteAssetProfileIfCalculatedFieldExists() {
AssetProfile assetProfile = this.createAssetProfile(tenantId, "Asset Profile");
AssetProfile savedAssetProfile = assetProfileService.saveAssetProfile(assetProfile);
CalculatedField calculatedField = new CalculatedField();
calculatedField.setTenantId(tenantId);
calculatedField.setName("Test CF");
calculatedField.setType("Simple");
calculatedField.setEntityId(savedAssetProfile.getId());
calculatedFieldService.save(calculatedField);
assertThatThrownBy(() -> assetProfileService.deleteAssetProfile(tenantId, savedAssetProfile.getId()))
.isInstanceOf(DataValidationException.class)
.hasMessage("Deletion of Asset Profile is prohibited!");
}
}

77
dao/src/test/java/org/thingsboard/server/dao/service/AssetServiceTest.java

@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.calculated_field.CalculatedField;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -39,6 +40,7 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.asset.AssetDao;
import org.thingsboard.server.dao.asset.AssetProfileService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.calculated_field.CalculatedFieldService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.relation.RelationService;
@ -47,6 +49,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@DaoSqlTest
@ -63,6 +66,8 @@ public class AssetServiceTest extends AbstractServiceTest {
@Autowired
private AssetProfileService assetProfileService;
@Autowired
private CalculatedFieldService calculatedFieldService;
@Autowired
private PlatformTransactionManager platformTransactionManager;
private IdComparator<Asset> idComparator = new IdComparator<>();
@ -214,24 +219,24 @@ public class AssetServiceTest extends AbstractServiceTest {
public void testFindAssetTypesByTenantId() throws Exception {
List<Asset> assets = new ArrayList<>();
try {
for (int i=0;i<3;i++) {
for (int i = 0; i < 3; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("My asset B"+i);
asset.setName("My asset B" + i);
asset.setType("typeB");
assets.add(assetService.saveAsset(asset));
}
for (int i=0;i<7;i++) {
for (int i = 0; i < 7; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("My asset C"+i);
asset.setName("My asset C" + i);
asset.setType("typeC");
assets.add(assetService.saveAsset(asset));
}
for (int i=0;i<9;i++) {
for (int i = 0; i < 9; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("My asset A"+i);
asset.setName("My asset A" + i);
asset.setType("typeA");
assets.add(assetService.saveAsset(asset));
}
@ -267,10 +272,10 @@ public class AssetServiceTest extends AbstractServiceTest {
@Test
public void testFindAssetsByTenantId() {
List<Asset> assets = new ArrayList<>();
for (int i=0;i<13;i++) {
for (int i = 0; i < 13; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("Asset"+i);
asset.setName("Asset" + i);
asset.setType("default");
assets.add(assetService.saveAsset(asset));
}
@ -303,11 +308,11 @@ public class AssetServiceTest extends AbstractServiceTest {
public void testFindAssetsByTenantIdAndName() {
String title1 = "Asset title 1";
List<AssetInfo> assetsTitle1 = new ArrayList<>();
for (int i=0;i<13;i++) {
for (int i = 0; i < 13; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1+suffix;
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
@ -315,11 +320,11 @@ public class AssetServiceTest extends AbstractServiceTest {
}
String title2 = "Asset title 2";
List<AssetInfo> assetsTitle2 = new ArrayList<>();
for (int i=0;i<17;i++) {
for (int i = 0; i < 17; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2+suffix;
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
@ -381,11 +386,11 @@ public class AssetServiceTest extends AbstractServiceTest {
String title1 = "Asset title 1";
String type1 = "typeA";
List<Asset> assetsType1 = new ArrayList<>();
for (int i=0;i<13;i++) {
for (int i = 0; i < 13; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1+suffix;
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type1);
@ -394,11 +399,11 @@ public class AssetServiceTest extends AbstractServiceTest {
String title2 = "Asset title 2";
String type2 = "typeB";
List<Asset> assetsType2 = new ArrayList<>();
for (int i=0;i<17;i++) {
for (int i = 0; i < 17; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2+suffix;
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type2);
@ -464,10 +469,10 @@ public class AssetServiceTest extends AbstractServiceTest {
CustomerId customerId = customer.getId();
List<AssetInfo> assets = new ArrayList<>();
for (int i=0;i<13;i++) {
for (int i = 0; i < 13; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("Asset"+i);
asset.setName("Asset" + i);
asset.setType("default");
asset = assetService.saveAsset(asset);
assets.add(new AssetInfo(assetService.assignAssetToCustomer(tenantId, asset.getId(), customerId), customer.getTitle(), customer.isPublic(), "default"));
@ -508,11 +513,11 @@ public class AssetServiceTest extends AbstractServiceTest {
String title1 = "Asset title 1";
List<Asset> assetsTitle1 = new ArrayList<>();
for (int i=0;i<17;i++) {
for (int i = 0; i < 17; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1+suffix;
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
@ -521,11 +526,11 @@ public class AssetServiceTest extends AbstractServiceTest {
}
String title2 = "Asset title 2";
List<Asset> assetsTitle2 = new ArrayList<>();
for (int i=0;i<13;i++) {
for (int i = 0; i < 13; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2+suffix;
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
@ -596,11 +601,11 @@ public class AssetServiceTest extends AbstractServiceTest {
String title1 = "Asset title 1";
String type1 = "typeC";
List<Asset> assetsType1 = new ArrayList<>();
for (int i=0;i<17;i++) {
for (int i = 0; i < 17; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title1+suffix;
String name = title1 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type1);
@ -610,11 +615,11 @@ public class AssetServiceTest extends AbstractServiceTest {
String title2 = "Asset title 2";
String type2 = "typeD";
List<Asset> assetsType2 = new ArrayList<>();
for (int i=0;i<13;i++) {
for (int i = 0; i < 13; i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = StringUtils.randomAlphanumeric(15);
String name = title2+suffix;
String name = title2 + suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type2);
@ -848,4 +853,24 @@ public class AssetServiceTest extends AbstractServiceTest {
);
}
@Test
public void testDeleteAssetIfCalculatedFieldExists() {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = assetService.saveAsset(asset);
CalculatedField calculatedField = new CalculatedField();
calculatedField.setTenantId(tenantId);
calculatedField.setName("Test CF");
calculatedField.setType("Simple");
calculatedField.setEntityId(savedAsset.getId());
calculatedFieldService.save(calculatedField);
assertThatThrownBy(() -> assetService.deleteAsset(tenantId, savedAsset.getId()))
.isInstanceOf(DataValidationException.class)
.hasMessage("Can't delete asset that has entity views or calculated fields!");
}
}

5
dao/src/test/java/org/thingsboard/server/dao/calculated_field/CalculatedFieldServiceTest.java → dao/src/test/java/org/thingsboard/server/dao/service/CalculatedFieldServiceTest.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.calculated_field;
package org.thingsboard.server.dao.service;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
@ -28,10 +28,9 @@ import org.thingsboard.server.common.data.calculated_field.CalculatedField;
import org.thingsboard.server.common.data.calculated_field.CalculatedFieldLink;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.dao.calculated_field.CalculatedFieldService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.AbstractServiceTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.UUID;

25
dao/src/test/java/org/thingsboard/server/dao/service/DeviceProfileServiceTest.java

@ -31,9 +31,11 @@ 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.OtaPackage;
import org.thingsboard.server.common.data.calculated_field.CalculatedField;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.calculated_field.CalculatedFieldService;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -49,6 +51,7 @@ import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
@DaoSqlTest
@ -60,6 +63,9 @@ public class DeviceProfileServiceTest extends AbstractServiceTest {
DeviceService deviceService;
@Autowired
OtaPackageService otaPackageService;
@Autowired
private CalculatedFieldService calculatedFieldService;
private IdComparator<DeviceProfile> idComparator = new IdComparator<>();
private IdComparator<DeviceProfileInfo> deviceProfileInfoIdComparator = new IdComparator<>();
@ -397,7 +403,7 @@ public class DeviceProfileServiceTest extends AbstractServiceTest {
var profileA = deviceProfileService.saveDeviceProfile(
createDeviceProfile(tenantId, "profile A"));
createDeviceProfile(tenantId, "profile A"));
deviceProfiles.add(deviceProfileService.saveDeviceProfile(profileA));
@ -478,4 +484,21 @@ public class DeviceProfileServiceTest extends AbstractServiceTest {
assertThat(deviceProfileInfos).isEqualTo(expected);
}
@Test
public void testDeleteDeviceProfileIfCalculatedFieldExists() {
DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile");
DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
CalculatedField calculatedField = new CalculatedField();
calculatedField.setTenantId(tenantId);
calculatedField.setName("Test CF");
calculatedField.setType("Simple");
calculatedField.setEntityId(savedDeviceProfile.getId());
calculatedFieldService.save(calculatedField);
assertThatThrownBy(() -> deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()))
.isInstanceOf(DataValidationException.class)
.hasMessage("Deletion of Device Profile is prohibited!");
}
}

20
dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceTest.java

@ -39,6 +39,7 @@ import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.calculated_field.CalculatedField;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId;
@ -50,6 +51,7 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.dao.calculated_field.CalculatedFieldService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceProfileService;
@ -87,6 +89,8 @@ public class DeviceServiceTest extends AbstractServiceTest {
@Autowired
TenantProfileService tenantProfileService;
@Autowired
private CalculatedFieldService calculatedFieldService;
@Autowired
private PlatformTransactionManager platformTransactionManager;
@SpyBean
private DeviceCredentialsDataValidator validator;
@ -1198,4 +1202,20 @@ public class DeviceServiceTest extends AbstractServiceTest {
);
}
@Test
public void testDeleteDeviceIfCalculatedFieldExists() {
Device device = saveDevice(tenantId, "Test");
CalculatedField calculatedField = new CalculatedField();
calculatedField.setTenantId(tenantId);
calculatedField.setName("Test CF");
calculatedField.setType("Simple");
calculatedField.setEntityId(device.getId());
calculatedFieldService.save(calculatedField);
assertThatThrownBy(() -> deviceService.deleteDevice(tenantId, device.getId()))
.isInstanceOf(DataValidationException.class)
.hasMessage("Can't delete device that has entity views or calculated fields!");
}
}

2
dao/src/test/java/org/thingsboard/server/dao/service/validator/CalculatedFieldLinkDataValidatorTest.java

@ -54,4 +54,4 @@ public class CalculatedFieldLinkDataValidatorTest {
.hasMessage("Can't update non existing calculated field link!");
}
}
}

Loading…
Cancel
Save