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