Commit 093eafb462cab211107761c6fa54f1aba54835cb
1 parent
f45ba17f
delete timeseries fixes and controller, entityView findByName added
Showing
9 changed files
with
224 additions
and
44 deletions
@@ -29,7 +29,6 @@ import org.springframework.web.bind.annotation.RequestParam; | @@ -29,7 +29,6 @@ import org.springframework.web.bind.annotation.RequestParam; | ||
29 | import org.springframework.web.bind.annotation.ResponseBody; | 29 | import org.springframework.web.bind.annotation.ResponseBody; |
30 | import org.springframework.web.bind.annotation.ResponseStatus; | 30 | import org.springframework.web.bind.annotation.ResponseStatus; |
31 | import org.springframework.web.bind.annotation.RestController; | 31 | import org.springframework.web.bind.annotation.RestController; |
32 | -import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; | ||
33 | import org.thingsboard.server.common.data.Customer; | 32 | import org.thingsboard.server.common.data.Customer; |
34 | import org.thingsboard.server.common.data.DataConstants; | 33 | import org.thingsboard.server.common.data.DataConstants; |
35 | import org.thingsboard.server.common.data.EntitySubtype; | 34 | import org.thingsboard.server.common.data.EntitySubtype; |
@@ -39,7 +38,6 @@ import org.thingsboard.server.common.data.audit.ActionType; | @@ -39,7 +38,6 @@ import org.thingsboard.server.common.data.audit.ActionType; | ||
39 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; | 38 | import org.thingsboard.server.common.data.entityview.EntityViewSearchQuery; |
40 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 39 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
41 | import org.thingsboard.server.common.data.id.CustomerId; | 40 | import org.thingsboard.server.common.data.id.CustomerId; |
42 | -import org.thingsboard.server.common.data.id.DeviceId; | ||
43 | import org.thingsboard.server.common.data.id.EntityId; | 41 | import org.thingsboard.server.common.data.id.EntityId; |
44 | import org.thingsboard.server.common.data.id.EntityViewId; | 42 | import org.thingsboard.server.common.data.id.EntityViewId; |
45 | import org.thingsboard.server.common.data.id.TenantId; | 43 | import org.thingsboard.server.common.data.id.TenantId; |
@@ -47,7 +45,6 @@ import org.thingsboard.server.common.data.id.UUIDBased; | @@ -47,7 +45,6 @@ import org.thingsboard.server.common.data.id.UUIDBased; | ||
47 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 45 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
48 | import org.thingsboard.server.common.data.page.TextPageData; | 46 | import org.thingsboard.server.common.data.page.TextPageData; |
49 | import org.thingsboard.server.common.data.page.TextPageLink; | 47 | import org.thingsboard.server.common.data.page.TextPageLink; |
50 | -import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | ||
51 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 48 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
52 | import org.thingsboard.server.dao.model.ModelConstants; | 49 | import org.thingsboard.server.dao.model.ModelConstants; |
53 | import org.thingsboard.server.service.security.model.SecurityUser; | 50 | import org.thingsboard.server.service.security.model.SecurityUser; |
@@ -174,7 +171,7 @@ public class EntityViewController extends BaseController { | @@ -174,7 +171,7 @@ public class EntityViewController extends BaseController { | ||
174 | EntityView entityView = checkEntityViewId(entityViewId); | 171 | EntityView entityView = checkEntityViewId(entityViewId); |
175 | entityViewService.deleteEntityView(entityViewId); | 172 | entityViewService.deleteEntityView(entityViewId); |
176 | logEntityAction(entityViewId, entityView, entityView.getCustomerId(), | 173 | logEntityAction(entityViewId, entityView, entityView.getCustomerId(), |
177 | - ActionType.DELETED,null, strEntityViewId); | 174 | + ActionType.DELETED, null, strEntityViewId); |
178 | } catch (Exception e) { | 175 | } catch (Exception e) { |
179 | logEntityAction(emptyId(EntityType.ENTITY_VIEW), | 176 | logEntityAction(emptyId(EntityType.ENTITY_VIEW), |
180 | null, | 177 | null, |
@@ -185,10 +182,23 @@ public class EntityViewController extends BaseController { | @@ -185,10 +182,23 @@ public class EntityViewController extends BaseController { | ||
185 | } | 182 | } |
186 | 183 | ||
187 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") | 184 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") |
185 | + @RequestMapping(value = "/tenant/entityViews", params = {"entityViewName"}, method = RequestMethod.GET) | ||
186 | + @ResponseBody | ||
187 | + public EntityView getTenantEntityView( | ||
188 | + @RequestParam String entityViewName) throws ThingsboardException { | ||
189 | + try { | ||
190 | + TenantId tenantId = getCurrentUser().getTenantId(); | ||
191 | + return checkNotNull(entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName)); | ||
192 | + } catch (Exception e) { | ||
193 | + throw handleException(e); | ||
194 | + } | ||
195 | + } | ||
196 | + | ||
197 | + @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ||
188 | @RequestMapping(value = "/customer/{customerId}/entityView/{entityViewId}", method = RequestMethod.POST) | 198 | @RequestMapping(value = "/customer/{customerId}/entityView/{entityViewId}", method = RequestMethod.POST) |
189 | @ResponseBody | 199 | @ResponseBody |
190 | public EntityView assignEntityViewToCustomer(@PathVariable(CUSTOMER_ID) String strCustomerId, | 200 | public EntityView assignEntityViewToCustomer(@PathVariable(CUSTOMER_ID) String strCustomerId, |
191 | - @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { | 201 | + @PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException { |
192 | checkParameter(CUSTOMER_ID, strCustomerId); | 202 | checkParameter(CUSTOMER_ID, strCustomerId); |
193 | checkParameter(ENTITY_VIEW_ID, strEntityViewId); | 203 | checkParameter(ENTITY_VIEW_ID, strEntityViewId); |
194 | try { | 204 | try { |
@@ -49,9 +49,11 @@ import org.thingsboard.server.common.data.kv.Aggregation; | @@ -49,9 +49,11 @@ import org.thingsboard.server.common.data.kv.Aggregation; | ||
49 | import org.thingsboard.server.common.data.kv.AttributeKey; | 49 | import org.thingsboard.server.common.data.kv.AttributeKey; |
50 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 50 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
51 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | 51 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
52 | +import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery; | ||
52 | import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; | 53 | import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; |
53 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 54 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
54 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 55 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
56 | +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | ||
55 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 57 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
56 | import org.thingsboard.server.common.data.kv.KvEntry; | 58 | import org.thingsboard.server.common.data.kv.KvEntry; |
57 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 59 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
@@ -60,12 +62,10 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | @@ -60,12 +62,10 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
60 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 62 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
61 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | 63 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; |
62 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; | 64 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
63 | -import org.thingsboard.server.dao.attributes.AttributesService; | ||
64 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 65 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
65 | import org.thingsboard.server.service.security.AccessValidator; | 66 | import org.thingsboard.server.service.security.AccessValidator; |
66 | import org.thingsboard.server.service.security.model.SecurityUser; | 67 | import org.thingsboard.server.service.security.model.SecurityUser; |
67 | import org.thingsboard.server.service.telemetry.AttributeData; | 68 | import org.thingsboard.server.service.telemetry.AttributeData; |
68 | -import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; | ||
69 | import org.thingsboard.server.service.telemetry.TsData; | 69 | import org.thingsboard.server.service.telemetry.TsData; |
70 | import org.thingsboard.server.service.telemetry.exception.InvalidParametersException; | 70 | import org.thingsboard.server.service.telemetry.exception.InvalidParametersException; |
71 | import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; | 71 | import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; |
@@ -250,6 +250,60 @@ public class TelemetryController extends BaseController { | @@ -250,6 +250,60 @@ public class TelemetryController extends BaseController { | ||
250 | } | 250 | } |
251 | 251 | ||
252 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | 252 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
253 | + @RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE) | ||
254 | + @ResponseBody | ||
255 | + public DeferredResult<ResponseEntity> deleteEntityTimeseries(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, | ||
256 | + @RequestParam(name = "keys") String keysStr, | ||
257 | + @RequestParam(name = "deleteAllDataForKeys", defaultValue = "false") boolean deleteAllDataForKeys, | ||
258 | + @RequestParam(name = "startTs", required = false) Long startTs, | ||
259 | + @RequestParam(name = "endTs", required = false) Long endTs, | ||
260 | + @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { | ||
261 | + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | ||
262 | + return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); | ||
263 | + } | ||
264 | + | ||
265 | + private DeferredResult<ResponseEntity> deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, | ||
266 | + Long startTs, Long endTs, boolean rewriteLatestIfDeleted) throws ThingsboardException { | ||
267 | + List<String> keys = toKeysList(keysStr); | ||
268 | + if (keys.isEmpty()) { | ||
269 | + return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST); | ||
270 | + } | ||
271 | + SecurityUser user = getCurrentUser(); | ||
272 | + | ||
273 | + long deleteFromTs; | ||
274 | + long deleteToTs; | ||
275 | + if (deleteAllDataForKeys) { | ||
276 | + deleteFromTs = 0L; | ||
277 | + deleteToTs = System.currentTimeMillis(); | ||
278 | + } else { | ||
279 | + deleteFromTs = startTs; | ||
280 | + deleteToTs = endTs; | ||
281 | + } | ||
282 | + | ||
283 | + return accessValidator.validateEntityAndCallback(user, entityIdStr, (result, entityId) -> { | ||
284 | + List<DeleteTsKvQuery> deleteTsKvQueries = new ArrayList<>(); | ||
285 | + for (String key : keys) { | ||
286 | + deleteTsKvQueries.add(new BaseDeleteTsKvQuery(key, deleteFromTs, deleteToTs, rewriteLatestIfDeleted)); | ||
287 | + } | ||
288 | + | ||
289 | + ListenableFuture<List<Void>> future = tsService.remove(entityId, deleteTsKvQueries); | ||
290 | + Futures.addCallback(future, new FutureCallback<List<Void>>() { | ||
291 | + @Override | ||
292 | + public void onSuccess(@Nullable List<Void> tmp) { | ||
293 | + logTimeseriesDeleted(user, entityId, keys, null); | ||
294 | + result.setResult(new ResponseEntity<>(HttpStatus.OK)); | ||
295 | + } | ||
296 | + | ||
297 | + @Override | ||
298 | + public void onFailure(Throwable t) { | ||
299 | + logTimeseriesDeleted(user, entityId, keys, t); | ||
300 | + result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR)); | ||
301 | + } | ||
302 | + }, executor); | ||
303 | + }); | ||
304 | + } | ||
305 | + | ||
306 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") | ||
253 | @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE) | 307 | @RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE) |
254 | @ResponseBody | 308 | @ResponseBody |
255 | public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr, | 309 | public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr, |
@@ -506,6 +560,15 @@ public class TelemetryController extends BaseController { | @@ -506,6 +560,15 @@ public class TelemetryController extends BaseController { | ||
506 | }; | 560 | }; |
507 | } | 561 | } |
508 | 562 | ||
563 | + private void logTimeseriesDeleted(SecurityUser user, EntityId entityId, List<String> keys, Throwable e) { | ||
564 | + try { | ||
565 | + logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.TIMESERIES_DELETED, toException(e), | ||
566 | + keys); | ||
567 | + } catch (ThingsboardException te) { | ||
568 | + log.warn("Failed to log timeseries delete", te); | ||
569 | + } | ||
570 | + } | ||
571 | + | ||
509 | private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List<String> keys, Throwable e) { | 572 | private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List<String> keys, Throwable e) { |
510 | try { | 573 | try { |
511 | logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_DELETED, toException(e), | 574 | logEntityAction(user, (UUIDBased & EntityId) entityId, null, null, ActionType.ATTRIBUTES_DELETED, toException(e), |
@@ -24,6 +24,7 @@ public enum ActionType { | @@ -24,6 +24,7 @@ public enum ActionType { | ||
24 | UPDATED(false), // log entity | 24 | UPDATED(false), // log entity |
25 | ATTRIBUTES_UPDATED(false), // log attributes/values | 25 | ATTRIBUTES_UPDATED(false), // log attributes/values |
26 | ATTRIBUTES_DELETED(false), // log attributes | 26 | ATTRIBUTES_DELETED(false), // log attributes |
27 | + TIMESERIES_DELETED(false), // log timeseries | ||
27 | RPC_CALL(false), // log method and params | 28 | RPC_CALL(false), // log method and params |
28 | CREDENTIALS_UPDATED(false), // log new credentials | 29 | CREDENTIALS_UPDATED(false), // log new credentials |
29 | ASSIGNED_TO_CUSTOMER(false), // log customer name | 30 | ASSIGNED_TO_CUSTOMER(false), // log customer name |
@@ -32,11 +33,11 @@ public enum ActionType { | @@ -32,11 +33,11 @@ public enum ActionType { | ||
32 | SUSPENDED(false), // log string id | 33 | SUSPENDED(false), // log string id |
33 | CREDENTIALS_READ(true), // log device id | 34 | CREDENTIALS_READ(true), // log device id |
34 | ATTRIBUTES_READ(true), // log attributes | 35 | ATTRIBUTES_READ(true), // log attributes |
35 | - RELATION_ADD_OR_UPDATE (false), | ||
36 | - RELATION_DELETED (false), | ||
37 | - RELATIONS_DELETED (false), | ||
38 | - ALARM_ACK (false), | ||
39 | - ALARM_CLEAR (false); | 36 | + RELATION_ADD_OR_UPDATE(false), |
37 | + RELATION_DELETED(false), | ||
38 | + RELATIONS_DELETED(false), | ||
39 | + ALARM_ACK(false), | ||
40 | + ALARM_CLEAR(false); | ||
40 | 41 | ||
41 | private final boolean isRead; | 42 | private final boolean isRead; |
42 | 43 |
@@ -43,6 +43,8 @@ public interface EntityViewService { | @@ -43,6 +43,8 @@ public interface EntityViewService { | ||
43 | 43 | ||
44 | EntityView findEntityViewById(EntityViewId entityViewId); | 44 | EntityView findEntityViewById(EntityViewId entityViewId); |
45 | 45 | ||
46 | + EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name); | ||
47 | + | ||
46 | TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink); | 48 | TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink); |
47 | 49 | ||
48 | TextPageData<EntityView> findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type); | 50 | TextPageData<EntityView> findEntityViewByTenantIdAndType(TenantId tenantId, TextPageLink pageLink, String type); |
@@ -29,8 +29,6 @@ import org.springframework.cache.annotation.Cacheable; | @@ -29,8 +29,6 @@ import org.springframework.cache.annotation.Cacheable; | ||
29 | import org.springframework.cache.annotation.Caching; | 29 | import org.springframework.cache.annotation.Caching; |
30 | import org.springframework.stereotype.Service; | 30 | import org.springframework.stereotype.Service; |
31 | import org.thingsboard.server.common.data.Customer; | 31 | import org.thingsboard.server.common.data.Customer; |
32 | -import org.thingsboard.server.common.data.DataConstants; | ||
33 | -import org.thingsboard.server.common.data.Device; | ||
34 | import org.thingsboard.server.common.data.EntitySubtype; | 32 | import org.thingsboard.server.common.data.EntitySubtype; |
35 | import org.thingsboard.server.common.data.EntityType; | 33 | import org.thingsboard.server.common.data.EntityType; |
36 | import org.thingsboard.server.common.data.EntityView; | 34 | import org.thingsboard.server.common.data.EntityView; |
@@ -40,12 +38,10 @@ import org.thingsboard.server.common.data.id.CustomerId; | @@ -40,12 +38,10 @@ import org.thingsboard.server.common.data.id.CustomerId; | ||
40 | import org.thingsboard.server.common.data.id.EntityId; | 38 | import org.thingsboard.server.common.data.id.EntityId; |
41 | import org.thingsboard.server.common.data.id.EntityViewId; | 39 | import org.thingsboard.server.common.data.id.EntityViewId; |
42 | import org.thingsboard.server.common.data.id.TenantId; | 40 | import org.thingsboard.server.common.data.id.TenantId; |
43 | -import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
44 | import org.thingsboard.server.common.data.page.TextPageData; | 41 | import org.thingsboard.server.common.data.page.TextPageData; |
45 | import org.thingsboard.server.common.data.page.TextPageLink; | 42 | import org.thingsboard.server.common.data.page.TextPageLink; |
46 | import org.thingsboard.server.common.data.relation.EntityRelation; | 43 | import org.thingsboard.server.common.data.relation.EntityRelation; |
47 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; | 44 | import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
48 | -import org.thingsboard.server.dao.attributes.AttributesService; | ||
49 | import org.thingsboard.server.dao.customer.CustomerDao; | 45 | import org.thingsboard.server.dao.customer.CustomerDao; |
50 | import org.thingsboard.server.dao.entity.AbstractEntityService; | 46 | import org.thingsboard.server.dao.entity.AbstractEntityService; |
51 | import org.thingsboard.server.dao.exception.DataValidationException; | 47 | import org.thingsboard.server.dao.exception.DataValidationException; |
@@ -56,15 +52,13 @@ import org.thingsboard.server.dao.tenant.TenantDao; | @@ -56,15 +52,13 @@ import org.thingsboard.server.dao.tenant.TenantDao; | ||
56 | import javax.annotation.Nullable; | 52 | import javax.annotation.Nullable; |
57 | import java.util.ArrayList; | 53 | import java.util.ArrayList; |
58 | import java.util.Arrays; | 54 | import java.util.Arrays; |
59 | -import java.util.Collection; | ||
60 | import java.util.Collections; | 55 | import java.util.Collections; |
61 | import java.util.Comparator; | 56 | import java.util.Comparator; |
62 | import java.util.List; | 57 | import java.util.List; |
63 | -import java.util.concurrent.ExecutionException; | 58 | +import java.util.Optional; |
64 | import java.util.stream.Collectors; | 59 | import java.util.stream.Collectors; |
65 | 60 | ||
66 | import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE; | 61 | import static org.thingsboard.server.common.data.CacheConstants.ENTITY_VIEW_CACHE; |
67 | -import static org.thingsboard.server.common.data.CacheConstants.RELATIONS_CACHE; | ||
68 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; | 62 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
69 | import static org.thingsboard.server.dao.service.Validator.validateId; | 63 | import static org.thingsboard.server.dao.service.Validator.validateId; |
70 | import static org.thingsboard.server.dao.service.Validator.validatePageLink; | 64 | import static org.thingsboard.server.dao.service.Validator.validatePageLink; |
@@ -96,6 +90,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -96,6 +90,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
96 | 90 | ||
97 | @Caching(evict = { | 91 | @Caching(evict = { |
98 | @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.entityId}"), | 92 | @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.entityId}"), |
93 | + @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.tenantId, #entityView.name}"), | ||
99 | @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}")}) | 94 | @CacheEvict(cacheNames = ENTITY_VIEW_CACHE, key = "{#entityView.id}")}) |
100 | @Override | 95 | @Override |
101 | public EntityView saveEntityView(EntityView entityView) { | 96 | public EntityView saveEntityView(EntityView entityView) { |
@@ -137,6 +132,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -137,6 +132,15 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
137 | return entityViewDao.findById(entityViewId.getId()); | 132 | return entityViewDao.findById(entityViewId.getId()); |
138 | } | 133 | } |
139 | 134 | ||
135 | + @Cacheable(cacheNames = ENTITY_VIEW_CACHE, key = "{#tenantId, #name}") | ||
136 | + @Override | ||
137 | + public EntityView findEntityViewByTenantIdAndName(TenantId tenantId, String name) { | ||
138 | + log.trace("Executing findEntityViewByTenantIdAndName [{}][{}]", tenantId, name); | ||
139 | + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | ||
140 | + Optional<EntityView> entityViewOpt = entityViewDao.findEntityViewByTenantIdAndName(tenantId.getId(), name); | ||
141 | + return entityViewOpt.orElse(null); | ||
142 | + } | ||
143 | + | ||
140 | @Override | 144 | @Override |
141 | public TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink) { | 145 | public TextPageData<EntityView> findEntityViewByTenantId(TenantId tenantId, TextPageLink pageLink) { |
142 | log.trace("Executing findEntityViewsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); | 146 | log.trace("Executing findEntityViewsByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); |
@@ -255,6 +259,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | @@ -255,6 +259,7 @@ public class EntityViewServiceImpl extends AbstractEntityService implements Enti | ||
255 | deleteEntityRelations(entityViewId); | 259 | deleteEntityRelations(entityViewId); |
256 | EntityView entityView = entityViewDao.findById(entityViewId.getId()); | 260 | EntityView entityView = entityViewDao.findById(entityViewId.getId()); |
257 | cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getEntityId())); | 261 | cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getEntityId())); |
262 | + cacheManager.getCache(ENTITY_VIEW_CACHE).evict(Arrays.asList(entityView.getTenantId(), entityView.getName())); | ||
258 | entityViewDao.removeById(entityViewId.getId()); | 263 | entityViewDao.removeById(entityViewId.getId()); |
259 | } | 264 | } |
260 | 265 |
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.sql.timeseries; | @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.sql.timeseries; | ||
17 | 17 | ||
18 | import com.google.common.base.Function; | 18 | import com.google.common.base.Function; |
19 | import com.google.common.collect.Lists; | 19 | import com.google.common.collect.Lists; |
20 | +import com.google.common.util.concurrent.FutureCallback; | ||
20 | import com.google.common.util.concurrent.Futures; | 21 | import com.google.common.util.concurrent.Futures; |
21 | import com.google.common.util.concurrent.ListenableFuture; | 22 | import com.google.common.util.concurrent.ListenableFuture; |
22 | import com.google.common.util.concurrent.ListeningExecutorService; | 23 | import com.google.common.util.concurrent.ListeningExecutorService; |
@@ -31,6 +32,7 @@ import org.springframework.stereotype.Component; | @@ -31,6 +32,7 @@ import org.springframework.stereotype.Component; | ||
31 | import org.thingsboard.server.common.data.UUIDConverter; | 32 | import org.thingsboard.server.common.data.UUIDConverter; |
32 | import org.thingsboard.server.common.data.id.EntityId; | 33 | import org.thingsboard.server.common.data.id.EntityId; |
33 | import org.thingsboard.server.common.data.kv.Aggregation; | 34 | import org.thingsboard.server.common.data.kv.Aggregation; |
35 | +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; | ||
34 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 36 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
35 | import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | 37 | import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; |
36 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | 38 | import org.thingsboard.server.common.data.kv.ReadTsKvQuery; |
@@ -41,9 +43,9 @@ import org.thingsboard.server.dao.model.sql.TsKvEntity; | @@ -41,9 +43,9 @@ import org.thingsboard.server.dao.model.sql.TsKvEntity; | ||
41 | import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey; | 43 | import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey; |
42 | import org.thingsboard.server.dao.model.sql.TsKvLatestEntity; | 44 | import org.thingsboard.server.dao.model.sql.TsKvLatestEntity; |
43 | import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; | 45 | import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; |
46 | +import org.thingsboard.server.dao.timeseries.SimpleListenableFuture; | ||
44 | import org.thingsboard.server.dao.timeseries.TimeseriesDao; | 47 | import org.thingsboard.server.dao.timeseries.TimeseriesDao; |
45 | import org.thingsboard.server.dao.timeseries.TsInsertExecutorType; | 48 | import org.thingsboard.server.dao.timeseries.TsInsertExecutorType; |
46 | -import org.thingsboard.server.dao.util.SqlDao; | ||
47 | import org.thingsboard.server.dao.util.SqlTsDao; | 49 | import org.thingsboard.server.dao.util.SqlTsDao; |
48 | 50 | ||
49 | import javax.annotation.Nullable; | 51 | import javax.annotation.Nullable; |
@@ -53,6 +55,7 @@ import java.util.ArrayList; | @@ -53,6 +55,7 @@ import java.util.ArrayList; | ||
53 | import java.util.List; | 55 | import java.util.List; |
54 | import java.util.Optional; | 56 | import java.util.Optional; |
55 | import java.util.concurrent.CompletableFuture; | 57 | import java.util.concurrent.CompletableFuture; |
58 | +import java.util.concurrent.ExecutionException; | ||
56 | import java.util.concurrent.Executors; | 59 | import java.util.concurrent.Executors; |
57 | import java.util.stream.Collectors; | 60 | import java.util.stream.Collectors; |
58 | 61 | ||
@@ -64,6 +67,8 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; | @@ -64,6 +67,8 @@ import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; | ||
64 | @SqlTsDao | 67 | @SqlTsDao |
65 | public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService implements TimeseriesDao { | 68 | public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService implements TimeseriesDao { |
66 | 69 | ||
70 | + private static final String DESC_ORDER = "DESC"; | ||
71 | + | ||
67 | @Value("${sql.ts_inserts_executor_type}") | 72 | @Value("${sql.ts_inserts_executor_type}") |
68 | private String insertExecutorType; | 73 | private String insertExecutorType; |
69 | 74 | ||
@@ -326,14 +331,72 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | @@ -326,14 +331,72 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | ||
326 | 331 | ||
327 | @Override | 332 | @Override |
328 | public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { | 333 | public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { |
329 | - TsKvLatestEntity latestEntity = new TsKvLatestEntity(); | ||
330 | - latestEntity.setEntityType(entityId.getEntityType()); | ||
331 | - latestEntity.setEntityId(fromTimeUUID(entityId.getId())); | ||
332 | - latestEntity.setKey(query.getKey()); | ||
333 | - return service.submit(() -> { | ||
334 | - tsKvLatestRepository.delete(latestEntity); | ||
335 | - return null; | 334 | + ListenableFuture<TsKvEntry> latestFuture = findLatest(entityId, query.getKey()); |
335 | + | ||
336 | + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestFuture, tsKvEntry -> { | ||
337 | + long ts = tsKvEntry.getTs(); | ||
338 | + return ts > query.getStartTs() && ts <= query.getEndTs(); | ||
339 | + }, service); | ||
340 | + | ||
341 | + ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | ||
342 | + if (isRemove) { | ||
343 | + TsKvLatestEntity latestEntity = new TsKvLatestEntity(); | ||
344 | + latestEntity.setEntityType(entityId.getEntityType()); | ||
345 | + latestEntity.setEntityId(fromTimeUUID(entityId.getId())); | ||
346 | + latestEntity.setKey(query.getKey()); | ||
347 | + return service.submit(() -> { | ||
348 | + tsKvLatestRepository.delete(latestEntity); | ||
349 | + return null; | ||
350 | + }); | ||
351 | + } | ||
352 | + return Futures.immediateFuture(null); | ||
353 | + }, service); | ||
354 | + | ||
355 | + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>(); | ||
356 | + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() { | ||
357 | + @Override | ||
358 | + public void onSuccess(@Nullable Void result) { | ||
359 | + if (query.getRewriteLatestIfDeleted()) { | ||
360 | + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | ||
361 | + if (isRemove) { | ||
362 | + return getNewLatestEntryFuture(entityId, query); | ||
363 | + } | ||
364 | + return Futures.immediateFuture(null); | ||
365 | + }, service); | ||
366 | + | ||
367 | + try { | ||
368 | + resultFuture.set(savedLatestFuture.get()); | ||
369 | + } catch (InterruptedException | ExecutionException e) { | ||
370 | + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e); | ||
371 | + } | ||
372 | + } else { | ||
373 | + resultFuture.set(null); | ||
374 | + } | ||
375 | + } | ||
376 | + | ||
377 | + @Override | ||
378 | + public void onFailure(Throwable t) { | ||
379 | + log.warn("[{}] Failed to process remove of the latest value", entityId, t); | ||
380 | + } | ||
336 | }); | 381 | }); |
382 | + return resultFuture; | ||
383 | + } | ||
384 | + | ||
385 | + private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) { | ||
386 | + long startTs = 0; | ||
387 | + long endTs = query.getStartTs() - 1; | ||
388 | + ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1, | ||
389 | + Aggregation.NONE, DESC_ORDER); | ||
390 | + ListenableFuture<List<TsKvEntry>> future = findAllAsync(entityId, findNewLatestQuery); | ||
391 | + | ||
392 | + return Futures.transformAsync(future, entryList -> { | ||
393 | + if (entryList.size() == 1) { | ||
394 | + return saveLatest(entityId, entryList.get(0)); | ||
395 | + } else { | ||
396 | + log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey()); | ||
397 | + } | ||
398 | + return Futures.immediateFuture(null); | ||
399 | + }, service); | ||
337 | } | 400 | } |
338 | 401 | ||
339 | @Override | 402 | @Override |
@@ -47,7 +47,7 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite | @@ -47,7 +47,7 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite | ||
47 | @Modifying | 47 | @Modifying |
48 | @Query("DELETE FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " + | 48 | @Query("DELETE FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " + |
49 | "AND tskv.entityType = :entityType AND tskv.key = :entityKey " + | 49 | "AND tskv.entityType = :entityType AND tskv.key = :entityKey " + |
50 | - "AND tskv.ts > :startTs AND tskv.ts < :endTs") | 50 | + "AND tskv.ts > :startTs AND tskv.ts <= :endTs") |
51 | void delete(@Param("entityId") String entityId, | 51 | void delete(@Param("entityId") String entityId, |
52 | @Param("entityType") EntityType entityType, | 52 | @Param("entityType") EntityType entityType, |
53 | @Param("entityKey") String key, | 53 | @Param("entityKey") String key, |
@@ -48,7 +48,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | @@ -48,7 +48,6 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
48 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 48 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
49 | import org.thingsboard.server.dao.model.ModelConstants; | 49 | import org.thingsboard.server.dao.model.ModelConstants; |
50 | import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao; | 50 | import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao; |
51 | -import org.thingsboard.server.dao.util.NoSqlDao; | ||
52 | import org.thingsboard.server.dao.util.NoSqlTsDao; | 51 | import org.thingsboard.server.dao.util.NoSqlTsDao; |
53 | 52 | ||
54 | import javax.annotation.Nullable; | 53 | import javax.annotation.Nullable; |
@@ -62,6 +61,7 @@ import java.util.Arrays; | @@ -62,6 +61,7 @@ import java.util.Arrays; | ||
62 | import java.util.Collections; | 61 | import java.util.Collections; |
63 | import java.util.List; | 62 | import java.util.List; |
64 | import java.util.Optional; | 63 | import java.util.Optional; |
64 | +import java.util.concurrent.ExecutionException; | ||
65 | import java.util.stream.Collectors; | 65 | import java.util.stream.Collectors; |
66 | 66 | ||
67 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; | 67 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; |
@@ -434,14 +434,14 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -434,14 +434,14 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
434 | public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { | 434 | public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { |
435 | ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(entityId, query.getKey()); | 435 | ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(entityId, query.getKey()); |
436 | 436 | ||
437 | - ListenableFuture<Boolean> booleanFuture = Futures.transformAsync(latestEntryFuture, latestEntry -> { | 437 | + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestEntryFuture, latestEntry -> { |
438 | long ts = latestEntry.getTs(); | 438 | long ts = latestEntry.getTs(); |
439 | - if (ts >= query.getStartTs() && ts <= query.getEndTs()) { | ||
440 | - return Futures.immediateFuture(true); | 439 | + if (ts > query.getStartTs() && ts <= query.getEndTs()) { |
440 | + return true; | ||
441 | } else { | 441 | } else { |
442 | log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey()); | 442 | log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey()); |
443 | } | 443 | } |
444 | - return Futures.immediateFuture(false); | 444 | + return false; |
445 | }, readResultsProcessingExecutor); | 445 | }, readResultsProcessingExecutor); |
446 | 446 | ||
447 | ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | 447 | ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { |
@@ -451,18 +451,34 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -451,18 +451,34 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
451 | return Futures.immediateFuture(null); | 451 | return Futures.immediateFuture(null); |
452 | }, readResultsProcessingExecutor); | 452 | }, readResultsProcessingExecutor); |
453 | 453 | ||
454 | - if (query.getRewriteLatestIfDeleted()) { | ||
455 | - ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | ||
456 | - if (isRemove) { | ||
457 | - return getNewLatestEntryFuture(entityId, query); | 454 | + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>(); |
455 | + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() { | ||
456 | + @Override | ||
457 | + public void onSuccess(@Nullable Void result) { | ||
458 | + if (query.getRewriteLatestIfDeleted()) { | ||
459 | + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | ||
460 | + if (isRemove) { | ||
461 | + return getNewLatestEntryFuture(entityId, query); | ||
462 | + } | ||
463 | + return Futures.immediateFuture(null); | ||
464 | + }, readResultsProcessingExecutor); | ||
465 | + | ||
466 | + try { | ||
467 | + resultFuture.set(savedLatestFuture.get()); | ||
468 | + } catch (InterruptedException | ExecutionException e) { | ||
469 | + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e); | ||
470 | + } | ||
471 | + } else { | ||
472 | + resultFuture.set(null); | ||
458 | } | 473 | } |
459 | - return Futures.immediateFuture(null); | ||
460 | - }, readResultsProcessingExecutor); | 474 | + } |
461 | 475 | ||
462 | - return Futures.transformAsync(Futures.allAsList(Arrays.asList(savedLatestFuture, removedLatestFuture)), | ||
463 | - list -> Futures.immediateFuture(null), readResultsProcessingExecutor); | ||
464 | - } | ||
465 | - return removedLatestFuture; | 476 | + @Override |
477 | + public void onFailure(Throwable t) { | ||
478 | + log.warn("[{}] Failed to process remove of the latest value", entityId, t); | ||
479 | + } | ||
480 | + }); | ||
481 | + return resultFuture; | ||
466 | } | 482 | } |
467 | 483 | ||
468 | private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) { | 484 | private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) { |
@@ -152,7 +152,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -152,7 +152,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
152 | } | 152 | } |
153 | 153 | ||
154 | @Test | 154 | @Test |
155 | - public void testDeleteDeviceTsData() throws Exception { | 155 | + public void testDeleteDeviceTsDataWithoutOverwritingLatest() throws Exception { |
156 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); | 156 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); |
157 | 157 | ||
158 | saveEntries(deviceId, 10000); | 158 | saveEntries(deviceId, 10000); |
@@ -172,6 +172,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -172,6 +172,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
172 | } | 172 | } |
173 | 173 | ||
174 | @Test | 174 | @Test |
175 | + public void testDeleteDeviceTsDataWithOverwritingLatest() throws Exception { | ||
176 | + DeviceId deviceId = new DeviceId(UUIDs.timeBased()); | ||
177 | + | ||
178 | + saveEntries(deviceId, 10000); | ||
179 | + saveEntries(deviceId, 20000); | ||
180 | + saveEntries(deviceId, 30000); | ||
181 | + saveEntries(deviceId, 40000); | ||
182 | + | ||
183 | + tsService.remove(deviceId, Collections.singletonList( | ||
184 | + new BaseDeleteTsKvQuery(STRING_KEY, 25000, 45000, true))).get(); | ||
185 | + | ||
186 | + List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList( | ||
187 | + new BaseReadTsKvQuery(STRING_KEY, 5000, 45000, 10000, 10, Aggregation.NONE))).get(); | ||
188 | + Assert.assertEquals(2, list.size()); | ||
189 | + | ||
190 | + List<TsKvEntry> latest = tsService.findLatest(deviceId, Collections.singletonList(STRING_KEY)).get(); | ||
191 | + Assert.assertEquals(20000, latest.get(0).getTs()); | ||
192 | + } | ||
193 | + | ||
194 | + @Test | ||
175 | public void testFindDeviceTsData() throws Exception { | 195 | public void testFindDeviceTsData() throws Exception { |
176 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); | 196 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); |
177 | List<TsKvEntry> entries = new ArrayList<>(); | 197 | List<TsKvEntry> entries = new ArrayList<>(); |