Commit 6475c696cc8ec937414d1beb13d29e55f871edac

Authored by VoBa
Committed by GitHub
2 parents 02895bf3 aa795578

Merge pull request #1108 from thingsboard/fix-for-delete

Fixes for cases when asset/device deleted but has entity view assigned
@@ -17,9 +17,10 @@ @@ -17,9 +17,10 @@
17 DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_name; 17 DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_name;
18 DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_search_text; 18 DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_search_text;
19 DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer; 19 DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_customer;
  20 +DROP MATERIALIZED VIEW IF EXISTS thingsboard.entity_view_by_tenant_and_entity_id;
20 21
21 DROP TABLE IF EXISTS thingsboard.entity_views; 22 DROP TABLE IF EXISTS thingsboard.entity_views;
22 - 23 +ControllerSqlTestSuite
23 CREATE TABLE IF NOT EXISTS thingsboard.entity_views ( 24 CREATE TABLE IF NOT EXISTS thingsboard.entity_views (
24 id timeuuid, 25 id timeuuid,
25 entity_id timeuuid, 26 entity_id timeuuid,
@@ -67,3 +68,14 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_cus @@ -67,3 +68,14 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_cus
67 AND id IS NOT NULL 68 AND id IS NOT NULL
68 PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id) 69 PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id)
69 WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC); 70 WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC);
  71 +
  72 +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS
  73 + SELECT *
  74 + from thingsboard.entity_views
  75 + WHERE tenant_id IS NOT NULL
  76 + AND customer_id IS NOT NULL
  77 + AND entity_id IS NOT NULL
  78 + AND search_text IS NOT NULL
  79 + AND id IS NOT NULL
  80 + PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id)
  81 + WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC);
@@ -30,6 +30,7 @@ import org.springframework.util.StringUtils; @@ -30,6 +30,7 @@ import org.springframework.util.StringUtils;
30 import org.thingsboard.server.common.data.Customer; 30 import org.thingsboard.server.common.data.Customer;
31 import org.thingsboard.server.common.data.EntitySubtype; 31 import org.thingsboard.server.common.data.EntitySubtype;
32 import org.thingsboard.server.common.data.EntityType; 32 import org.thingsboard.server.common.data.EntityType;
  33 +import org.thingsboard.server.common.data.EntityView;
33 import org.thingsboard.server.common.data.Tenant; 34 import org.thingsboard.server.common.data.Tenant;
34 import org.thingsboard.server.common.data.asset.Asset; 35 import org.thingsboard.server.common.data.asset.Asset;
35 import org.thingsboard.server.common.data.asset.AssetSearchQuery; 36 import org.thingsboard.server.common.data.asset.AssetSearchQuery;
@@ -43,6 +44,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; @@ -43,6 +44,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
43 import org.thingsboard.server.common.data.relation.EntitySearchDirection; 44 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
44 import org.thingsboard.server.dao.customer.CustomerDao; 45 import org.thingsboard.server.dao.customer.CustomerDao;
45 import org.thingsboard.server.dao.entity.AbstractEntityService; 46 import org.thingsboard.server.dao.entity.AbstractEntityService;
  47 +import org.thingsboard.server.dao.entityview.EntityViewService;
46 import org.thingsboard.server.dao.exception.DataValidationException; 48 import org.thingsboard.server.dao.exception.DataValidationException;
47 import org.thingsboard.server.dao.service.DataValidator; 49 import org.thingsboard.server.dao.service.DataValidator;
48 import org.thingsboard.server.dao.service.PaginatedRemover; 50 import org.thingsboard.server.dao.service.PaginatedRemover;
@@ -77,6 +79,9 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @@ -77,6 +79,9 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
77 private CustomerDao customerDao; 79 private CustomerDao customerDao;
78 80
79 @Autowired 81 @Autowired
  82 + private EntityViewService entityViewService;
  83 +
  84 + @Autowired
80 private CacheManager cacheManager; 85 private CacheManager cacheManager;
81 86
82 @Override 87 @Override
@@ -130,11 +135,21 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @@ -130,11 +135,21 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
130 validateId(assetId, INCORRECT_ASSET_ID + assetId); 135 validateId(assetId, INCORRECT_ASSET_ID + assetId);
131 deleteEntityRelations(assetId); 136 deleteEntityRelations(assetId);
132 137
133 - Cache cache = cacheManager.getCache(ASSET_CACHE);  
134 Asset asset = assetDao.findById(assetId.getId()); 138 Asset asset = assetDao.findById(assetId.getId());
  139 + try {
  140 + List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get();
  141 + if (entityViews != null && !entityViews.isEmpty()) {
  142 + throw new DataValidationException("Can't delete asset that is assigned to entity views!");
  143 + }
  144 + } catch (Exception e) {
  145 + log.error("Exception while finding entity views for assetId [{}]", assetId, e);
  146 + throw new RuntimeException("Exception while finding entity views for assetId [" + assetId + "]", e);
  147 + }
  148 +
135 List<Object> list = new ArrayList<>(); 149 List<Object> list = new ArrayList<>();
136 list.add(asset.getTenantId()); 150 list.add(asset.getTenantId());
137 list.add(asset.getName()); 151 list.add(asset.getName());
  152 + Cache cache = cacheManager.getCache(ASSET_CACHE);
138 cache.evict(list); 153 cache.evict(list);
139 154
140 assetDao.removeById(assetId.getId()); 155 assetDao.removeById(assetId.getId());
@@ -115,9 +115,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom @@ -115,9 +115,9 @@ public class CustomerServiceImpl extends AbstractEntityService implements Custom
115 throw new IncorrectParameterException("Unable to delete non-existent customer."); 115 throw new IncorrectParameterException("Unable to delete non-existent customer.");
116 } 116 }
117 dashboardService.unassignCustomerDashboards(customerId); 117 dashboardService.unassignCustomerDashboards(customerId);
  118 + entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId);
118 assetService.unassignCustomerAssets(customer.getTenantId(), customerId); 119 assetService.unassignCustomerAssets(customer.getTenantId(), customerId);
119 deviceService.unassignCustomerDevices(customer.getTenantId(), customerId); 120 deviceService.unassignCustomerDevices(customer.getTenantId(), customerId);
120 - entityViewService.unassignCustomerEntityViews(customer.getTenantId(), customerId);  
121 userService.deleteCustomerUsers(customer.getTenantId(), customerId); 121 userService.deleteCustomerUsers(customer.getTenantId(), customerId);
122 deleteEntityRelations(customerId); 122 deleteEntityRelations(customerId);
123 customerDao.removeById(customerId.getId()); 123 customerDao.removeById(customerId.getId());
@@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.Customer; @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.Customer;
31 import org.thingsboard.server.common.data.Device; 31 import org.thingsboard.server.common.data.Device;
32 import org.thingsboard.server.common.data.EntitySubtype; 32 import org.thingsboard.server.common.data.EntitySubtype;
33 import org.thingsboard.server.common.data.EntityType; 33 import org.thingsboard.server.common.data.EntityType;
  34 +import org.thingsboard.server.common.data.EntityView;
34 import org.thingsboard.server.common.data.Tenant; 35 import org.thingsboard.server.common.data.Tenant;
35 import org.thingsboard.server.common.data.device.DeviceSearchQuery; 36 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
36 import org.thingsboard.server.common.data.id.CustomerId; 37 import org.thingsboard.server.common.data.id.CustomerId;
@@ -45,6 +46,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials; @@ -45,6 +46,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentials;
45 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 46 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
46 import org.thingsboard.server.dao.customer.CustomerDao; 47 import org.thingsboard.server.dao.customer.CustomerDao;
47 import org.thingsboard.server.dao.entity.AbstractEntityService; 48 import org.thingsboard.server.dao.entity.AbstractEntityService;
  49 +import org.thingsboard.server.dao.entityview.EntityViewService;
48 import org.thingsboard.server.dao.exception.DataValidationException; 50 import org.thingsboard.server.dao.exception.DataValidationException;
49 import org.thingsboard.server.dao.service.DataValidator; 51 import org.thingsboard.server.dao.service.DataValidator;
50 import org.thingsboard.server.dao.service.PaginatedRemover; 52 import org.thingsboard.server.dao.service.PaginatedRemover;
@@ -87,6 +89,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -87,6 +89,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
87 private DeviceCredentialsService deviceCredentialsService; 89 private DeviceCredentialsService deviceCredentialsService;
88 90
89 @Autowired 91 @Autowired
  92 + private EntityViewService entityViewService;
  93 +
  94 + @Autowired
90 private CacheManager cacheManager; 95 private CacheManager cacheManager;
91 96
92 @Override 97 @Override
@@ -145,18 +150,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -145,18 +150,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
145 @Override 150 @Override
146 public void deleteDevice(DeviceId deviceId) { 151 public void deleteDevice(DeviceId deviceId) {
147 log.trace("Executing deleteDevice [{}]", deviceId); 152 log.trace("Executing deleteDevice [{}]", deviceId);
148 - Cache cache = cacheManager.getCache(DEVICE_CACHE);  
149 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); 153 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
  154 +
  155 + Device device = deviceDao.findById(deviceId.getId());
  156 + try {
  157 + List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get();
  158 + if (entityViews != null && !entityViews.isEmpty()) {
  159 + throw new DataValidationException("Can't delete device that is assigned to entity views!");
  160 + }
  161 + } catch (Exception e) {
  162 + log.error("Exception while finding entity views for deviceId [{}]", deviceId, e);
  163 + throw new RuntimeException("Exception while finding entity views for deviceId [" + deviceId + "]", e);
  164 + }
  165 +
150 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId); 166 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceId);
151 if (deviceCredentials != null) { 167 if (deviceCredentials != null) {
152 deviceCredentialsService.deleteDeviceCredentials(deviceCredentials); 168 deviceCredentialsService.deleteDeviceCredentials(deviceCredentials);
153 } 169 }
154 deleteEntityRelations(deviceId); 170 deleteEntityRelations(deviceId);
155 - Device device = deviceDao.findById(deviceId.getId()); 171 +
156 List<Object> list = new ArrayList<>(); 172 List<Object> list = new ArrayList<>();
157 list.add(device.getTenantId()); 173 list.add(device.getTenantId());
158 list.add(device.getName()); 174 list.add(device.getName());
  175 + Cache cache = cacheManager.getCache(DEVICE_CACHE);
159 cache.evict(list); 176 cache.evict(list);
  177 +
160 deviceDao.removeById(deviceId.getId()); 178 deviceDao.removeById(deviceId.getId());
161 } 179 }
162 180
@@ -40,7 +40,8 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; @@ -40,7 +40,8 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
40 import static com.datastax.driver.core.querybuilder.QueryBuilder.select; 40 import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
41 import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_ID_PROPERTY; 41 import static org.thingsboard.server.dao.model.ModelConstants.CUSTOMER_ID_PROPERTY;
42 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; 42 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN;
43 -import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_SEARCH_TEXT; 43 +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF;
  44 +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF;
44 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_NAME; 45 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_NAME;
45 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME; 46 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME;
46 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_NAME_PROPERTY; 47 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_VIEW_NAME_PROPERTY;
@@ -101,7 +102,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit @@ -101,7 +102,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
101 log.debug("Try to find entity views by tenantId [{}], customerId[{}] and pageLink [{}]", 102 log.debug("Try to find entity views by tenantId [{}], customerId[{}] and pageLink [{}]",
102 tenantId, customerId, pageLink); 103 tenantId, customerId, pageLink);
103 List<EntityViewEntity> entityViewEntities = findPageWithTextSearch( 104 List<EntityViewEntity> entityViewEntities = findPageWithTextSearch(
104 - ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_SEARCH_TEXT, 105 + ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF,
105 Arrays.asList(eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)), 106 Arrays.asList(eq(CUSTOMER_ID_PROPERTY, customerId), eq(TENANT_ID_PROPERTY, tenantId)),
106 pageLink); 107 pageLink);
107 log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}] and pageLink [{}]", 108 log.trace("Found find entity views [{}] by tenantId [{}], customerId [{}] and pageLink [{}]",
@@ -112,7 +113,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit @@ -112,7 +113,7 @@ public class CassandraEntityViewDao extends CassandraAbstractSearchTextDao<Entit
112 @Override 113 @Override
113 public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) { 114 public ListenableFuture<List<EntityView>> findEntityViewsByTenantIdAndEntityIdAsync(UUID tenantId, UUID entityId) {
114 log.debug("Try to find entity views by tenantId [{}] and entityId [{}]", tenantId, entityId); 115 log.debug("Try to find entity views by tenantId [{}] and entityId [{}]", tenantId, entityId);
115 - Select.Where query = select().from(getColumnFamilyName()).where(); 116 + Select.Where query = select().from(ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF).where();
116 query.and(eq(TENANT_ID_PROPERTY, tenantId)); 117 query.and(eq(TENANT_ID_PROPERTY, tenantId));
117 query.and(eq(ENTITY_ID_COLUMN, entityId)); 118 query.and(eq(ENTITY_ID_COLUMN, entityId));
118 return findListByStatementAsync(query); 119 return findListByStatementAsync(query);
@@ -150,7 +150,8 @@ public class ModelConstants { @@ -150,7 +150,8 @@ public class ModelConstants {
150 public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; 150 public static final String ENTITY_VIEW_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
151 public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; 151 public static final String ENTITY_VIEW_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
152 public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY; 152 public static final String ENTITY_VIEW_NAME_PROPERTY = DEVICE_NAME_PROPERTY;
153 - public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_AND_SEARCH_TEXT = "entity_view_by_tenant_and_customer"; 153 + public static final String ENTITY_VIEW_BY_TENANT_AND_CUSTOMER_CF = "entity_view_by_tenant_and_customer";
  154 + public static final String ENTITY_VIEW_BY_TENANT_AND_ENTITY_ID_CF = "entity_view_by_tenant_and_entity_id";
154 public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys"; 155 public static final String ENTITY_VIEW_KEYS_PROPERTY = "keys";
155 public static final String ENTITY_VIEW_START_TS_PROPERTY = "start_ts"; 156 public static final String ENTITY_VIEW_START_TS_PROPERTY = "start_ts";
156 public static final String ENTITY_VIEW_END_TS_PROPERTY = "end_ts"; 157 public static final String ENTITY_VIEW_END_TS_PROPERTY = "end_ts";
@@ -105,9 +105,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @@ -105,9 +105,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe
105 customerService.deleteCustomersByTenantId(tenantId); 105 customerService.deleteCustomersByTenantId(tenantId);
106 widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId); 106 widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId);
107 dashboardService.deleteDashboardsByTenantId(tenantId); 107 dashboardService.deleteDashboardsByTenantId(tenantId);
  108 + entityViewService.deleteEntityViewsByTenantId(tenantId);
108 assetService.deleteAssetsByTenantId(tenantId); 109 assetService.deleteAssetsByTenantId(tenantId);
109 deviceService.deleteDevicesByTenantId(tenantId); 110 deviceService.deleteDevicesByTenantId(tenantId);
110 - entityViewService.deleteEntityViewsByTenantId(tenantId);  
111 userService.deleteTenantAdmins(tenantId); 111 userService.deleteTenantAdmins(tenantId);
112 ruleChainService.deleteRuleChainsByTenantId(tenantId); 112 ruleChainService.deleteRuleChainsByTenantId(tenantId);
113 tenantDao.removeById(tenantId.getId()); 113 tenantDao.removeById(tenantId.getId());
@@ -671,3 +671,14 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_cus @@ -671,3 +671,14 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_cus
671 AND id IS NOT NULL 671 AND id IS NOT NULL
672 PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id) 672 PRIMARY KEY (tenant_id, customer_id, search_text, id, entity_id)
673 WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC); 673 WITH CLUSTERING ORDER BY (customer_id DESC, search_text ASC, id DESC);
  674 +
  675 +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.entity_view_by_tenant_and_entity_id AS
  676 + SELECT *
  677 + from thingsboard.entity_views
  678 + WHERE tenant_id IS NOT NULL
  679 + AND customer_id IS NOT NULL
  680 + AND entity_id IS NOT NULL
  681 + AND search_text IS NOT NULL
  682 + AND id IS NOT NULL
  683 + PRIMARY KEY (tenant_id, entity_id, customer_id, search_text, id)
  684 + WITH CLUSTERING ORDER BY (entity_id DESC, customer_id DESC, search_text ASC, id DESC);
@@ -24,6 +24,9 @@ caffeine.specs.devices.maxSize=100000 @@ -24,6 +24,9 @@ caffeine.specs.devices.maxSize=100000
24 caffeine.specs.assets.timeToLiveInMinutes=1440 24 caffeine.specs.assets.timeToLiveInMinutes=1440
25 caffeine.specs.assets.maxSize=100000 25 caffeine.specs.assets.maxSize=100000
26 26
  27 +caffeine.specs.entityViews.timeToLiveInMinutes=1440
  28 +caffeine.specs.entityViews.maxSize=100000
  29 +
27 caching.specs.devices.timeToLiveInMinutes=1440 30 caching.specs.devices.timeToLiveInMinutes=1440
28 caching.specs.devices.maxSize=100000 31 caching.specs.devices.maxSize=100000
29 32