Merge pull request #1108 from thingsboard/fix-for-delete
Fixes for cases when asset/device deleted but has entity view assigned
This commit is contained in:
commit
6475c696cc
@ -17,9 +17,10 @@
|
|||||||
DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_name;
|
DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_name;
|
||||||
DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_search_text;
|
DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_search_text;
|
||||||
DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer;
|
DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer;
|
||||||
|
DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id;
|
||||||
|
|
||||||
DROP TABLE IF EXISTS thingsboard.entity_views;
|
DROP TABLE IF EXISTS thingsboard.entity_views;
|
||||||
|
ControllerSqlTestSuite
|
||||||
CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
|
CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
|
||||||
id timeuuid,
|
id timeuuid,
|
||||||
entity_id timeuuid,
|
entity_id timeuuid,
|
||||||
@ -67,3 +68,14 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_cus
|
|||||||
AND id IS NOT NULL
|
AND id IS NOT NULL
|
||||||
PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id)
|
PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id)
|
||||||
WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC);
|
WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC);
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS
|
||||||
|
SELECT *
|
||||||
|
from thingsboard.entity_views
|
||||||
|
WHERE tenant_id IS NOT NULL
|
||||||
|
AND customer_id IS NOT NULL
|
||||||
|
AND entity_id IS NOT NULL
|
||||||
|
AND search_text IS NOT NULL
|
||||||
|
AND id IS NOT NULL
|
||||||
|
PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id)
|
||||||
|
WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC);
|
||||||
@ -30,6 +30,7 @@ import org.springframework.util.StringUtils;
|
|||||||
import org.thingsboard.server.common.data.Customer;
|
import org.thingsboard.server.common.data.Customer;
|
||||||
import org.thingsboard.server.common.data.EntitySubtype;
|
import org.thingsboard.server.common.data.EntitySubtype;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
|
import org.thingsboard.server.common.data.EntityView;
|
||||||
import org.thingsboard.server.common.data.Tenant;
|
import org.thingsboard.server.common.data.Tenant;
|
||||||
import org.thingsboard.server.common.data.asset.Asset;
|
import org.thingsboard.server.common.data.asset.Asset;
|
||||||
import org.thingsboard.server.common.data.asset.AssetSearchQuery;
|
import org.thingsboard.server.common.data.asset.AssetSearchQuery;
|
||||||
@ -43,6 +44,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
|
|||||||
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
||||||
import org.thingsboard.server.dao.customer.CustomerDao;
|
import org.thingsboard.server.dao.customer.CustomerDao;
|
||||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
||||||
|
import org.thingsboard.server.dao.entityview.EntityViewService;
|
||||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||||
import org.thingsboard.server.dao.service.DataValidator;
|
import org.thingsboard.server.dao.service.DataValidator;
|
||||||
import org.thingsboard.server.dao.service.PaginatedRemover;
|
import org.thingsboard.server.dao.service.PaginatedRemover;
|
||||||
@ -76,6 +78,9 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
|
|||||||
@Autowired
|
@Autowired
|
||||||
private CustomerDao customerDao;
|
private CustomerDao customerDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EntityViewService entityViewService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private CacheManager cacheManager;
|
private CacheManager cacheManager;
|
||||||
|
|
||||||
@ -130,11 +135,21 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
|
|||||||
validateId(assetId, INCORRECT_ASSET_ID + assetId);
|
validateId(assetId, INCORRECT_ASSET_ID + assetId);
|
||||||
deleteEntityRelations(assetId);
|
deleteEntityRelations(assetId);
|
||||||
|
|
||||||
Cache cache = cacheManager.getCache(ASSET_CACHE);
|
|
||||||
Asset asset = assetDao.findById(assetId.getId());
|
Asset asset = assetDao.findById(assetId.getId());
|
||||||
|
try {
|
||||||
|
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get();
|
||||||
|
if (entityViews != null && !entityViews.isEmpty()) {
|
||||||
|
throw new DataValidationException("Can't delete asset that is assigned to entity views!");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Exception while finding entity views for assetId [{}]", assetId, e);
|
||||||
|
throw new RuntimeException("Exception while finding entity views for assetId [" + assetId + "]", e);
|
||||||
|
}
|
||||||
|
|
||||||
List<Object> list = new ArrayList<>();
|
List<Object> list = new ArrayList<>();
|
||||||
list.add(asset.getTenantId());
|
list.add(asset.getTenantId());
|
||||||
list.add(asset.getName());
|
list.add(asset.getName());
|
||||||
|
Cache cache = cacheManager.getCache(ASSET_CACHE);
|
||||||
cache.evict(list);
|
cache.evict(list);
|
||||||
|
|
||||||
assetDao.removeById(assetId.getId());
|
assetDao.removeById(assetId.getId());
|
||||||
|
|||||||
@ -115,9 +115,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
|
|||||||
throw new IncorrectParameterException("Unable to delete non-existent customer.");
|
throw new IncorrectParameterException("Unable to delete non-existent customer.");
|
||||||
}
|
}
|
||||||
dashboardService.unassignCustomerDashboards(customerId);
|
dashboardService.unassignCustomerDashboards(customerId);
|
||||||
|
entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId);
|
||||||
assetService.unassignCustomerAssets(customer.getTenantId(), customerId);
|
assetService.unassignCustomerAssets(customer.getTenantId(), customerId);
|
||||||
deviceService.unassignCustomerDevices(customer.getTenantId(), customerId);
|
deviceService.unassignCustomerDevices(customer.getTenantId(), customerId);
|
||||||
entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId);
|
|
||||||
userService.deleteCustomerUsers(customer.getTenantId(), customerId);
|
userService.deleteCustomerUsers(customer.getTenantId(), customerId);
|
||||||
deleteEntityRelations(customerId);
|
deleteEntityRelations(customerId);
|
||||||
customerDao.removeById(customerId.getId());
|
customerDao.removeById(customerId.getId());
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.Customer;
|
|||||||
import org.thingsboard.server.common.data.Device;
|
import org.thingsboard.server.common.data.Device;
|
||||||
import org.thingsboard.server.common.data.EntitySubtype;
|
import org.thingsboard.server.common.data.EntitySubtype;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
|
import org.thingsboard.server.common.data.EntityView;
|
||||||
import org.thingsboard.server.common.data.Tenant;
|
import org.thingsboard.server.common.data.Tenant;
|
||||||
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
|
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
@ -45,6 +46,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
|
|||||||
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
|
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
|
||||||
import org.thingsboard.server.dao.customer.CustomerDao;
|
import org.thingsboard.server.dao.customer.CustomerDao;
|
||||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
||||||
|
import org.thingsboard.server.dao.entityview.EntityViewService;
|
||||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||||
import org.thingsboard.server.dao.service.DataValidator;
|
import org.thingsboard.server.dao.service.DataValidator;
|
||||||
import org.thingsboard.server.dao.service.PaginatedRemover;
|
import org.thingsboard.server.dao.service.PaginatedRemover;
|
||||||
@ -86,6 +88,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
|
|||||||
@Autowired
|
@Autowired
|
||||||
private DeviceCredentialsService deviceCredentialsService;
|
private DeviceCredentialsService deviceCredentialsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EntityViewService entityViewService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private CacheManager cacheManager;
|
private CacheManager cacheManager;
|
||||||
|
|
||||||
@ -145,18 +150,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
|
|||||||
@Override
|
@Override
|
||||||
public void deleteDevice(DeviceId deviceId) {
|
public void deleteDevice(DeviceId deviceId) {
|
||||||
log.trace("Executing deleteDevice [{}]", deviceId);
|
log.trace("Executing deleteDevice [{}]", deviceId);
|
||||||
Cache cache = cacheManager.getCache(DEVICE_CACHE);
|
|
||||||
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
|
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
|
||||||
|
|
||||||
|
Device device = deviceDao.findById(deviceId.getId());
|
||||||
|
try {
|
||||||
|
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get();
|
||||||
|
if (entityViews != null && !entityViews.isEmpty()) {
|
||||||
|
throw new DataValidationException("Can't delete device that is assigned to entity views!");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Exception while finding entity views for deviceId [{}]", deviceId, e);
|
||||||
|
throw new RuntimeException("Exception while finding entity views for deviceId [" + deviceId + "]", e);
|
||||||
|
}
|
||||||
|
|
||||||
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId);
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId);
|
||||||
if (deviceCredentials != null) {
|
if (deviceCredentials != null) {
|
||||||
deviceCredentialsService.deleteDeviceCredentials(deviceCredentials);
|
deviceCredentialsService.deleteDeviceCredentials(deviceCredentials);
|
||||||
}
|
}
|
||||||
deleteEntityRelations(deviceId);
|
deleteEntityRelations(deviceId);
|
||||||
Device device = deviceDao.findById(deviceId.getId());
|
|
||||||
List<Object> list = new ArrayList<>();
|
List<Object> list = new ArrayList<>();
|
||||||
list.add(device.getTenantId());
|
list.add(device.getTenantId());
|
||||||
list.add(device.getName());
|
list.add(device.getName());
|
||||||
|
Cache cache = cacheManager.getCache(DEVICE_CACHE);
|
||||||
cache.evict(list);
|
cache.evict(list);
|
||||||
|
|
||||||
deviceDao.removeById(deviceId.getId());
|
deviceDao.removeById(deviceId.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,7 +40,8 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
|
|||||||
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
|
import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_ID_PROPERTY;
|
import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_ID_PROPERTY;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN;
|
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_SEARCH_TEXT;
|
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF;
|
||||||
|
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_NAME;
|
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_NAME;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME;
|
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_NAME_PROPERTY;
|
import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_NAME_PROPERTY;
|
||||||
@ -101,7 +102,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
|
|||||||
log.debug("Try to find entity views by tenantId [{}], customerId[{}] and pageLink [{}]",
|
log.debug("Try to find entity views by tenantId [{}], customerId[{}] and pageLink [{}]",
|
||||||
tenantId, customerId, pageLink);
|
tenantId, customerId, pageLink);
|
||||||
List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(
|
List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(
|
||||||
ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_SEARCH_TEXT,
|
ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF,
|
||||||
Arrays.asList(eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)),
|
Arrays.asList(eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)),
|
||||||
pageLink);
|
pageLink);
|
||||||
log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}] and pageLink [{}]",
|
log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}] and pageLink [{}]",
|
||||||
@ -112,7 +113,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
|
|||||||
@Override
|
@Override
|
||||||
public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) {
|
public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) {
|
||||||
log.debug("Try to find entity views by tenantId [{}] and entityId [{}]", tenantId, entityId);
|
log.debug("Try to find entity views by tenantId [{}] and entityId [{}]", tenantId, entityId);
|
||||||
Select.Where query = select().from(getColumnFamilyName()).where();
|
Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF).where();
|
||||||
query.and(eq(TENANT_ID_PROPERTY, tenantId));
|
query.and(eq(TENANT_ID_PROPERTY, tenantId));
|
||||||
query.and(eq(ENTITY_ID_COLUMN, entityId));
|
query.and(eq(ENTITY_ID_COLUMN, entityId));
|
||||||
return findListByStatementAsync(query);
|
return findListByStatementAsync(query);
|
||||||
|
|||||||
@ -150,7 +150,8 @@ public class ModelConstants {
|
|||||||
public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
|
public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
|
||||||
public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
|
public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
|
||||||
public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY;
|
public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY;
|
||||||
public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_SEARCH_TEXT = "entity_view_by_tenant_and_customer";
|
public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF = "entity_view_by_tenant_and_customer";
|
||||||
|
public static final String ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF = "entity_view_by_tenant_and_entity_id";
|
||||||
public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys";
|
public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys";
|
||||||
public static final String ENTITY_VIEW_START_TS_PROPERTY = "start_ts";
|
public static final String ENTITY_VIEW_START_TS_PROPERTY = "start_ts";
|
||||||
public static final String ENTITY_VIEW_END_TS_PROPERTY = "end_ts";
|
public static final String ENTITY_VIEW_END_TS_PROPERTY = "end_ts";
|
||||||
|
|||||||
@ -105,9 +105,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
|
|||||||
customerService.deleteCustomersByTenantId(tenantId);
|
customerService.deleteCustomersByTenantId(tenantId);
|
||||||
widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId);
|
widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId);
|
||||||
dashboardService.deleteDashboardsByTenantId(tenantId);
|
dashboardService.deleteDashboardsByTenantId(tenantId);
|
||||||
|
entityViewService.deleteEntityViewsByTenantId(tenantId);
|
||||||
assetService.deleteAssetsByTenantId(tenantId);
|
assetService.deleteAssetsByTenantId(tenantId);
|
||||||
deviceService.deleteDevicesByTenantId(tenantId);
|
deviceService.deleteDevicesByTenantId(tenantId);
|
||||||
entityViewService.deleteEntityViewsByTenantId(tenantId);
|
|
||||||
userService.deleteTenantAdmins(tenantId);
|
userService.deleteTenantAdmins(tenantId);
|
||||||
ruleChainService.deleteRuleChainsByTenantId(tenantId);
|
ruleChainService.deleteRuleChainsByTenantId(tenantId);
|
||||||
tenantDao.removeById(tenantId.getId());
|
tenantDao.removeById(tenantId.getId());
|
||||||
|
|||||||
@ -671,3 +671,14 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_cus
|
|||||||
AND id IS NOT NULL
|
AND id IS NOT NULL
|
||||||
PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id)
|
PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id)
|
||||||
WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC);
|
WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC);
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS
|
||||||
|
SELECT *
|
||||||
|
from thingsboard.entity_views
|
||||||
|
WHERE tenant_id IS NOT NULL
|
||||||
|
AND customer_id IS NOT NULL
|
||||||
|
AND entity_id IS NOT NULL
|
||||||
|
AND search_text IS NOT NULL
|
||||||
|
AND id IS NOT NULL
|
||||||
|
PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id)
|
||||||
|
WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC);
|
||||||
@ -24,6 +24,9 @@ caffeine.specs.devices.maxSize=100000
|
|||||||
caffeine.specs.assets.timeToLiveInMinutes=1440
|
caffeine.specs.assets.timeToLiveInMinutes=1440
|
||||||
caffeine.specs.assets.maxSize=100000
|
caffeine.specs.assets.maxSize=100000
|
||||||
|
|
||||||
|
caffeine.specs.entityViews.timeToLiveInMinutes=1440
|
||||||
|
caffeine.specs.entityViews.maxSize=100000
|
||||||
|
|
||||||
caching.specs.devices.timeToLiveInMinutes=1440
|
caching.specs.devices.timeToLiveInMinutes=1440
|
||||||
caching.specs.devices.maxSize=100000
|
caching.specs.devices.maxSize=100000
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user