diff --git a/application/src/main/data/upgrade/2.1.1/schema_update.cql b/application/src/main/data/upgrade/2.1.1/schema_update.cql index a633634f60..c477e8a02a 100644 --- a/application/src/main/data/upgrade/2.1.1/schema_update.cql +++ b/application/src/main/data/upgrade/2.1.1/schema_update.cql @@ -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_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_entity_id; DROP TABLE IF EXISTS thingsboard.entity_views; - +ControllerSqlTestSuite CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( 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 PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id) 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); \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index 6c8b60914e..f35b89069c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -30,6 +30,7 @@ import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntitySubtype; 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.asset.Asset; 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.dao.customer.CustomerDao; 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.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -76,6 +78,9 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @Autowired private CustomerDao customerDao; + @Autowired + private EntityViewService entityViewService; + @Autowired private CacheManager cacheManager; @@ -130,11 +135,21 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ validateId(assetId, INCORRECT_ASSET_ID + assetId); deleteEntityRelations(assetId); - Cache cache = cacheManager.getCache(ASSET_CACHE); Asset asset = assetDao.findById(assetId.getId()); + try { + List 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 list = new ArrayList<>(); list.add(asset.getTenantId()); list.add(asset.getName()); + Cache cache = cacheManager.getCache(ASSET_CACHE); cache.evict(list); assetDao.removeById(assetId.getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java index 9d96f3a97a..a9b8bfe181 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java @@ -115,9 +115,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom throw new IncorrectParameterException("Unable to delete non-existent customer."); } dashboardService.unassignCustomerDashboards(customerId); + entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId); assetService.unassignCustomerAssets(customer.getTenantId(), customerId); deviceService.unassignCustomerDevices(customer.getTenantId(), customerId); - entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId); userService.deleteCustomerUsers(customer.getTenantId(), customerId); deleteEntityRelations(customerId); customerDao.removeById(customerId.getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 3930e3a94f..6f9ea62244 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntitySubtype; 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.device.DeviceSearchQuery; 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.dao.customer.CustomerDao; 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.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -86,6 +88,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @Autowired private DeviceCredentialsService deviceCredentialsService; + @Autowired + private EntityViewService entityViewService; + @Autowired private CacheManager cacheManager; @@ -145,18 +150,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @Override public void deleteDevice(DeviceId deviceId) { log.trace("Executing deleteDevice [{}]", deviceId); - Cache cache = cacheManager.getCache(DEVICE_CACHE); validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); + + Device device = deviceDao.findById(deviceId.getId()); + try { + List 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); if (deviceCredentials != null) { deviceCredentialsService.deleteDeviceCredentials(deviceCredentials); } deleteEntityRelations(deviceId); - Device device = deviceDao.findById(deviceId.getId()); + List list = new ArrayList<>(); list.add(device.getTenantId()); list.add(device.getName()); + Cache cache = cacheManager.getCache(DEVICE_CACHE); cache.evict(list); + deviceDao.removeById(deviceId.getId()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java index 395f9022b0..a03dd89815 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entityview/CassandraEntityViewDao.java @@ -40,7 +40,8 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; 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.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_SEARCH_TEXT_COLUMN_FAMILY_NAME; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_NAME_PROPERTY; @@ -101,7 +102,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao 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)), pageLink); log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", @@ -112,7 +113,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID 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(ENTITY_ID_COLUMN, entityId)); return findListByStatementAsync(query); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 9890ff62d3..a487ede93a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -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_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_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_START_TS_PROPERTY = "start_ts"; public static final String ENTITY_VIEW_END_TS_PROPERTY = "end_ts"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index a94e715616..189c7139ac 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -105,9 +105,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe customerService.deleteCustomersByTenantId(tenantId); widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId); dashboardService.deleteDashboardsByTenantId(tenantId); + entityViewService.deleteEntityViewsByTenantId(tenantId); assetService.deleteAssetsByTenantId(tenantId); deviceService.deleteDevicesByTenantId(tenantId); - entityViewService.deleteEntityViewsByTenantId(tenantId); userService.deleteTenantAdmins(tenantId); ruleChainService.deleteRuleChainsByTenantId(tenantId); tenantDao.removeById(tenantId.getId()); diff --git a/dao/src/main/resources/cassandra/schema-entities.cql b/dao/src/main/resources/cassandra/schema-entities.cql index bd978f7960..7ccd41f03a 100644 --- a/dao/src/main/resources/cassandra/schema-entities.cql +++ b/dao/src/main/resources/cassandra/schema-entities.cql @@ -671,3 +671,14 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_cus AND id IS NOT NULL PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id) 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); \ No newline at end of file diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index 20cf91c3dd..a61c285136 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -24,6 +24,9 @@ caffeine.specs.devices.maxSize=100000 caffeine.specs.assets.timeToLiveInMinutes=1440 caffeine.specs.assets.maxSize=100000 +caffeine.specs.entityViews.timeToLiveInMinutes=1440 +caffeine.specs.entityViews.maxSize=100000 + caching.specs.devices.timeToLiveInMinutes=1440 caching.specs.devices.maxSize=100000