Commit 63384d458b781537f75dec8c54b388083cd597b9

Authored by Andrii Shvaika
1 parent 9600cc04

Alarm Search queries

Showing 38 changed files with 1262 additions and 122 deletions
@@ -34,20 +34,26 @@ import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; @@ -34,20 +34,26 @@ import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
34 import org.thingsboard.server.common.data.kv.ReadTsKvQuery; 34 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
35 import org.thingsboard.server.common.data.kv.TsKvEntry; 35 import org.thingsboard.server.common.data.kv.TsKvEntry;
36 import org.thingsboard.server.common.data.page.PageData; 36 import org.thingsboard.server.common.data.page.PageData;
  37 +import org.thingsboard.server.common.data.query.AlarmData;
  38 +import org.thingsboard.server.common.data.query.AlarmDataQuery;
37 import org.thingsboard.server.common.data.query.EntityData; 39 import org.thingsboard.server.common.data.query.EntityData;
  40 +import org.thingsboard.server.common.data.query.EntityDataPageLink;
38 import org.thingsboard.server.common.data.query.EntityDataQuery; 41 import org.thingsboard.server.common.data.query.EntityDataQuery;
  42 +import org.thingsboard.server.common.data.query.EntityDataSortOrder;
39 import org.thingsboard.server.common.data.query.EntityKey; 43 import org.thingsboard.server.common.data.query.EntityKey;
40 import org.thingsboard.server.common.data.query.EntityKeyType; 44 import org.thingsboard.server.common.data.query.EntityKeyType;
41 import org.thingsboard.server.common.data.query.TsValue; 45 import org.thingsboard.server.common.data.query.TsValue;
  46 +import org.thingsboard.server.dao.alarm.AlarmService;
42 import org.thingsboard.server.dao.entity.EntityService; 47 import org.thingsboard.server.dao.entity.EntityService;
43 -import org.thingsboard.server.dao.entityview.EntityViewService; 48 +import org.thingsboard.server.dao.model.ModelConstants;
44 import org.thingsboard.server.dao.timeseries.TimeseriesService; 49 import org.thingsboard.server.dao.timeseries.TimeseriesService;
45 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; 50 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
46 import org.thingsboard.server.queue.util.TbCoreComponent; 51 import org.thingsboard.server.queue.util.TbCoreComponent;
47 import org.thingsboard.server.service.executors.DbCallbackExecutorService; 52 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
48 -import org.thingsboard.server.service.telemetry.DefaultTelemetryWebSocketService;  
49 import org.thingsboard.server.service.telemetry.TelemetryWebSocketService; 53 import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
50 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; 54 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
  55 +import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd;
  56 +import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUpdate;
51 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; 57 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
52 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; 58 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
53 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; 59 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
@@ -63,7 +69,6 @@ import java.util.ArrayList; @@ -63,7 +69,6 @@ import java.util.ArrayList;
63 import java.util.Arrays; 69 import java.util.Arrays;
64 import java.util.Collection; 70 import java.util.Collection;
65 import java.util.Collections; 71 import java.util.Collections;
66 -import java.util.Comparator;  
67 import java.util.HashMap; 72 import java.util.HashMap;
68 import java.util.LinkedHashMap; 73 import java.util.LinkedHashMap;
69 import java.util.LinkedHashSet; 74 import java.util.LinkedHashSet;
@@ -88,7 +93,7 @@ import java.util.stream.Collectors; @@ -88,7 +93,7 @@ import java.util.stream.Collectors;
88 public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubscriptionService { 93 public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubscriptionService {
89 94
90 private static final int DEFAULT_LIMIT = 100; 95 private static final int DEFAULT_LIMIT = 100;
91 - private final Map<String, Map<Integer, TbEntityDataSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>(); 96 + private final Map<String, Map<Integer, TbAbstractDataSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>();
92 97
93 @Autowired 98 @Autowired
94 private TelemetryWebSocketService wsService; 99 private TelemetryWebSocketService wsService;
@@ -97,6 +102,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -97,6 +102,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
97 private EntityService entityService; 102 private EntityService entityService;
98 103
99 @Autowired 104 @Autowired
  105 + private AlarmService alarmService;
  106 +
  107 + @Autowired
100 @Lazy 108 @Lazy
101 private TbLocalSubscriptionService localSubscriptionService; 109 private TbLocalSubscriptionService localSubscriptionService;
102 110
@@ -114,10 +122,12 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -114,10 +122,12 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
114 122
115 @Value("${database.ts.type}") 123 @Value("${database.ts.type}")
116 private String databaseTsType; 124 private String databaseTsType;
117 - @Value("${server.ws.dynamic_page_link_refresh_interval:6}") 125 + @Value("${server.ws.dynamic_page_link.refresh_interval:6}")
118 private long dynamicPageLinkRefreshInterval; 126 private long dynamicPageLinkRefreshInterval;
119 - @Value("${server.ws.dynamic_page_link_refresh_pool_size:1}") 127 + @Value("${server.ws.dynamic_page_link.refresh_pool_size:1}")
120 private int dynamicPageLinkRefreshPoolSize; 128 private int dynamicPageLinkRefreshPoolSize;
  129 + @Value("${server.ws.max_entities_per_alarm_subscription:1000}")
  130 + private int maxEntitiesPerAlarmSubscription;
121 131
122 private ExecutorService wsCallBackExecutor; 132 private ExecutorService wsCallBackExecutor;
123 private boolean tsInSqlDB; 133 private boolean tsInSqlDB;
@@ -195,6 +205,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -195,6 +205,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
195 ctx.setData(data); 205 ctx.setData(data);
196 ctx.cancelRefreshTask(); 206 ctx.cancelRefreshTask();
197 if (ctx.getQuery().getPageLink().isDynamic()) { 207 if (ctx.getQuery().getPageLink().isDynamic()) {
  208 + //TODO: validate number of dynamic page links against rate limits. Ignore dynamic flag if limit is reached.
198 TbEntityDataSubCtx finalCtx = ctx; 209 TbEntityDataSubCtx finalCtx = ctx;
199 ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay( 210 ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay(
200 () -> refreshDynamicQuery(tenantId, customerId, finalCtx), 211 () -> refreshDynamicQuery(tenantId, customerId, finalCtx),
@@ -230,6 +241,39 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -230,6 +241,39 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
230 }, wsCallBackExecutor); 241 }, wsCallBackExecutor);
231 } 242 }
232 243
  244 + @Override
  245 + public void handleCmd(TelemetryWebSocketSessionRef session, AlarmDataCmd cmd) {
  246 + TbAlarmDataSubCtx ctx = getSubCtx(session.getSessionId(), cmd.getCmdId());
  247 + if (ctx == null) {
  248 + log.debug("[{}][{}] Creating new alarm subscription using: {}", session.getSessionId(), cmd.getCmdId(), cmd);
  249 + ctx = createSubCtx(session, cmd);
  250 + }
  251 + AlarmDataQuery adq = cmd.getQuery();
  252 + EntityDataSortOrder sortOrder = adq.getPageLink().getSortOrder();
  253 + EntityDataSortOrder entitiesSortOrder;
  254 + if (sortOrder == null || sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) {
  255 + entitiesSortOrder = new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, ModelConstants.CREATED_TIME_PROPERTY));
  256 + } else {
  257 + entitiesSortOrder = sortOrder;
  258 + }
  259 + EntityDataPageLink edpl = new EntityDataPageLink(0, maxEntitiesPerAlarmSubscription, null, entitiesSortOrder);
  260 + EntityDataQuery edq = new EntityDataQuery(adq.getEntityFilter(), edpl, adq.getEntityFields(), adq.getLatestValues(), adq.getKeyFilters());
  261 + PageData<EntityData> entitiesData = entityService.findEntityDataByQuery(ctx.getTenantId(), ctx.getCustomerId(), edq);
  262 + List<EntityData> entities = entitiesData.getData();
  263 + ctx.setEntitiesData(entitiesData);
  264 + if (entities.isEmpty()) {
  265 + AlarmDataUpdate update = new AlarmDataUpdate(cmd.getCmdId(), new PageData<>(Collections.emptyList(), 1, 0, false), null);
  266 + wsService.sendWsMsg(ctx.getSessionId(), update);
  267 + } else {
  268 + PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(ctx.getTenantId(), ctx.getCustomerId(),
  269 + ctx.getQuery().getPageLink(), ctx.getOrderedEntityIds());
  270 + ctx.setAlarmsData(alarms);
  271 + AlarmDataUpdate update = new AlarmDataUpdate(cmd.getCmdId(), alarms, null);
  272 + wsService.sendWsMsg(ctx.getSessionId(), update);
  273 + //TODO: Create WS subscription for alarms for this entities. If this is first page(?!) and new alarm matches the filter - invalidate alarms.
  274 + }
  275 + }
  276 +
233 private void refreshDynamicQuery(TenantId tenantId, CustomerId customerId, TbEntityDataSubCtx finalCtx) { 277 private void refreshDynamicQuery(TenantId tenantId, CustomerId customerId, TbEntityDataSubCtx finalCtx) {
234 try { 278 try {
235 long start = System.currentTimeMillis(); 279 long start = System.currentTimeMillis();
@@ -244,7 +288,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -244,7 +288,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
244 } 288 }
245 } 289 }
246 290
247 - @Scheduled(fixedDelayString = "${server.ws.dynamic_page_link_stats:10000}") 291 + @Scheduled(fixedDelayString = "${server.ws.dynamic_page_link.stats:10000}")
248 public void printStats() { 292 public void printStats() {
249 int regularQueryInvocationCntValue = regularQueryInvocationCnt.getAndSet(0); 293 int regularQueryInvocationCntValue = regularQueryInvocationCnt.getAndSet(0);
250 long regularQueryInvocationTimeValue = regularQueryTimeSpent.getAndSet(0); 294 long regularQueryInvocationTimeValue = regularQueryTimeSpent.getAndSet(0);
@@ -263,17 +307,25 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -263,17 +307,25 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
263 } 307 }
264 308
265 private TbEntityDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) { 309 private TbEntityDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, EntityDataCmd cmd) {
266 - Map<Integer, TbEntityDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>()); 310 + Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
267 TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, sessionRef, cmd.getCmdId()); 311 TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, sessionRef, cmd.getCmdId());
268 ctx.setQuery(cmd.getQuery()); 312 ctx.setQuery(cmd.getQuery());
269 sessionSubs.put(cmd.getCmdId(), ctx); 313 sessionSubs.put(cmd.getCmdId(), ctx);
270 return ctx; 314 return ctx;
271 } 315 }
272 316
273 - private TbEntityDataSubCtx getSubCtx(String sessionId, int cmdId) {  
274 - Map<Integer, TbEntityDataSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId); 317 + private TbAlarmDataSubCtx createSubCtx(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) {
  318 + Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
  319 + TbAlarmDataSubCtx ctx = new TbAlarmDataSubCtx(serviceId, wsService, sessionRef, cmd.getCmdId());
  320 + ctx.setQuery(cmd.getQuery());
  321 + sessionSubs.put(cmd.getCmdId(), ctx);
  322 + return ctx;
  323 + }
  324 +
  325 + private <T extends TbAbstractDataSubCtx> T getSubCtx(String sessionId, int cmdId) {
  326 + Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.get(sessionId);
275 if (sessionSubs != null) { 327 if (sessionSubs != null) {
276 - return sessionSubs.get(cmdId); 328 + return (T) sessionSubs.get(cmdId);
277 } else { 329 } else {
278 return null; 330 return null;
279 } 331 }
@@ -420,14 +472,6 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -420,14 +472,6 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
420 return data.stream().collect(Collectors.toMap(TsKvEntry::getKey, value -> new TsValue(value.getTs(), value.getValueAsString()))); 472 return data.stream().collect(Collectors.toMap(TsKvEntry::getKey, value -> new TsValue(value.getTs(), value.getValueAsString())));
421 } 473 }
422 474
423 - private Map<String, List<TsValue>> toTsValues(List<TsKvEntry> data) {  
424 - Map<String, List<TsValue>> results = new HashMap<>();  
425 - for (TsKvEntry tsKvEntry : data) {  
426 - results.computeIfAbsent(tsKvEntry.getKey(), k -> new ArrayList<>()).add(new TsValue(tsKvEntry.getTs(), tsKvEntry.getValueAsString()));  
427 - }  
428 - return results;  
429 - }  
430 -  
431 @Override 475 @Override
432 public void cancelSubscription(String sessionId, EntityDataUnsubscribeCmd cmd) { 476 public void cancelSubscription(String sessionId, EntityDataUnsubscribeCmd cmd) {
433 cleanupAndCancel(getSubCtx(sessionId, cmd.getCmdId())); 477 cleanupAndCancel(getSubCtx(sessionId, cmd.getCmdId()));
@@ -442,9 +486,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc @@ -442,9 +486,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
442 486
443 @Override 487 @Override
444 public void cancelAllSessionSubscriptions(String sessionId) { 488 public void cancelAllSessionSubscriptions(String sessionId) {
445 - Map<Integer, TbEntityDataSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId); 489 + Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId);
446 if (sessionSubs != null) { 490 if (sessionSubs != null) {
447 - sessionSubs.values().forEach(this::cleanupAndCancel); 491 + sessionSubs.values().stream().filter(sub -> sub instanceof TbEntityDataSubCtx).map(sub -> (TbEntityDataSubCtx) sub).forEach(this::cleanupAndCancel);
448 } 492 }
449 } 493 }
450 494
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.subscription;
  17 +
  18 +import lombok.Data;
  19 +import lombok.Getter;
  20 +import lombok.Setter;
  21 +import lombok.extern.slf4j.Slf4j;
  22 +import org.thingsboard.server.common.data.id.CustomerId;
  23 +import org.thingsboard.server.common.data.id.TenantId;
  24 +import org.thingsboard.server.common.data.query.AbstractDataQuery;
  25 +import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
  26 +import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
  27 +
  28 +@Slf4j
  29 +@Data
  30 +public abstract class TbAbstractDataSubCtx<T extends AbstractDataQuery> {
  31 +
  32 + protected final String serviceId;
  33 + protected final TelemetryWebSocketService wsService;
  34 + protected final TelemetryWebSocketSessionRef sessionRef;
  35 + protected final int cmdId;
  36 + @Getter
  37 + @Setter
  38 + protected T query;
  39 +
  40 + public TbAbstractDataSubCtx(String serviceId, TelemetryWebSocketService wsService, TelemetryWebSocketSessionRef sessionRef, int cmdId) {
  41 + this.serviceId = serviceId;
  42 + this.wsService = wsService;
  43 + this.sessionRef = sessionRef;
  44 + this.cmdId = cmdId;
  45 + }
  46 +
  47 + public String getSessionId() {
  48 + return sessionRef.getSessionId();
  49 + }
  50 +
  51 + public TenantId getTenantId() {
  52 + return sessionRef.getSecurityCtx().getTenantId();
  53 + }
  54 +
  55 + public CustomerId getCustomerId() {
  56 + return sessionRef.getSecurityCtx().getCustomerId();
  57 + }
  58 +
  59 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.subscription;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.Setter;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.thingsboard.server.common.data.id.EntityId;
  22 +import org.thingsboard.server.common.data.page.PageData;
  23 +import org.thingsboard.server.common.data.query.AlarmData;
  24 +import org.thingsboard.server.common.data.query.AlarmDataQuery;
  25 +import org.thingsboard.server.common.data.query.EntityData;
  26 +import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
  27 +import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
  28 +
  29 +import java.util.Collection;
  30 +import java.util.LinkedHashMap;
  31 +import java.util.List;
  32 +
  33 +@Slf4j
  34 +public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
  35 +
  36 + @Getter
  37 + @Setter
  38 + private final LinkedHashMap<EntityId, EntityData> entitiesMap;
  39 + @Getter
  40 + @Setter
  41 + private boolean tooManyEntities;
  42 +
  43 + public TbAlarmDataSubCtx(String serviceId, TelemetryWebSocketService wsService, TelemetryWebSocketSessionRef sessionRef, int cmdId) {
  44 + super(serviceId, wsService, sessionRef, cmdId);
  45 + this.entitiesMap = new LinkedHashMap<>();
  46 + }
  47 +
  48 + public void setEntitiesData(PageData<EntityData> entitiesData) {
  49 + entitiesMap.clear();
  50 + tooManyEntities = entitiesData.hasNext();
  51 + for (EntityData entityData : entitiesData.getData()) {
  52 + entitiesMap.put(entityData.getEntityId(), entityData);
  53 + }
  54 + }
  55 +
  56 + public Collection<EntityId> getOrderedEntityIds() {
  57 + return entitiesMap.keySet();
  58 + }
  59 +
  60 + public void setAlarmsData(PageData<AlarmData> alarms) {
  61 +// TODO: implement
  62 + }
  63 +}
@@ -17,6 +17,9 @@ package org.thingsboard.server.service.subscription; @@ -17,6 +17,9 @@ package org.thingsboard.server.service.subscription;
17 17
18 import lombok.AllArgsConstructor; 18 import lombok.AllArgsConstructor;
19 import lombok.Data; 19 import lombok.Data;
  20 +import lombok.Getter;
  21 +import lombok.RequiredArgsConstructor;
  22 +import lombok.Setter;
20 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
21 import org.thingsboard.server.common.data.id.CustomerId; 24 import org.thingsboard.server.common.data.id.CustomerId;
22 import org.thingsboard.server.common.data.id.EntityId; 25 import org.thingsboard.server.common.data.id.EntityId;
@@ -51,17 +54,13 @@ import java.util.function.Function; @@ -51,17 +54,13 @@ import java.util.function.Function;
51 import java.util.stream.Collectors; 54 import java.util.stream.Collectors;
52 55
53 @Slf4j 56 @Slf4j
54 -@Data  
55 -public class TbEntityDataSubCtx { 57 +public class TbEntityDataSubCtx extends TbAbstractDataSubCtx<EntityDataQuery> {
56 58
57 - public static final int MAX_SUBS_PER_CMD = 1024 * 8;  
58 - private final String serviceId;  
59 - private final TelemetryWebSocketService wsService;  
60 - private final TelemetryWebSocketSessionRef sessionRef;  
61 - private final int cmdId;  
62 - private EntityDataQuery query; 59 + @Getter @Setter
63 private TimeSeriesCmd tsCmd; 60 private TimeSeriesCmd tsCmd;
  61 + @Getter
64 private PageData<EntityData> data; 62 private PageData<EntityData> data;
  63 + @Getter @Setter
65 private boolean initialDataSent; 64 private boolean initialDataSent;
66 private Map<Integer, EntityId> subToEntityIdMap; 65 private Map<Integer, EntityId> subToEntityIdMap;
67 private volatile ScheduledFuture<?> refreshTask; 66 private volatile ScheduledFuture<?> refreshTask;
@@ -69,22 +68,7 @@ public class TbEntityDataSubCtx { @@ -69,22 +68,7 @@ public class TbEntityDataSubCtx {
69 private LatestValueCmd latestValueCmd; 68 private LatestValueCmd latestValueCmd;
70 69
71 public TbEntityDataSubCtx(String serviceId, TelemetryWebSocketService wsService, TelemetryWebSocketSessionRef sessionRef, int cmdId) { 70 public TbEntityDataSubCtx(String serviceId, TelemetryWebSocketService wsService, TelemetryWebSocketSessionRef sessionRef, int cmdId) {
72 - this.serviceId = serviceId;  
73 - this.wsService = wsService;  
74 - this.sessionRef = sessionRef;  
75 - this.cmdId = cmdId;  
76 - }  
77 -  
78 - public String getSessionId() {  
79 - return sessionRef.getSessionId();  
80 - }  
81 -  
82 - public TenantId getTenantId() {  
83 - return sessionRef.getSecurityCtx().getTenantId();  
84 - }  
85 -  
86 - public CustomerId getCustomerId() {  
87 - return sessionRef.getSecurityCtx().getCustomerId(); 71 + super(serviceId, wsService, sessionRef, cmdId);
88 } 72 }
89 73
90 public void setData(PageData<EntityData> data) { 74 public void setData(PageData<EntityData> data) {
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.service.subscription; 16 package org.thingsboard.server.service.subscription;
17 17
18 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef; 18 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
  19 +import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd;
19 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; 20 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
20 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; 21 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
21 22
@@ -23,6 +24,8 @@ public interface TbEntityDataSubscriptionService { @@ -23,6 +24,8 @@ public interface TbEntityDataSubscriptionService {
23 24
24 void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityDataCmd cmd); 25 void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityDataCmd cmd);
25 26
  27 + void handleCmd(TelemetryWebSocketSessionRef sessionId, AlarmDataCmd cmd);
  28 +
26 void cancelSubscription(String sessionId, EntityDataUnsubscribeCmd subscriptionId); 29 void cancelSubscription(String sessionId, EntityDataUnsubscribeCmd subscriptionId);
27 30
28 void cancelAllSessionSubscriptions(String sessionId); 31 void cancelAllSessionSubscriptions(String sessionId);
@@ -63,6 +63,9 @@ import org.thingsboard.server.service.telemetry.cmd.v1.SubscriptionCmd; @@ -63,6 +63,9 @@ import org.thingsboard.server.service.telemetry.cmd.v1.SubscriptionCmd;
63 import org.thingsboard.server.service.telemetry.cmd.v1.TelemetryPluginCmd; 63 import org.thingsboard.server.service.telemetry.cmd.v1.TelemetryPluginCmd;
64 import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper; 64 import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
65 import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; 65 import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd;
  66 +import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd;
  67 +import org.thingsboard.server.service.telemetry.cmd.v2.DataCmd;
  68 +import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate;
66 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; 69 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
67 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; 70 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
68 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; 71 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
@@ -210,6 +213,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -210,6 +213,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
210 if (cmdsWrapper.getEntityDataCmds() != null) { 213 if (cmdsWrapper.getEntityDataCmds() != null) {
211 cmdsWrapper.getEntityDataCmds().forEach(cmd -> handleWsEntityDataCmd(sessionRef, cmd)); 214 cmdsWrapper.getEntityDataCmds().forEach(cmd -> handleWsEntityDataCmd(sessionRef, cmd));
212 } 215 }
  216 + if (cmdsWrapper.getAlarmDataCmds() != null) {
  217 + cmdsWrapper.getAlarmDataCmds().forEach(cmd -> handleWsAlarmDataCmd(sessionRef, cmd));
  218 + }
213 if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) { 219 if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) {
214 cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsEntityDataUnsubscribeCmd(sessionRef, cmd)); 220 cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsEntityDataUnsubscribeCmd(sessionRef, cmd));
215 } 221 }
@@ -231,6 +237,16 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -231,6 +237,16 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
231 } 237 }
232 } 238 }
233 239
  240 + private void handleWsAlarmDataCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) {
  241 + String sessionId = sessionRef.getSessionId();
  242 + log.debug("[{}] Processing: {}", sessionId, cmd);
  243 +
  244 + if (validateSessionMetadata(sessionRef, cmd.getCmdId(), sessionId)
  245 + && validateSubscriptionCmd(sessionRef, cmd)) {
  246 + entityDataSubService.handleCmd(sessionRef, cmd);
  247 + }
  248 + }
  249 +
234 private void handleWsEntityDataUnsubscribeCmd(TelemetryWebSocketSessionRef sessionRef, EntityDataUnsubscribeCmd cmd) { 250 private void handleWsEntityDataUnsubscribeCmd(TelemetryWebSocketSessionRef sessionRef, EntityDataUnsubscribeCmd cmd) {
235 String sessionId = sessionRef.getSessionId(); 251 String sessionId = sessionRef.getSessionId();
236 log.debug("[{}] Processing: {}", sessionId, cmd); 252 log.debug("[{}] Processing: {}", sessionId, cmd);
@@ -246,7 +262,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -246,7 +262,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
246 } 262 }
247 263
248 @Override 264 @Override
249 - public void sendWsMsg(String sessionId, EntityDataUpdate update) { 265 + public void sendWsMsg(String sessionId, DataUpdate update) {
250 sendWsMsg(sessionId, update.getCmdId(), update); 266 sendWsMsg(sessionId, update.getCmdId(), update);
251 } 267 }
252 268
@@ -661,6 +677,21 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi @@ -661,6 +677,21 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
661 return true; 677 return true;
662 } 678 }
663 679
  680 + private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, AlarmDataCmd cmd) {
  681 + if (cmd.getCmdId() < 0) {
  682 + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
  683 + "Cmd id is negative value!");
  684 + sendWsMsg(sessionRef, update);
  685 + return false;
  686 + } else if (cmd.getQuery() == null) {
  687 + SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
  688 + "Query is empty!");
  689 + sendWsMsg(sessionRef, update);
  690 + return false;
  691 + }
  692 + return true;
  693 + }
  694 +
664 private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) { 695 private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) {
665 if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) { 696 if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) {
666 SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST, 697 SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.service.telemetry; 16 package org.thingsboard.server.service.telemetry;
17 17
  18 +import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate;
18 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate; 19 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
19 import org.thingsboard.server.service.telemetry.sub.SubscriptionUpdate; 20 import org.thingsboard.server.service.telemetry.sub.SubscriptionUpdate;
20 21
@@ -29,6 +30,6 @@ public interface TelemetryWebSocketService { @@ -29,6 +30,6 @@ public interface TelemetryWebSocketService {
29 30
30 void sendWsMsg(String sessionId, SubscriptionUpdate update); 31 void sendWsMsg(String sessionId, SubscriptionUpdate update);
31 32
32 - void sendWsMsg(String sessionId, EntityDataUpdate update); 33 + void sendWsMsg(String sessionId, DataUpdate update);
33 34
34 } 35 }
@@ -19,6 +19,8 @@ import lombok.Data; @@ -19,6 +19,8 @@ import lombok.Data;
19 import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd; 19 import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd;
20 import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd; 20 import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd;
21 import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd; 21 import org.thingsboard.server.service.telemetry.cmd.v1.TimeseriesSubscriptionCmd;
  22 +import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd;
  23 +import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataUnsubscribeCmd;
22 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd; 24 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
23 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd; 25 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
24 26
@@ -40,4 +42,8 @@ public class TelemetryPluginCmdsWrapper { @@ -40,4 +42,8 @@ public class TelemetryPluginCmdsWrapper {
40 42
41 private List<EntityDataUnsubscribeCmd> entityDataUnsubscribeCmds; 43 private List<EntityDataUnsubscribeCmd> entityDataUnsubscribeCmds;
42 44
  45 + private List<AlarmDataCmd> alarmDataCmds;
  46 +
  47 + private List<AlarmDataUnsubscribeCmd> alarmDataUnsubscribeCmds;
  48 +
43 } 49 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.telemetry.cmd.v2;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonProperty;
  20 +import lombok.Getter;
  21 +import org.thingsboard.server.common.data.query.AlarmDataQuery;
  22 +
  23 +public class AlarmDataCmd extends DataCmd {
  24 +
  25 + @Getter
  26 + private final AlarmDataQuery query;
  27 +
  28 + @JsonCreator
  29 + public AlarmDataCmd(@JsonProperty("cmdId") int cmdId, @JsonProperty("query") AlarmDataQuery query) {
  30 + super(cmdId);
  31 + this.query = query;
  32 + }
  33 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.telemetry.cmd.v2;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class AlarmDataUnsubscribeCmd {
  22 +
  23 + private final int cmdId;
  24 +
  25 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.telemetry.cmd.v2;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonProperty;
  20 +import lombok.NoArgsConstructor;
  21 +import org.thingsboard.server.common.data.page.PageData;
  22 +import org.thingsboard.server.common.data.query.AlarmData;
  23 +import org.thingsboard.server.common.data.query.EntityData;
  24 +import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode;
  25 +
  26 +import java.util.List;
  27 +
  28 +public class AlarmDataUpdate extends DataUpdate<AlarmData> {
  29 +
  30 + public AlarmDataUpdate(int cmdId, PageData<AlarmData> data, List<AlarmData> update) {
  31 + super(cmdId, data, update, SubscriptionErrorCode.NO_ERROR.getCode(), null);
  32 + }
  33 +
  34 + public AlarmDataUpdate(int cmdId, int errorCode, String errorMsg) {
  35 + super(cmdId, null, null, errorCode, errorMsg);
  36 + }
  37 +
  38 + @JsonCreator
  39 + public AlarmDataUpdate(@JsonProperty("cmdId") int cmdId,
  40 + @JsonProperty("data") PageData<AlarmData> data,
  41 + @JsonProperty("update") List<AlarmData> update,
  42 + @JsonProperty("errorCode") int errorCode,
  43 + @JsonProperty("errorMsg") String errorMsg) {
  44 + super(cmdId, data, update, errorCode, errorMsg);
  45 + }
  46 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.telemetry.cmd.v2;
  17 +
  18 +import lombok.Data;
  19 +import lombok.Getter;
  20 +import lombok.NoArgsConstructor;
  21 +
  22 +@Data
  23 +public class DataCmd {
  24 +
  25 + @Getter
  26 + private final int cmdId;
  27 +
  28 + public DataCmd(int cmdId) {
  29 + this.cmdId = cmdId;
  30 + }
  31 +
  32 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.telemetry.cmd.v2;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.common.data.page.PageData;
  21 +import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode;
  22 +
  23 +import java.util.List;
  24 +
  25 +@Data
  26 +@AllArgsConstructor
  27 +public abstract class DataUpdate<T> {
  28 +
  29 + private final int cmdId;
  30 + private final PageData<T> data;
  31 + private final List<T> update;
  32 + private final int errorCode;
  33 + private final String errorMsg;
  34 +
  35 + public DataUpdate(int cmdId, PageData<T> data, List<T> update) {
  36 + this(cmdId, data, update, SubscriptionErrorCode.NO_ERROR.getCode(), null);
  37 + }
  38 +
  39 + public DataUpdate(int cmdId, int errorCode, String errorMsg) {
  40 + this(cmdId, null, null, errorCode, errorMsg);
  41 + }
  42 +
  43 +}
@@ -15,16 +15,32 @@ @@ -15,16 +15,32 @@
15 */ 15 */
16 package org.thingsboard.server.service.telemetry.cmd.v2; 16 package org.thingsboard.server.service.telemetry.cmd.v2;
17 17
18 -import lombok.Data; 18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonProperty;
  20 +import lombok.Getter;
19 import org.thingsboard.server.common.data.query.EntityDataQuery; 21 import org.thingsboard.server.common.data.query.EntityDataQuery;
20 22
21 -@Data  
22 -public class EntityDataCmd { 23 +public class EntityDataCmd extends DataCmd {
23 24
24 - private final int cmdId; 25 + @Getter
25 private final EntityDataQuery query; 26 private final EntityDataQuery query;
  27 + @Getter
26 private final EntityHistoryCmd historyCmd; 28 private final EntityHistoryCmd historyCmd;
  29 + @Getter
27 private final LatestValueCmd latestCmd; 30 private final LatestValueCmd latestCmd;
  31 + @Getter
28 private final TimeSeriesCmd tsCmd; 32 private final TimeSeriesCmd tsCmd;
29 33
  34 + @JsonCreator
  35 + public EntityDataCmd(@JsonProperty("cmdId") int cmdId,
  36 + @JsonProperty("query") EntityDataQuery query,
  37 + @JsonProperty("historyCmd") EntityHistoryCmd historyCmd,
  38 + @JsonProperty("latestCmd") LatestValueCmd latestCmd,
  39 + @JsonProperty("tsCmd") TimeSeriesCmd tsCmd) {
  40 + super(cmdId);
  41 + this.query = query;
  42 + this.historyCmd = historyCmd;
  43 + this.latestCmd = latestCmd;
  44 + this.tsCmd = tsCmd;
  45 + }
30 } 46 }
@@ -15,30 +15,31 @@ @@ -15,30 +15,31 @@
15 */ 15 */
16 package org.thingsboard.server.service.telemetry.cmd.v2; 16 package org.thingsboard.server.service.telemetry.cmd.v2;
17 17
18 -import lombok.AllArgsConstructor;  
19 -import lombok.Data; 18 +import com.fasterxml.jackson.annotation.JsonCreator;
  19 +import com.fasterxml.jackson.annotation.JsonProperty;
20 import org.thingsboard.server.common.data.page.PageData; 20 import org.thingsboard.server.common.data.page.PageData;
21 import org.thingsboard.server.common.data.query.EntityData; 21 import org.thingsboard.server.common.data.query.EntityData;
22 import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode; 22 import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode;
23 23
24 import java.util.List; 24 import java.util.List;
25 25
26 -@Data  
27 -@AllArgsConstructor  
28 -public class EntityDataUpdate {  
29 -  
30 - private final int cmdId;  
31 - private final PageData<EntityData> data;  
32 - private final List<EntityData> update;  
33 - private final int errorCode;  
34 - private final String errorMsg; 26 +public class EntityDataUpdate extends DataUpdate<EntityData> {
35 27
36 public EntityDataUpdate(int cmdId, PageData<EntityData> data, List<EntityData> update) { 28 public EntityDataUpdate(int cmdId, PageData<EntityData> data, List<EntityData> update) {
37 - this(cmdId, data, update, SubscriptionErrorCode.NO_ERROR.getCode(), null); 29 + super(cmdId, data, update, SubscriptionErrorCode.NO_ERROR.getCode(), null);
38 } 30 }
39 31
40 public EntityDataUpdate(int cmdId, int errorCode, String errorMsg) { 32 public EntityDataUpdate(int cmdId, int errorCode, String errorMsg) {
41 - this(cmdId, null, null, errorCode, errorMsg); 33 + super(cmdId, null, null, errorCode, errorMsg);
  34 + }
  35 +
  36 + @JsonCreator
  37 + public EntityDataUpdate(@JsonProperty("cmdId") int cmdId,
  38 + @JsonProperty("data") PageData<EntityData> data,
  39 + @JsonProperty("update") List<EntityData> update,
  40 + @JsonProperty("errorCode") int errorCode,
  41 + @JsonProperty("errorMsg") String errorMsg) {
  42 + super(cmdId, data, update, errorCode, errorMsg);
42 } 43 }
43 44
44 } 45 }
@@ -46,8 +46,11 @@ server: @@ -46,8 +46,11 @@ server:
46 max_subscriptions_per_regular_user: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SUBSCRIPTIONS_PER_REGULAR_USER:0}" 46 max_subscriptions_per_regular_user: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SUBSCRIPTIONS_PER_REGULAR_USER:0}"
47 max_subscriptions_per_public_user: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SUBSCRIPTIONS_PER_PUBLIC_USER:0}" 47 max_subscriptions_per_public_user: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SUBSCRIPTIONS_PER_PUBLIC_USER:0}"
48 max_updates_per_session: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_UPDATES_PER_SESSION:300:1,3000:60}" 48 max_updates_per_session: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_UPDATES_PER_SESSION:300:1,3000:60}"
49 - dynamic_page_link_refresh_interval: "${TB_SERVER_WS_DYNAMIC_PAGE_LINK_REFRESH_INTERVAL_SEC:6}"  
50 - dynamic_page_link_refresh_pool_size: "${TB_SERVER_WS_DYNAMIC_PAGE_LINK_REFRESH_POOL_SIZE:1}" 49 + dynamic_page_link:
  50 + refresh_interval: "${TB_SERVER_WS_DYNAMIC_PAGE_LINK_REFRESH_INTERVAL_SEC:60}"
  51 + refresh_pool_size: "${TB_SERVER_WS_DYNAMIC_PAGE_LINK_REFRESH_POOL_SIZE:1}"
  52 + max_per_user: "${TB_SERVER_WS_DYNAMIC_PAGE_LINK_MAX_PER_USER:10}"
  53 + max_entities_per_alarm_subscription: "${TB_SERVER_WS_MAX_ENTITIES_PER_ALARM_SUBSCRIPTION:1000}"
51 rest: 54 rest:
52 limits: 55 limits:
53 tenant: 56 tenant:
@@ -26,9 +26,9 @@ import java.util.Arrays; @@ -26,9 +26,9 @@ import java.util.Arrays;
26 26
27 @RunWith(ClasspathSuite.class) 27 @RunWith(ClasspathSuite.class)
28 @ClasspathSuite.ClassnameFilters({ 28 @ClasspathSuite.ClassnameFilters({
29 -// "org.thingsboard.server.controller.sql.WebsocketApiSqlTest", 29 + "org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
30 // "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest", 30 // "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest",
31 - "org.thingsboard.server.controller.sql.*Test", 31 +// "org.thingsboard.server.controller.sql.*Test",
32 }) 32 })
33 public class ControllerSqlTestSuite { 33 public class ControllerSqlTestSuite {
34 34
@@ -24,9 +24,15 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery; @@ -24,9 +24,15 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery;
24 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; 24 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
25 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 25 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
26 import org.thingsboard.server.common.data.alarm.AlarmStatus; 26 import org.thingsboard.server.common.data.alarm.AlarmStatus;
  27 +import org.thingsboard.server.common.data.id.CustomerId;
27 import org.thingsboard.server.common.data.id.EntityId; 28 import org.thingsboard.server.common.data.id.EntityId;
28 import org.thingsboard.server.common.data.id.TenantId; 29 import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.common.data.page.PageData; 30 import org.thingsboard.server.common.data.page.PageData;
  31 +import org.thingsboard.server.common.data.query.AlarmData;
  32 +import org.thingsboard.server.common.data.query.AlarmDataPageLink;
  33 +import org.thingsboard.server.common.data.query.AlarmDataQuery;
  34 +
  35 +import java.util.Collection;
30 36
31 /** 37 /**
32 * Created by ashvayka on 11.05.17. 38 * Created by ashvayka on 11.05.17.
@@ -52,4 +58,6 @@ public interface AlarmService { @@ -52,4 +58,6 @@ public interface AlarmService {
52 58
53 ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); 59 ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);
54 60
  61 + PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
  62 + AlarmDataPageLink pageLink, Collection<EntityId> orderedEntityIds);
55 } 63 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.query;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.Getter;
  20 +import lombok.ToString;
  21 +
  22 +import java.util.List;
  23 +
  24 +@ToString
  25 +public abstract class AbstractDataQuery<T extends EntityDataPageLink> extends EntityCountQuery {
  26 +
  27 + @Getter
  28 + protected T pageLink;
  29 + @Getter
  30 + protected List<EntityKey> entityFields;
  31 + @Getter
  32 + protected List<EntityKey> latestValues;
  33 + @Getter
  34 + protected List<KeyFilter> keyFilters;
  35 +
  36 + public AbstractDataQuery() {
  37 + super();
  38 + }
  39 +
  40 + public AbstractDataQuery(EntityFilter entityFilter) {
  41 + super(entityFilter);
  42 + }
  43 +
  44 + public AbstractDataQuery(EntityFilter entityFilter,
  45 + T pageLink,
  46 + List<EntityKey> entityFields,
  47 + List<EntityKey> latestValues,
  48 + List<KeyFilter> keyFilters) {
  49 + super(entityFilter);
  50 + this.pageLink = pageLink;
  51 + this.entityFields = entityFields;
  52 + this.latestValues = latestValues;
  53 + this.keyFilters = keyFilters;
  54 + }
  55 +
  56 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.query;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.alarm.Alarm;
  20 +import org.thingsboard.server.common.data.alarm.AlarmInfo;
  21 +import org.thingsboard.server.common.data.id.EntityId;
  22 +
  23 +import java.util.HashMap;
  24 +import java.util.Map;
  25 +import java.util.UUID;
  26 +
  27 +public class AlarmData extends AlarmInfo {
  28 +
  29 + private final UUID entityId;
  30 + private final Map<EntityKeyType, Map<String, TsValue>> latest;
  31 +
  32 + public AlarmData(Alarm alarm, String originatorName, UUID entityId) {
  33 + super(alarm, originatorName);
  34 + this.entityId = entityId;
  35 + this.latest = new HashMap<>();
  36 + }
  37 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.query;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.AllArgsConstructor;
  20 +import lombok.Data;
  21 +import lombok.Getter;
  22 +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
  23 +import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  24 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  25 +
  26 +import java.util.List;
  27 +
  28 +@Data
  29 +@AllArgsConstructor
  30 +public class AlarmDataPageLink extends EntityDataPageLink {
  31 +
  32 + private long startTs;
  33 + private long endTs;
  34 + //TODO: handle this;
  35 + private long timeWindow;
  36 + private List<String> typeList;
  37 + private List<AlarmSearchStatus> statusList;
  38 + private List<AlarmSeverity> severityList;
  39 + private boolean searchPropagatedAlarms;
  40 +
  41 + public AlarmDataPageLink() {
  42 + super();
  43 + }
  44 +
  45 + public AlarmDataPageLink(int pageSize, int page, String textSearch, EntityDataSortOrder sortOrder, boolean dynamic,
  46 + boolean searchPropagatedAlarms,
  47 + long startTs, long endTs, long timeWindow,
  48 + List<String> typeList, List<AlarmSearchStatus> statusList, List<AlarmSeverity> severityList) {
  49 + super(pageSize, page, textSearch, sortOrder, dynamic);
  50 + this.searchPropagatedAlarms = searchPropagatedAlarms;
  51 + this.startTs = startTs;
  52 + this.endTs = endTs;
  53 + this.timeWindow = timeWindow;
  54 + this.typeList = typeList;
  55 + this.statusList = statusList;
  56 + this.severityList = severityList;
  57 + }
  58 +
  59 + @JsonIgnore
  60 + public AlarmDataPageLink nextPageLink() {
  61 + return new AlarmDataPageLink(this.getPageSize(), this.getPage() + 1, this.getTextSearch(), this.getSortOrder(), this.isDynamic(),
  62 + this.searchPropagatedAlarms,
  63 + this.startTs, this.endTs, this.timeWindow,
  64 + this.typeList, this.statusList, this.severityList
  65 + );
  66 + }
  67 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.query;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import lombok.Getter;
  20 +import lombok.ToString;
  21 +
  22 +import java.util.List;
  23 +
  24 +@ToString
  25 +public class AlarmDataQuery extends AbstractDataQuery<AlarmDataPageLink> {
  26 +
  27 + public AlarmDataQuery() {
  28 + }
  29 +
  30 + public AlarmDataQuery(EntityFilter entityFilter) {
  31 + super(entityFilter);
  32 + }
  33 +
  34 + public AlarmDataQuery(EntityFilter entityFilter, AlarmDataPageLink pageLink, List<EntityKey> entityFields, List<EntityKey> latestValues, List<KeyFilter> keyFilters) {
  35 + super(entityFilter, pageLink, entityFields, latestValues, keyFilters);
  36 + }
  37 +
  38 + @JsonIgnore
  39 + public AlarmDataQuery next() {
  40 + return new AlarmDataQuery(getEntityFilter(), getPageLink().nextPageLink(), entityFields, latestValues, keyFilters);
  41 + }
  42 +}
@@ -38,6 +38,6 @@ public class EntityDataPageLink { @@ -38,6 +38,6 @@ public class EntityDataPageLink {
38 38
39 @JsonIgnore 39 @JsonIgnore
40 public EntityDataPageLink nextPageLink() { 40 public EntityDataPageLink nextPageLink() {
41 - return new EntityDataPageLink(this.pageSize, this.page+1, this.textSearch, this.sortOrder); 41 + return new EntityDataPageLink(this.pageSize, this.page + 1, this.textSearch, this.sortOrder);
42 } 42 }
43 } 43 }
@@ -22,39 +22,22 @@ import lombok.ToString; @@ -22,39 +22,22 @@ import lombok.ToString;
22 import java.util.List; 22 import java.util.List;
23 23
24 @ToString 24 @ToString
25 -public class EntityDataQuery extends EntityCountQuery {  
26 -  
27 - @Getter  
28 - private EntityDataPageLink pageLink;  
29 - @Getter  
30 - private List<EntityKey> entityFields;  
31 - @Getter  
32 - private List<EntityKey> latestValues;  
33 - @Getter  
34 - private List<KeyFilter> keyFilters; 25 +public class EntityDataQuery extends AbstractDataQuery<EntityDataPageLink> {
35 26
36 public EntityDataQuery() { 27 public EntityDataQuery() {
37 - super();  
38 } 28 }
39 29
40 public EntityDataQuery(EntityFilter entityFilter) { 30 public EntityDataQuery(EntityFilter entityFilter) {
41 super(entityFilter); 31 super(entityFilter);
42 } 32 }
43 33
44 - public EntityDataQuery(EntityFilter entityFilter,  
45 - EntityDataPageLink pageLink,  
46 - List<EntityKey> entityFields,  
47 - List<EntityKey> latestValues,  
48 - List<KeyFilter> keyFilters) {  
49 - super(entityFilter);  
50 - this.pageLink = pageLink;  
51 - this.entityFields = entityFields;  
52 - this.latestValues = latestValues;  
53 - this.keyFilters = keyFilters; 34 + public EntityDataQuery(EntityFilter entityFilter, EntityDataPageLink pageLink, List<EntityKey> entityFields, List<EntityKey> latestValues, List<KeyFilter> keyFilters) {
  35 + super(entityFilter, pageLink, entityFields, latestValues, keyFilters);
54 } 36 }
55 37
56 @JsonIgnore 38 @JsonIgnore
57 public EntityDataQuery next() { 39 public EntityDataQuery next() {
58 - return new EntityDataQuery(getEntityFilter(), pageLink.nextPageLink(), entityFields, latestValues, keyFilters); 40 + return new EntityDataQuery(getEntityFilter(), getPageLink().nextPageLink(), entityFields, latestValues, keyFilters);
59 } 41 }
  42 +
60 } 43 }
@@ -21,5 +21,6 @@ public enum EntityKeyType { @@ -21,5 +21,6 @@ public enum EntityKeyType {
21 SHARED_ATTRIBUTE, 21 SHARED_ATTRIBUTE,
22 SERVER_ATTRIBUTE, 22 SERVER_ATTRIBUTE,
23 TIME_SERIES, 23 TIME_SERIES,
24 - ENTITY_FIELD; 24 + ENTITY_FIELD,
  25 + ALARM_FIELD;
25 } 26 }
@@ -19,11 +19,16 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -19,11 +19,16 @@ import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.server.common.data.alarm.Alarm; 19 import org.thingsboard.server.common.data.alarm.Alarm;
20 import org.thingsboard.server.common.data.alarm.AlarmInfo; 20 import org.thingsboard.server.common.data.alarm.AlarmInfo;
21 import org.thingsboard.server.common.data.alarm.AlarmQuery; 21 import org.thingsboard.server.common.data.alarm.AlarmQuery;
  22 +import org.thingsboard.server.common.data.id.CustomerId;
22 import org.thingsboard.server.common.data.id.EntityId; 23 import org.thingsboard.server.common.data.id.EntityId;
23 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
24 import org.thingsboard.server.common.data.page.PageData; 25 import org.thingsboard.server.common.data.page.PageData;
  26 +import org.thingsboard.server.common.data.query.AlarmData;
  27 +import org.thingsboard.server.common.data.query.AlarmDataPageLink;
  28 +import org.thingsboard.server.common.data.query.AlarmDataQuery;
25 import org.thingsboard.server.dao.Dao; 29 import org.thingsboard.server.dao.Dao;
26 30
  31 +import java.util.Collection;
27 import java.util.UUID; 32 import java.util.UUID;
28 33
29 /** 34 /**
@@ -40,4 +45,7 @@ public interface AlarmDao extends Dao<Alarm> { @@ -40,4 +45,7 @@ public interface AlarmDao extends Dao<Alarm> {
40 Alarm save(TenantId tenantId, Alarm alarm); 45 Alarm save(TenantId tenantId, Alarm alarm);
41 46
42 PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query); 47 PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query);
  48 +
  49 + PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
  50 + AlarmDataPageLink pageLink, Collection<EntityId> orderedEntityIds);
43 } 51 }
@@ -35,10 +35,14 @@ import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; @@ -35,10 +35,14 @@ import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
35 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 35 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
36 import org.thingsboard.server.common.data.alarm.AlarmStatus; 36 import org.thingsboard.server.common.data.alarm.AlarmStatus;
37 import org.thingsboard.server.common.data.id.AlarmId; 37 import org.thingsboard.server.common.data.id.AlarmId;
  38 +import org.thingsboard.server.common.data.id.CustomerId;
38 import org.thingsboard.server.common.data.id.EntityId; 39 import org.thingsboard.server.common.data.id.EntityId;
39 import org.thingsboard.server.common.data.id.TenantId; 40 import org.thingsboard.server.common.data.id.TenantId;
40 import org.thingsboard.server.common.data.page.PageData; 41 import org.thingsboard.server.common.data.page.PageData;
41 import org.thingsboard.server.common.data.page.TimePageLink; 42 import org.thingsboard.server.common.data.page.TimePageLink;
  43 +import org.thingsboard.server.common.data.query.AlarmData;
  44 +import org.thingsboard.server.common.data.query.AlarmDataPageLink;
  45 +import org.thingsboard.server.common.data.query.AlarmDataQuery;
42 import org.thingsboard.server.common.data.relation.EntityRelation; 46 import org.thingsboard.server.common.data.relation.EntityRelation;
43 import org.thingsboard.server.common.data.relation.EntityRelationsQuery; 47 import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
44 import org.thingsboard.server.common.data.relation.EntitySearchDirection; 48 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
@@ -54,6 +58,7 @@ import javax.annotation.Nullable; @@ -54,6 +58,7 @@ import javax.annotation.Nullable;
54 import javax.annotation.PostConstruct; 58 import javax.annotation.PostConstruct;
55 import javax.annotation.PreDestroy; 59 import javax.annotation.PreDestroy;
56 import java.util.ArrayList; 60 import java.util.ArrayList;
  61 +import java.util.Collection;
57 import java.util.Comparator; 62 import java.util.Comparator;
58 import java.util.List; 63 import java.util.List;
59 import java.util.Set; 64 import java.util.Set;
@@ -69,6 +74,8 @@ import static org.thingsboard.server.dao.service.Validator.validateId; @@ -69,6 +74,8 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
69 @Slf4j 74 @Slf4j
70 public class BaseAlarmService extends AbstractEntityService implements AlarmService { 75 public class BaseAlarmService extends AbstractEntityService implements AlarmService {
71 76
  77 + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
  78 + public static final String INCORRECT_CUSTOMER_ID = "Incorrect customerId ";
72 public static final String ALARM_RELATION_PREFIX = "ALARM_"; 79 public static final String ALARM_RELATION_PREFIX = "ALARM_";
73 80
74 @Autowired 81 @Autowired
@@ -124,6 +131,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -124,6 +131,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
124 } 131 }
125 132
126 @Override 133 @Override
  134 + public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
  135 + AlarmDataPageLink pageLink, Collection<EntityId> orderedEntityIds) {
  136 + validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
  137 + validateId(customerId, INCORRECT_CUSTOMER_ID + customerId);
  138 + return alarmDao.findAlarmDataByQueryForEntities(tenantId, customerId, pageLink, orderedEntityIds);
  139 + }
  140 +
  141 + @Override
127 public Boolean deleteAlarm(TenantId tenantId, AlarmId alarmId) { 142 public Boolean deleteAlarm(TenantId tenantId, AlarmId alarmId) {
128 try { 143 try {
129 log.debug("Deleting Alarm Id: {}", alarmId); 144 log.debug("Deleting Alarm Id: {}", alarmId);
@@ -227,6 +227,7 @@ public class ModelConstants { @@ -227,6 +227,7 @@ public class ModelConstants {
227 public static final String ALARM_TYPE_PROPERTY = "type"; 227 public static final String ALARM_TYPE_PROPERTY = "type";
228 public static final String ALARM_DETAILS_PROPERTY = "details"; 228 public static final String ALARM_DETAILS_PROPERTY = "details";
229 public static final String ALARM_ORIGINATOR_ID_PROPERTY = "originator_id"; 229 public static final String ALARM_ORIGINATOR_ID_PROPERTY = "originator_id";
  230 + public static final String ALARM_ORIGINATOR_NAME_PROPERTY = "originator_name";
230 public static final String ALARM_ORIGINATOR_TYPE_PROPERTY = "originator_type"; 231 public static final String ALARM_ORIGINATOR_TYPE_PROPERTY = "originator_type";
231 public static final String ALARM_SEVERITY_PROPERTY = "severity"; 232 public static final String ALARM_SEVERITY_PROPERTY = "severity";
232 public static final String ALARM_STATUS_PROPERTY = "status"; 233 public static final String ALARM_STATUS_PROPERTY = "status";
@@ -20,10 +20,17 @@ import org.springframework.data.domain.Pageable; @@ -20,10 +20,17 @@ import org.springframework.data.domain.Pageable;
20 import org.springframework.data.jpa.repository.Query; 20 import org.springframework.data.jpa.repository.Query;
21 import org.springframework.data.repository.CrudRepository; 21 import org.springframework.data.repository.CrudRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
  23 +import org.thingsboard.server.common.data.id.CustomerId;
  24 +import org.thingsboard.server.common.data.id.EntityId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.page.PageData;
  27 +import org.thingsboard.server.common.data.query.AlarmData;
  28 +import org.thingsboard.server.common.data.query.AlarmDataQuery;
23 import org.thingsboard.server.dao.model.sql.AlarmEntity; 29 import org.thingsboard.server.dao.model.sql.AlarmEntity;
24 import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; 30 import org.thingsboard.server.dao.model.sql.AlarmInfoEntity;
25 import org.thingsboard.server.dao.util.SqlDao; 31 import org.thingsboard.server.dao.util.SqlDao;
26 32
  33 +import java.util.Collection;
27 import java.util.List; 34 import java.util.List;
28 import java.util.UUID; 35 import java.util.UUID;
29 36
@@ -72,4 +79,6 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> { @@ -72,4 +79,6 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> {
72 @Param("endTime") Long endTime, 79 @Param("endTime") Long endTime,
73 @Param("searchText") String searchText, 80 @Param("searchText") String searchText,
74 Pageable pageable); 81 Pageable pageable);
  82 +
  83 +
75 } 84 }
@@ -26,17 +26,23 @@ import org.thingsboard.server.common.data.alarm.Alarm; @@ -26,17 +26,23 @@ import org.thingsboard.server.common.data.alarm.Alarm;
26 import org.thingsboard.server.common.data.alarm.AlarmInfo; 26 import org.thingsboard.server.common.data.alarm.AlarmInfo;
27 import org.thingsboard.server.common.data.alarm.AlarmQuery; 27 import org.thingsboard.server.common.data.alarm.AlarmQuery;
28 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; 28 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
  29 +import org.thingsboard.server.common.data.id.CustomerId;
29 import org.thingsboard.server.common.data.id.EntityId; 30 import org.thingsboard.server.common.data.id.EntityId;
30 import org.thingsboard.server.common.data.id.TenantId; 31 import org.thingsboard.server.common.data.id.TenantId;
31 import org.thingsboard.server.common.data.page.PageData; 32 import org.thingsboard.server.common.data.page.PageData;
  33 +import org.thingsboard.server.common.data.query.AlarmData;
  34 +import org.thingsboard.server.common.data.query.AlarmDataPageLink;
  35 +import org.thingsboard.server.common.data.query.AlarmDataQuery;
32 import org.thingsboard.server.dao.DaoUtil; 36 import org.thingsboard.server.dao.DaoUtil;
33 import org.thingsboard.server.dao.alarm.AlarmDao; 37 import org.thingsboard.server.dao.alarm.AlarmDao;
34 import org.thingsboard.server.dao.alarm.BaseAlarmService; 38 import org.thingsboard.server.dao.alarm.BaseAlarmService;
35 import org.thingsboard.server.dao.model.sql.AlarmEntity; 39 import org.thingsboard.server.dao.model.sql.AlarmEntity;
36 import org.thingsboard.server.dao.relation.RelationDao; 40 import org.thingsboard.server.dao.relation.RelationDao;
37 import org.thingsboard.server.dao.sql.JpaAbstractDao; 41 import org.thingsboard.server.dao.sql.JpaAbstractDao;
  42 +import org.thingsboard.server.dao.sql.query.AlarmQueryRepository;
38 import org.thingsboard.server.dao.util.SqlDao; 43 import org.thingsboard.server.dao.util.SqlDao;
39 44
  45 +import java.util.Collection;
40 import java.util.List; 46 import java.util.List;
41 import java.util.Objects; 47 import java.util.Objects;
42 import java.util.UUID; 48 import java.util.UUID;
@@ -56,6 +62,9 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A @@ -56,6 +62,9 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
56 private AlarmRepository alarmRepository; 62 private AlarmRepository alarmRepository;
57 63
58 @Autowired 64 @Autowired
  65 + private AlarmQueryRepository alarmQueryRepository;
  66 +
  67 + @Autowired
59 private RelationDao relationDao; 68 private RelationDao relationDao;
60 69
61 @Override 70 @Override
@@ -116,4 +125,9 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A @@ -116,4 +125,9 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
116 ) 125 )
117 ); 126 );
118 } 127 }
  128 +
  129 + @Override
  130 + public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, AlarmDataPageLink pageLink, Collection<EntityId> orderedEntityIds) {
  131 + return alarmQueryRepository.findAlarmDataByQueryForEntities(tenantId, customerId, pageLink, orderedEntityIds);
  132 + }
119 } 133 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sql.query;
  17 +
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.util.StringUtils;
  22 +import org.thingsboard.server.common.data.EntityType;
  23 +import org.thingsboard.server.common.data.alarm.Alarm;
  24 +import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  25 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  26 +import org.thingsboard.server.common.data.id.AlarmId;
  27 +import org.thingsboard.server.common.data.id.EntityIdFactory;
  28 +import org.thingsboard.server.common.data.id.TenantId;
  29 +import org.thingsboard.server.common.data.page.PageData;
  30 +import org.thingsboard.server.common.data.query.AlarmData;
  31 +import org.thingsboard.server.common.data.query.EntityDataPageLink;
  32 +import org.thingsboard.server.dao.model.ModelConstants;
  33 +
  34 +import java.util.Arrays;
  35 +import java.util.Collections;
  36 +import java.util.List;
  37 +import java.util.Map;
  38 +import java.util.UUID;
  39 +import java.util.stream.Collectors;
  40 +
  41 +@Slf4j
  42 +public class AlarmDataAdapter {
  43 +
  44 + private final static ObjectMapper mapper = new ObjectMapper();
  45 +
  46 + public static PageData<AlarmData> createAlarmData(EntityDataPageLink pageLink,
  47 + List<Map<String, Object>> rows,
  48 + int totalElements) {
  49 + int totalPages = pageLink.getPageSize() > 0 ? (int) Math.ceil((float) totalElements / pageLink.getPageSize()) : 1;
  50 + int startIndex = pageLink.getPageSize() * pageLink.getPage();
  51 + boolean hasNext = pageLink.getPageSize() > 0 && totalElements > startIndex + rows.size();
  52 + List<AlarmData> entitiesData = convertListToAlarmData(rows);
  53 + return new PageData<>(entitiesData, totalPages, totalElements, hasNext);
  54 + }
  55 +
  56 + private static List<AlarmData> convertListToAlarmData(List<Map<String, Object>> result) {
  57 + return result.stream().map(AlarmDataAdapter::toEntityData).collect(Collectors.toList());
  58 + }
  59 +
  60 + private static AlarmData toEntityData(Map<String, Object> row) {
  61 + Alarm alarm = new Alarm();
  62 + alarm.setId(new AlarmId((UUID) row.get(ModelConstants.ID_PROPERTY)));
  63 + alarm.setCreatedTime((long) row.get(ModelConstants.CREATED_TIME_PROPERTY));
  64 + alarm.setAckTs((long) row.get(ModelConstants.ALARM_ACK_TS_PROPERTY));
  65 + alarm.setClearTs((long) row.get(ModelConstants.ALARM_CLEAR_TS_PROPERTY));
  66 + alarm.setStartTs((long) row.get(ModelConstants.ALARM_START_TS_PROPERTY));
  67 + alarm.setEndTs((long) row.get(ModelConstants.ALARM_END_TS_PROPERTY));
  68 + Object additionalInfo = row.get(ModelConstants.ADDITIONAL_INFO_PROPERTY);
  69 + if (additionalInfo != null) {
  70 + try {
  71 + alarm.setDetails(mapper.readTree(additionalInfo.toString()));
  72 + } catch (JsonProcessingException e) {
  73 + log.warn("Failed to parse json: {}", row.get(ModelConstants.ADDITIONAL_INFO_PROPERTY), e);
  74 + }
  75 + }
  76 + EntityType originatorType = EntityType.values()[(int) row.get(ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY)];
  77 + UUID originatorId = (UUID) row.get(ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY);
  78 + alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, originatorId));
  79 + alarm.setPropagate((boolean) row.get(ModelConstants.ALARM_PROPAGATE_PROPERTY));
  80 + alarm.setType(row.get(ModelConstants.ALARM_TYPE_PROPERTY).toString());
  81 + alarm.setSeverity(AlarmSeverity.valueOf(row.get(ModelConstants.ALARM_SEVERITY_PROPERTY).toString()));
  82 + alarm.setStatus(AlarmStatus.valueOf(row.get(ModelConstants.ALARM_STATUS_PROPERTY).toString()));
  83 + alarm.setTenantId(new TenantId((UUID) row.get(ModelConstants.TENANT_ID_PROPERTY)));
  84 + if (row.get(ModelConstants.ALARM_PROPAGATE_RELATION_TYPES) != null) {
  85 + String propagateRelationTypes = row.get(ModelConstants.ALARM_PROPAGATE_RELATION_TYPES).toString();
  86 + if (!StringUtils.isEmpty(propagateRelationTypes)) {
  87 + alarm.setPropagateRelationTypes(Arrays.asList(propagateRelationTypes.split(",")));
  88 + } else {
  89 + alarm.setPropagateRelationTypes(Collections.emptyList());
  90 + }
  91 + } else {
  92 + alarm.setPropagateRelationTypes(Collections.emptyList());
  93 + }
  94 + UUID entityId = (UUID) row.get(ModelConstants.ENTITY_ID_COLUMN);
  95 + Object originatorNameObj = row.get(ModelConstants.ALARM_ORIGINATOR_NAME_PROPERTY);
  96 + String originatorName = originatorNameObj != null ? originatorNameObj.toString() : null;
  97 + return new AlarmData(alarm, originatorName, entityId);
  98 + }
  99 +
  100 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sql.query;
  17 +
  18 +import org.thingsboard.server.common.data.id.CustomerId;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.page.PageData;
  22 +import org.thingsboard.server.common.data.query.AlarmData;
  23 +import org.thingsboard.server.common.data.query.AlarmDataPageLink;
  24 +
  25 +import java.util.Collection;
  26 +
  27 +public interface AlarmQueryRepository {
  28 +
  29 + PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
  30 + AlarmDataPageLink pageLink, Collection<EntityId> orderedEntityIds);
  31 +
  32 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sql.query;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
  21 +import org.springframework.stereotype.Repository;
  22 +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
  23 +import org.thingsboard.server.common.data.alarm.AlarmSeverity;
  24 +import org.thingsboard.server.common.data.alarm.AlarmStatus;
  25 +import org.thingsboard.server.common.data.id.CustomerId;
  26 +import org.thingsboard.server.common.data.id.EntityId;
  27 +import org.thingsboard.server.common.data.id.TenantId;
  28 +import org.thingsboard.server.common.data.page.PageData;
  29 +import org.thingsboard.server.common.data.query.AlarmData;
  30 +import org.thingsboard.server.common.data.query.AlarmDataPageLink;
  31 +import org.thingsboard.server.common.data.query.EntityDataSortOrder;
  32 +import org.thingsboard.server.common.data.query.EntityKeyType;
  33 +import org.thingsboard.server.dao.model.ModelConstants;
  34 +import org.thingsboard.server.dao.util.SqlDao;
  35 +
  36 +import java.util.Collection;
  37 +import java.util.HashMap;
  38 +import java.util.HashSet;
  39 +import java.util.List;
  40 +import java.util.Map;
  41 +import java.util.Set;
  42 +import java.util.stream.Collectors;
  43 +
  44 +@SqlDao
  45 +@Repository
  46 +@Slf4j
  47 +public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
  48 +
  49 + private static final Map<String, String> alarmFieldColumnMap = new HashMap<>();
  50 +
  51 + static {
  52 + alarmFieldColumnMap.put("createdTime", ModelConstants.CREATED_TIME_PROPERTY);
  53 + alarmFieldColumnMap.put("ackTs", ModelConstants.ALARM_ACK_TS_PROPERTY);
  54 + alarmFieldColumnMap.put("clearTs", ModelConstants.ALARM_CLEAR_TS_PROPERTY);
  55 + alarmFieldColumnMap.put("details", ModelConstants.ADDITIONAL_INFO_PROPERTY);
  56 + alarmFieldColumnMap.put("endTs", ModelConstants.ALARM_END_TS_PROPERTY);
  57 + alarmFieldColumnMap.put("startTs", ModelConstants.ALARM_START_TS_PROPERTY);
  58 + alarmFieldColumnMap.put("status", ModelConstants.ALARM_STATUS_PROPERTY);
  59 + alarmFieldColumnMap.put("type", ModelConstants.ALARM_TYPE_PROPERTY);
  60 + alarmFieldColumnMap.put("severity", ModelConstants.ALARM_SEVERITY_PROPERTY);
  61 + alarmFieldColumnMap.put("originator_id", ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY);
  62 + alarmFieldColumnMap.put("originator_type", ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY);
  63 + }
  64 +
  65 + public static final String SELECT_ORIGINATOR_NAME = " CASE" +
  66 + " WHEN a.originator_type = 0" +
  67 + " THEN (select title from tenant where id = a.originator_id)" +
  68 + " WHEN a.originator_type = 1 " +
  69 + " THEN (select title from customer where id = a.originator_id)" +
  70 + " WHEN a.originator_type = 2" +
  71 + " THEN (select CONCAT (first_name, ' ', last_name) from tb_user where id = a.originator_id)" +
  72 + " WHEN a.originator_type = 3" +
  73 + " THEN (select title from dashboard where id = a.originator_id)" +
  74 + " WHEN a.originator_type = 4" +
  75 + " THEN (select name from asset where id = a.originator_id)" +
  76 + " WHEN a.originator_type = 5" +
  77 + " THEN (select name from device where id = a.originator_id)" +
  78 + " WHEN a.originator_type = 9" +
  79 + " THEN (select name from entity_view where id = a.originator_id)" +
  80 + " END as originator_name";
  81 +
  82 + public static final String FIELDS_SELECTION = "select a.id as id," +
  83 + " a.created_time as created_time," +
  84 + " a.ack_ts as ack_ts," +
  85 + " a.clear_ts as clear_ts," +
  86 + " a.additional_info as additional_info," +
  87 + " a.end_ts as end_ts," +
  88 + " a.originator_id as originator_id," +
  89 + " a.originator_type as originator_type," +
  90 + " a.propagate as propagate," +
  91 + " a.severity as severity," +
  92 + " a.start_ts as start_ts," +
  93 + " a.status as status, " +
  94 + " a.tenant_id as tenant_id, " +
  95 + " a.propagate_relation_types as propagate_relation_types, " +
  96 + " a.type as type," + SELECT_ORIGINATOR_NAME + ", ";
  97 +
  98 + public static final String JOIN_RELATIONS = "left join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ALARM_ANY' and a.id = r.to_id";
  99 +
  100 + @Autowired
  101 + protected NamedParameterJdbcTemplate jdbcTemplate;
  102 +
  103 +
  104 + @Override
  105 + public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
  106 + AlarmDataPageLink pageLink, Collection<EntityId> orderedEntityIds) {
  107 + QueryContext ctx = new QueryContext();
  108 +
  109 + StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION);
  110 + StringBuilder fromPart = new StringBuilder(" from alarm a ");
  111 + StringBuilder wherePart = new StringBuilder(" where ");
  112 + StringBuilder sortPart = new StringBuilder(" order by ");
  113 + boolean addAnd = false;
  114 + if (pageLink.isSearchPropagatedAlarms()) {
  115 + selectPart.append(" r.from_id as entity_id ");
  116 + fromPart.append(JOIN_RELATIONS);
  117 + } else {
  118 + selectPart.append(" a.originator_id as entity_id ");
  119 + }
  120 + EntityDataSortOrder sortOrder = pageLink.getSortOrder();
  121 + if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) {
  122 + String sortOrderKey = sortOrder.getKey().getKey();
  123 + sortPart.append("a.").append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey))
  124 + .append(" ").append(sortOrder.getDirection().name());
  125 + ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList()));
  126 + if (pageLink.isSearchPropagatedAlarms()) {
  127 + fromPart.append(" and r.from_id in (:entity_ids)");
  128 + } else {
  129 + wherePart.append(" a.originator_id in (:entity_ids)");
  130 + addAnd = true;
  131 + }
  132 + } else {
  133 + fromPart.append(" left join (select * from (VALUES");
  134 + int entityIdIdx = 0;
  135 + int lastEntityIdIdx = orderedEntityIds.size() - 1;
  136 + for (EntityId entityId : orderedEntityIds) {
  137 + fromPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")");
  138 + if (entityIdIdx != lastEntityIdIdx) {
  139 + fromPart.append(",");
  140 + } else {
  141 + fromPart.append(")");
  142 + }
  143 + entityIdIdx++;
  144 + }
  145 + fromPart.append(" as e(id, priority)) e ");
  146 + if (pageLink.isSearchPropagatedAlarms()) {
  147 + fromPart.append("on r.from_id = e.id");
  148 + } else {
  149 + fromPart.append("on a.originator_id = e.id");
  150 + }
  151 + sortPart.append("e.priority");
  152 + }
  153 +
  154 + if (pageLink.getStartTs() > 0) {
  155 + addAndIfNeeded(wherePart, addAnd);
  156 + addAnd = true;
  157 + ctx.addLongParameter("startTime", pageLink.getStartTs());
  158 + wherePart.append("a.created_time >= :startTime");
  159 + }
  160 +
  161 + if (pageLink.getEndTs() > 0) {
  162 + addAndIfNeeded(wherePart, addAnd);
  163 + addAnd = true;
  164 + ctx.addLongParameter("endTime", pageLink.getEndTs());
  165 + wherePart.append("a.created_time <= :endTime");
  166 + }
  167 +
  168 + if (pageLink.getTypeList() != null && !pageLink.getTypeList().isEmpty()) {
  169 + addAndIfNeeded(wherePart, addAnd);
  170 + addAnd = true;
  171 + ctx.addStringListParameter("alarmTypes", pageLink.getTypeList());
  172 + wherePart.append("a.type in (:alarmTypes)");
  173 + }
  174 +
  175 + if (pageLink.getSeverityList() != null && !pageLink.getSeverityList().isEmpty()) {
  176 + addAndIfNeeded(wherePart, addAnd);
  177 + addAnd = true;
  178 + ctx.addStringListParameter("alarmSeverities", pageLink.getSeverityList().stream().map(AlarmSeverity::name).collect(Collectors.toList()));
  179 + wherePart.append("a.severity in (:alarmSeverities)");
  180 + }
  181 +
  182 + if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) {
  183 + Set<AlarmStatus> statusSet = toStatusSet(pageLink.getStatusList());
  184 + if (!statusSet.isEmpty()) {
  185 + addAndIfNeeded(wherePart, addAnd);
  186 + addAnd = true;
  187 + ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList()));
  188 + wherePart.append(" a.status in (:alarmStatuses)");
  189 + }
  190 + }
  191 +
  192 + String countQuery = fromPart.toString() + wherePart.toString();
  193 + int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) %s", countQuery), ctx, Integer.class);
  194 +
  195 + String dataQuery = selectPart.toString() + countQuery + sortPart;
  196 +
  197 + int startIndex = pageLink.getPageSize() * pageLink.getPage();
  198 + if (pageLink.getPageSize() > 0) {
  199 + dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex);
  200 + }
  201 + List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx);
  202 + return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements);
  203 + }
  204 +
  205 + private Set<AlarmStatus> toStatusSet(List<AlarmSearchStatus> statusList) {
  206 + Set<AlarmStatus> result = new HashSet<>();
  207 + for (AlarmSearchStatus searchStatus : statusList) {
  208 + switch (searchStatus) {
  209 + case ACK:
  210 + result.add(AlarmStatus.ACTIVE_ACK);
  211 + result.add(AlarmStatus.CLEARED_ACK);
  212 + break;
  213 + case UNACK:
  214 + result.add(AlarmStatus.ACTIVE_UNACK);
  215 + result.add(AlarmStatus.CLEARED_UNACK);
  216 + break;
  217 + case CLEARED:
  218 + result.add(AlarmStatus.CLEARED_ACK);
  219 + result.add(AlarmStatus.CLEARED_UNACK);
  220 + break;
  221 + case ACTIVE:
  222 + result.add(AlarmStatus.ACTIVE_ACK);
  223 + result.add(AlarmStatus.ACTIVE_UNACK);
  224 + break;
  225 + default:
  226 + break;
  227 + }
  228 + if (searchStatus == AlarmSearchStatus.ANY || result.size() == AlarmStatus.values().length) {
  229 + result.clear();
  230 + return result;
  231 + }
  232 + }
  233 + return result;
  234 + }
  235 +
  236 + private void addAndIfNeeded(StringBuilder wherePart, boolean addAnd) {
  237 + if (addAnd) {
  238 + wherePart.append(" and ");
  239 + }
  240 + }
  241 +}
@@ -218,7 +218,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -218,7 +218,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
218 @Override 218 @Override
219 public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { 219 public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) {
220 EntityType entityType = resolveEntityType(query.getEntityFilter()); 220 EntityType entityType = resolveEntityType(query.getEntityFilter());
221 - EntityQueryContext ctx = new EntityQueryContext(); 221 + QueryContext ctx = new QueryContext();
222 ctx.append("select count(e.id) from "); 222 ctx.append("select count(e.id) from ");
223 ctx.append(addEntityTableQuery(ctx, query.getEntityFilter(), entityType)); 223 ctx.append(addEntityTableQuery(ctx, query.getEntityFilter(), entityType));
224 ctx.append(" e where "); 224 ctx.append(" e where ");
@@ -228,7 +228,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -228,7 +228,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
228 228
229 @Override 229 @Override
230 public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { 230 public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) {
231 - EntityQueryContext ctx = new EntityQueryContext(); 231 + QueryContext ctx = new QueryContext();
232 EntityType entityType = resolveEntityType(query.getEntityFilter()); 232 EntityType entityType = resolveEntityType(query.getEntityFilter());
233 EntityDataPageLink pageLink = query.getPageLink(); 233 EntityDataPageLink pageLink = query.getPageLink();
234 234
@@ -308,7 +308,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -308,7 +308,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
308 return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements); 308 return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements);
309 } 309 }
310 310
311 - private String buildEntityWhere(EntityQueryContext ctx, 311 + private String buildEntityWhere(QueryContext ctx,
312 TenantId tenantId, 312 TenantId tenantId,
313 CustomerId customerId, 313 CustomerId customerId,
314 EntityFilter entityFilter, 314 EntityFilter entityFilter,
@@ -327,7 +327,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -327,7 +327,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
327 return result; 327 return result;
328 } 328 }
329 329
330 - private String buildPermissionQuery(EntityQueryContext ctx, EntityFilter entityFilter, TenantId tenantId, CustomerId customerId, EntityType entityType) { 330 + private String buildPermissionQuery(QueryContext ctx, EntityFilter entityFilter, TenantId tenantId, CustomerId customerId, EntityType entityType) {
331 switch (entityFilter.getType()) { 331 switch (entityFilter.getType()) {
332 case RELATIONS_QUERY: 332 case RELATIONS_QUERY:
333 case DEVICE_SEARCH_QUERY: 333 case DEVICE_SEARCH_QUERY:
@@ -343,7 +343,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -343,7 +343,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
343 } 343 }
344 } 344 }
345 345
346 - private String defaultPermissionQuery(EntityQueryContext ctx, TenantId tenantId, CustomerId customerId, EntityType entityType) { 346 + private String defaultPermissionQuery(QueryContext ctx, TenantId tenantId, CustomerId customerId, EntityType entityType) {
347 ctx.addUuidParameter("permissions_tenant_id", tenantId.getId()); 347 ctx.addUuidParameter("permissions_tenant_id", tenantId.getId());
348 if (customerId != null && !customerId.isNullUid()) { 348 if (customerId != null && !customerId.isNullUid()) {
349 ctx.addUuidParameter("permissions_customer_id", customerId.getId()); 349 ctx.addUuidParameter("permissions_customer_id", customerId.getId());
@@ -357,7 +357,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -357,7 +357,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
357 } 357 }
358 } 358 }
359 359
360 - private String buildEntityFilterQuery(EntityQueryContext ctx, EntityFilter entityFilter) { 360 + private String buildEntityFilterQuery(QueryContext ctx, EntityFilter entityFilter) {
361 switch (entityFilter.getType()) { 361 switch (entityFilter.getType()) {
362 case SINGLE_ENTITY: 362 case SINGLE_ENTITY:
363 return this.singleEntityQuery(ctx, (SingleEntityFilter) entityFilter); 363 return this.singleEntityQuery(ctx, (SingleEntityFilter) entityFilter);
@@ -378,7 +378,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -378,7 +378,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
378 } 378 }
379 } 379 }
380 380
381 - private String addEntityTableQuery(EntityQueryContext ctx, EntityFilter entityFilter, EntityType entityType) { 381 + private String addEntityTableQuery(QueryContext ctx, EntityFilter entityFilter, EntityType entityType) {
382 switch (entityFilter.getType()) { 382 switch (entityFilter.getType()) {
383 case RELATIONS_QUERY: 383 case RELATIONS_QUERY:
384 return relationQuery(ctx, (RelationsQueryFilter) entityFilter); 384 return relationQuery(ctx, (RelationsQueryFilter) entityFilter);
@@ -393,7 +393,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -393,7 +393,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
393 } 393 }
394 } 394 }
395 395
396 - private String entitySearchQuery(EntityQueryContext ctx, EntitySearchQueryFilter entityFilter, EntityType entityType, List<String> types) { 396 + private String entitySearchQuery(QueryContext ctx, EntitySearchQueryFilter entityFilter, EntityType entityType, List<String> types) {
397 EntityId rootId = entityFilter.getRootEntity(); 397 EntityId rootId = entityFilter.getRootEntity();
398 //TODO: fetch last level only. 398 //TODO: fetch last level only.
399 //TODO: fetch distinct records. 399 //TODO: fetch distinct records.
@@ -416,7 +416,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -416,7 +416,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
416 return query; 416 return query;
417 } 417 }
418 418
419 - private String relationQuery(EntityQueryContext ctx, RelationsQueryFilter entityFilter) { 419 + private String relationQuery(QueryContext ctx, RelationsQueryFilter entityFilter) {
420 EntityId rootId = entityFilter.getRootEntity(); 420 EntityId rootId = entityFilter.getRootEntity();
421 String lvlFilter = getLvlFilter(entityFilter.getMaxLevel()); 421 String lvlFilter = getLvlFilter(entityFilter.getMaxLevel());
422 String selectFields = SELECT_TENANT_ID + ", " + SELECT_CUSTOMER_ID 422 String selectFields = SELECT_TENANT_ID + ", " + SELECT_CUSTOMER_ID
@@ -478,7 +478,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -478,7 +478,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
478 return from; 478 return from;
479 } 479 }
480 480
481 - private String buildWhere(EntityQueryContext ctx, List<EntityKeyMapping> latestFiltersMapping) { 481 + private String buildWhere(QueryContext ctx, List<EntityKeyMapping> latestFiltersMapping) {
482 String latestFilters = EntityKeyMapping.buildQuery(ctx, latestFiltersMapping); 482 String latestFilters = EntityKeyMapping.buildQuery(ctx, latestFiltersMapping);
483 if (!StringUtils.isEmpty(latestFilters)) { 483 if (!StringUtils.isEmpty(latestFilters)) {
484 return String.format("where %s", latestFilters); 484 return String.format("where %s", latestFilters);
@@ -487,7 +487,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -487,7 +487,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
487 } 487 }
488 } 488 }
489 489
490 - private String buildTextSearchQuery(EntityQueryContext ctx, List<EntityKeyMapping> selectionMapping, String searchText) { 490 + private String buildTextSearchQuery(QueryContext ctx, List<EntityKeyMapping> selectionMapping, String searchText) {
491 if (!StringUtils.isEmpty(searchText) && !selectionMapping.isEmpty()) { 491 if (!StringUtils.isEmpty(searchText) && !selectionMapping.isEmpty()) {
492 String lowerSearchText = searchText.toLowerCase() + "%"; 492 String lowerSearchText = searchText.toLowerCase() + "%";
493 List<String> searchPredicates = selectionMapping.stream().map(mapping -> { 493 List<String> searchPredicates = selectionMapping.stream().map(mapping -> {
@@ -502,22 +502,22 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { @@ -502,22 +502,22 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
502 } 502 }
503 } 503 }
504 504
505 - private String singleEntityQuery(EntityQueryContext ctx, SingleEntityFilter filter) { 505 + private String singleEntityQuery(QueryContext ctx, SingleEntityFilter filter) {
506 ctx.addUuidParameter("entity_filter_single_entity_id", filter.getSingleEntity().getId()); 506 ctx.addUuidParameter("entity_filter_single_entity_id", filter.getSingleEntity().getId());
507 return "e.id=:entity_filter_single_entity_id"; 507 return "e.id=:entity_filter_single_entity_id";
508 } 508 }
509 509
510 - private String entityListQuery(EntityQueryContext ctx, EntityListFilter filter) { 510 + private String entityListQuery(QueryContext ctx, EntityListFilter filter) {
511 ctx.addUuidListParameter("entity_filter_entity_ids", filter.getEntityList().stream().map(UUID::fromString).collect(Collectors.toList())); 511 ctx.addUuidListParameter("entity_filter_entity_ids", filter.getEntityList().stream().map(UUID::fromString).collect(Collectors.toList()));
512 return "e.id in (:entity_filter_entity_ids)"; 512 return "e.id in (:entity_filter_entity_ids)";
513 } 513 }
514 514
515 - private String entityNameQuery(EntityQueryContext ctx, EntityNameFilter filter) { 515 + private String entityNameQuery(QueryContext ctx, EntityNameFilter filter) {
516 ctx.addStringParameter("entity_filter_name_filter", filter.getEntityNameFilter()); 516 ctx.addStringParameter("entity_filter_name_filter", filter.getEntityNameFilter());
517 return "lower(e.search_text) like lower(concat(:entity_filter_name_filter, '%%'))"; 517 return "lower(e.search_text) like lower(concat(:entity_filter_name_filter, '%%'))";
518 } 518 }
519 519
520 - private String typeQuery(EntityQueryContext ctx, EntityFilter filter) { 520 + private String typeQuery(QueryContext ctx, EntityFilter filter) {
521 String type; 521 String type;
522 String name; 522 String name;
523 switch (filter.getType()) { 523 switch (filter.getType()) {
@@ -194,7 +194,7 @@ public class EntityKeyMapping { @@ -194,7 +194,7 @@ public class EntityKeyMapping {
194 } 194 }
195 } 195 }
196 196
197 - public Stream<String> toQueries(EntityQueryContext ctx) { 197 + public Stream<String> toQueries(QueryContext ctx) {
198 if (hasFilter()) { 198 if (hasFilter()) {
199 String keyAlias = entityKey.getType().equals(EntityKeyType.ENTITY_FIELD) ? "e" : alias; 199 String keyAlias = entityKey.getType().equals(EntityKeyType.ENTITY_FIELD) ? "e" : alias;
200 return keyFilters.stream().map(keyFilter -> 200 return keyFilters.stream().map(keyFilter ->
@@ -204,7 +204,7 @@ public class EntityKeyMapping { @@ -204,7 +204,7 @@ public class EntityKeyMapping {
204 } 204 }
205 } 205 }
206 206
207 - public String toLatestJoin(EntityQueryContext ctx, EntityFilter entityFilter, EntityType entityType) { 207 + public String toLatestJoin(QueryContext ctx, EntityFilter entityFilter, EntityType entityType) {
208 String entityTypeStr; 208 String entityTypeStr;
209 if (entityFilter.getType().equals(EntityFilterType.RELATIONS_QUERY)) { 209 if (entityFilter.getType().equals(EntityFilterType.RELATIONS_QUERY)) {
210 entityTypeStr = "entities.entity_type"; 210 entityTypeStr = "entities.entity_type";
@@ -239,12 +239,12 @@ public class EntityKeyMapping { @@ -239,12 +239,12 @@ public class EntityKeyMapping {
239 Collectors.joining(", ")); 239 Collectors.joining(", "));
240 } 240 }
241 241
242 - public static String buildLatestJoins(EntityQueryContext ctx, EntityFilter entityFilter, EntityType entityType, List<EntityKeyMapping> latestMappings) { 242 + public static String buildLatestJoins(QueryContext ctx, EntityFilter entityFilter, EntityType entityType, List<EntityKeyMapping> latestMappings) {
243 return latestMappings.stream().map(mapping -> mapping.toLatestJoin(ctx, entityFilter, entityType)).collect( 243 return latestMappings.stream().map(mapping -> mapping.toLatestJoin(ctx, entityFilter, entityType)).collect(
244 Collectors.joining(" ")); 244 Collectors.joining(" "));
245 } 245 }
246 246
247 - public static String buildQuery(EntityQueryContext ctx, List<EntityKeyMapping> mappings) { 247 + public static String buildQuery(QueryContext ctx, List<EntityKeyMapping> mappings) {
248 return mappings.stream().flatMap(mapping -> mapping.toQueries(ctx)).collect( 248 return mappings.stream().flatMap(mapping -> mapping.toQueries(ctx)).collect(
249 Collectors.joining(" AND ")); 249 Collectors.joining(" AND "));
250 } 250 }
@@ -357,11 +357,11 @@ public class EntityKeyMapping { @@ -357,11 +357,11 @@ public class EntityKeyMapping {
357 return String.join(", ", attrValSelection, attrTsSelection); 357 return String.join(", ", attrValSelection, attrTsSelection);
358 } 358 }
359 359
360 - private String buildKeyQuery(EntityQueryContext ctx, String alias, KeyFilter keyFilter) { 360 + private String buildKeyQuery(QueryContext ctx, String alias, KeyFilter keyFilter) {
361 return this.buildPredicateQuery(ctx, alias, keyFilter.getKey(), keyFilter.getPredicate()); 361 return this.buildPredicateQuery(ctx, alias, keyFilter.getKey(), keyFilter.getPredicate());
362 } 362 }
363 363
364 - private String buildPredicateQuery(EntityQueryContext ctx, String alias, EntityKey key, KeyFilterPredicate predicate) { 364 + private String buildPredicateQuery(QueryContext ctx, String alias, EntityKey key, KeyFilterPredicate predicate) {
365 if (predicate.getType().equals(FilterPredicateType.COMPLEX)) { 365 if (predicate.getType().equals(FilterPredicateType.COMPLEX)) {
366 return this.buildComplexPredicateQuery(ctx, alias, key, (ComplexFilterPredicate) predicate); 366 return this.buildComplexPredicateQuery(ctx, alias, key, (ComplexFilterPredicate) predicate);
367 } else { 367 } else {
@@ -369,14 +369,14 @@ public class EntityKeyMapping { @@ -369,14 +369,14 @@ public class EntityKeyMapping {
369 } 369 }
370 } 370 }
371 371
372 - private String buildComplexPredicateQuery(EntityQueryContext ctx, String alias, EntityKey key, ComplexFilterPredicate predicate) { 372 + private String buildComplexPredicateQuery(QueryContext ctx, String alias, EntityKey key, ComplexFilterPredicate predicate) {
373 return predicate.getPredicates().stream() 373 return predicate.getPredicates().stream()
374 .map(keyFilterPredicate -> this.buildPredicateQuery(ctx, alias, key, keyFilterPredicate)).collect(Collectors.joining( 374 .map(keyFilterPredicate -> this.buildPredicateQuery(ctx, alias, key, keyFilterPredicate)).collect(Collectors.joining(
375 " " + predicate.getOperation().name() + " " 375 " " + predicate.getOperation().name() + " "
376 )); 376 ));
377 } 377 }
378 378
379 - private String buildSimplePredicateQuery(EntityQueryContext ctx, String alias, EntityKey key, KeyFilterPredicate predicate) { 379 + private String buildSimplePredicateQuery(QueryContext ctx, String alias, EntityKey key, KeyFilterPredicate predicate) {
380 if (predicate.getType().equals(FilterPredicateType.NUMERIC)) { 380 if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
381 if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) { 381 if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
382 String column = entityFieldColumnMap.get(key.getKey()); 382 String column = entityFieldColumnMap.get(key.getKey());
@@ -402,7 +402,7 @@ public class EntityKeyMapping { @@ -402,7 +402,7 @@ public class EntityKeyMapping {
402 } 402 }
403 } 403 }
404 404
405 - private String buildStringPredicateQuery(EntityQueryContext ctx, String field, StringFilterPredicate stringFilterPredicate) { 405 + private String buildStringPredicateQuery(QueryContext ctx, String field, StringFilterPredicate stringFilterPredicate) {
406 String operationField = field; 406 String operationField = field;
407 String paramName = getNextParameterName(field); 407 String paramName = getNextParameterName(field);
408 String value = stringFilterPredicate.getValue(); 408 String value = stringFilterPredicate.getValue();
@@ -439,7 +439,7 @@ public class EntityKeyMapping { @@ -439,7 +439,7 @@ public class EntityKeyMapping {
439 return String.format("(%s is not null and %s)", field, stringOperationQuery); 439 return String.format("(%s is not null and %s)", field, stringOperationQuery);
440 } 440 }
441 441
442 - private String buildNumericPredicateQuery(EntityQueryContext ctx, String field, NumericFilterPredicate numericFilterPredicate) { 442 + private String buildNumericPredicateQuery(QueryContext ctx, String field, NumericFilterPredicate numericFilterPredicate) {
443 String paramName = getNextParameterName(field); 443 String paramName = getNextParameterName(field);
444 ctx.addDoubleParameter(paramName, numericFilterPredicate.getValue()); 444 ctx.addDoubleParameter(paramName, numericFilterPredicate.getValue());
445 String numericOperationQuery = ""; 445 String numericOperationQuery = "";
@@ -466,7 +466,7 @@ public class EntityKeyMapping { @@ -466,7 +466,7 @@ public class EntityKeyMapping {
466 return String.format("(%s is not null and %s)", field, numericOperationQuery); 466 return String.format("(%s is not null and %s)", field, numericOperationQuery);
467 } 467 }
468 468
469 - private String buildBooleanPredicateQuery(EntityQueryContext ctx, String field, 469 + private String buildBooleanPredicateQuery(QueryContext ctx, String field,
470 BooleanFilterPredicate booleanFilterPredicate) { 470 BooleanFilterPredicate booleanFilterPredicate) {
471 String paramName = getNextParameterName(field); 471 String paramName = getNextParameterName(field);
472 ctx.addBooleanParameter(paramName, booleanFilterPredicate.isValue()); 472 ctx.addBooleanParameter(paramName, booleanFilterPredicate.isValue());
dao/src/main/java/org/thingsboard/server/dao/sql/query/QueryContext.java renamed from dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityQueryContext.java
@@ -24,13 +24,13 @@ import java.util.List; @@ -24,13 +24,13 @@ import java.util.List;
24 import java.util.Map; 24 import java.util.Map;
25 import java.util.UUID; 25 import java.util.UUID;
26 26
27 -public class EntityQueryContext implements SqlParameterSource { 27 +public class QueryContext implements SqlParameterSource {
28 private static final PostgresUUIDType UUID_TYPE = new PostgresUUIDType(); 28 private static final PostgresUUIDType UUID_TYPE = new PostgresUUIDType();
29 29
30 private final StringBuilder query; 30 private final StringBuilder query;
31 private final Map<String, Parameter> params; 31 private final Map<String, Parameter> params;
32 32
33 - public EntityQueryContext() { 33 + public QueryContext() {
34 query = new StringBuilder(); 34 query = new StringBuilder();
35 params = new HashMap<>(); 35 params = new HashMap<>();
36 } 36 }
@@ -91,6 +91,10 @@ public class EntityQueryContext implements SqlParameterSource { @@ -91,6 +91,10 @@ public class EntityQueryContext implements SqlParameterSource {
91 addParameter(name, value, Types.DOUBLE, "DOUBLE"); 91 addParameter(name, value, Types.DOUBLE, "DOUBLE");
92 } 92 }
93 93
  94 + public void addLongParameter(String name, long value) {
  95 + addParameter(name, value, Types.BIGINT, "BIGINT");
  96 + }
  97 +
94 public void addStringListParameter(String name, List<String> value) { 98 public void addStringListParameter(String name, List<String> value) {
95 addParameter(name, value, Types.VARCHAR, "VARCHAR"); 99 addParameter(name, value, Types.VARCHAR, "VARCHAR");
96 } 100 }
@@ -24,16 +24,26 @@ import org.thingsboard.server.common.data.Tenant; @@ -24,16 +24,26 @@ import org.thingsboard.server.common.data.Tenant;
24 import org.thingsboard.server.common.data.alarm.Alarm; 24 import org.thingsboard.server.common.data.alarm.Alarm;
25 import org.thingsboard.server.common.data.alarm.AlarmInfo; 25 import org.thingsboard.server.common.data.alarm.AlarmInfo;
26 import org.thingsboard.server.common.data.alarm.AlarmQuery; 26 import org.thingsboard.server.common.data.alarm.AlarmQuery;
  27 +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
27 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 28 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
28 import org.thingsboard.server.common.data.alarm.AlarmStatus; 29 import org.thingsboard.server.common.data.alarm.AlarmStatus;
29 import org.thingsboard.server.common.data.id.AssetId; 30 import org.thingsboard.server.common.data.id.AssetId;
  31 +import org.thingsboard.server.common.data.id.CustomerId;
30 import org.thingsboard.server.common.data.id.TenantId; 32 import org.thingsboard.server.common.data.id.TenantId;
31 import org.thingsboard.server.common.data.page.PageData; 33 import org.thingsboard.server.common.data.page.PageData;
32 import org.thingsboard.server.common.data.page.SortOrder; 34 import org.thingsboard.server.common.data.page.SortOrder;
33 import org.thingsboard.server.common.data.page.TimePageLink; 35 import org.thingsboard.server.common.data.page.TimePageLink;
  36 +import org.thingsboard.server.common.data.query.AlarmData;
  37 +import org.thingsboard.server.common.data.query.AlarmDataPageLink;
  38 +import org.thingsboard.server.common.data.query.AlarmDataQuery;
  39 +import org.thingsboard.server.common.data.query.EntityDataSortOrder;
  40 +import org.thingsboard.server.common.data.query.EntityKey;
  41 +import org.thingsboard.server.common.data.query.EntityKeyType;
34 import org.thingsboard.server.common.data.relation.EntityRelation; 42 import org.thingsboard.server.common.data.relation.EntityRelation;
35 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 43 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
36 44
  45 +import java.util.Arrays;
  46 +import java.util.Collections;
37 import java.util.List; 47 import java.util.List;
38 import java.util.concurrent.ExecutionException; 48 import java.util.concurrent.ExecutionException;
39 49
@@ -196,6 +206,128 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { @@ -196,6 +206,128 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
196 } 206 }
197 207
198 @Test 208 @Test
  209 + public void testFindAlarmUsingAlarmDataQuery() throws ExecutionException, InterruptedException {
  210 + AssetId parentId = new AssetId(Uuids.timeBased());
  211 + AssetId childId = new AssetId(Uuids.timeBased());
  212 +
  213 + EntityRelation relation = new EntityRelation(parentId, childId, EntityRelation.CONTAINS_TYPE);
  214 +
  215 + Assert.assertTrue(relationService.saveRelationAsync(tenantId, relation).get());
  216 +
  217 + long ts = System.currentTimeMillis();
  218 + Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId)
  219 + .type(TEST_ALARM)
  220 + .propagate(false)
  221 + .severity(AlarmSeverity.CRITICAL)
  222 + .status(AlarmStatus.ACTIVE_UNACK)
  223 + .startTs(ts).build();
  224 +
  225 + Alarm created = alarmService.createOrUpdateAlarm(alarm);
  226 +
  227 + AlarmDataPageLink pageLink = new AlarmDataPageLink();
  228 + pageLink.setPage(0);
  229 + pageLink.setPageSize(1);
  230 + pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime")));
  231 +
  232 + pageLink.setStartTs(0L);
  233 + pageLink.setEndTs(System.currentTimeMillis());
  234 + pageLink.setSearchPropagatedAlarms(false);
  235 + pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
  236 + pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
  237 +
  238 + PageData<AlarmData> alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Collections.singletonList(childId));
  239 +
  240 + Assert.assertNotNull(alarms.getData());
  241 + Assert.assertEquals(1, alarms.getData().size());
  242 + Assert.assertEquals(created, alarms.getData().get(0));
  243 +
  244 + pageLink.setPage(0);
  245 + pageLink.setPageSize(1);
  246 + pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime")));
  247 +
  248 + pageLink.setStartTs(0L);
  249 + pageLink.setEndTs(System.currentTimeMillis());
  250 + pageLink.setSearchPropagatedAlarms(false);
  251 + pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
  252 + pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
  253 +
  254 + alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Collections.singletonList(childId));
  255 + Assert.assertNotNull(alarms.getData());
  256 + Assert.assertEquals(1, alarms.getData().size());
  257 + Assert.assertEquals(created, alarms.getData().get(0));
  258 +
  259 + // Check child relation
  260 + Assert.assertNotNull(alarms.getData());
  261 + Assert.assertEquals(1, alarms.getData().size());
  262 + Assert.assertEquals(created, new Alarm(alarms.getData().get(0)));
  263 +
  264 + created.setPropagate(true);
  265 + created = alarmService.createOrUpdateAlarm(created);
  266 +
  267 + // Check child relation
  268 + pageLink.setPage(0);
  269 + pageLink.setPageSize(1);
  270 + pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime")));
  271 +
  272 + pageLink.setStartTs(0L);
  273 + pageLink.setEndTs(System.currentTimeMillis());
  274 + pageLink.setSearchPropagatedAlarms(true);
  275 + pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
  276 + pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
  277 +
  278 + alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Collections.singletonList(childId));
  279 +
  280 + // Check parent relation
  281 + pageLink.setPage(0);
  282 + pageLink.setPageSize(1);
  283 + pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime")));
  284 +
  285 + pageLink.setStartTs(0L);
  286 + pageLink.setEndTs(System.currentTimeMillis());
  287 + pageLink.setSearchPropagatedAlarms(true);
  288 + pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
  289 + pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
  290 +
  291 + alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Collections.singletonList(parentId));
  292 + Assert.assertNotNull(alarms.getData());
  293 + Assert.assertEquals(1, alarms.getData().size());
  294 + Assert.assertEquals(created, alarms.getData().get(0));
  295 +
  296 + pageLink.setPage(0);
  297 + pageLink.setPageSize(1);
  298 + pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime")));
  299 +
  300 + pageLink.setStartTs(0L);
  301 + pageLink.setEndTs(System.currentTimeMillis());
  302 + pageLink.setSearchPropagatedAlarms(true);
  303 + pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
  304 + pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
  305 +
  306 + alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Collections.singletonList(parentId));
  307 + Assert.assertNotNull(alarms.getData());
  308 + Assert.assertEquals(1, alarms.getData().size());
  309 + Assert.assertEquals(created, alarms.getData().get(0));
  310 +
  311 + alarmService.ackAlarm(tenantId, created.getId(), System.currentTimeMillis()).get();
  312 + created = alarmService.findAlarmByIdAsync(tenantId, created.getId()).get();
  313 +
  314 + pageLink.setPage(0);
  315 + pageLink.setPageSize(1);
  316 + pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime")));
  317 +
  318 + pageLink.setStartTs(0L);
  319 + pageLink.setEndTs(System.currentTimeMillis());
  320 + pageLink.setSearchPropagatedAlarms(true);
  321 + pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING));
  322 + pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE));
  323 +
  324 + alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Collections.singletonList(childId));
  325 + Assert.assertNotNull(alarms.getData());
  326 + Assert.assertEquals(1, alarms.getData().size());
  327 + Assert.assertEquals(created, alarms.getData().get(0));
  328 + }
  329 +
  330 + @Test
199 public void testDeleteAlarm() throws ExecutionException, InterruptedException { 331 public void testDeleteAlarm() throws ExecutionException, InterruptedException {
200 AssetId parentId = new AssetId(Uuids.timeBased()); 332 AssetId parentId = new AssetId(Uuids.timeBased());
201 AssetId childId = new AssetId(Uuids.timeBased()); 333 AssetId childId = new AssetId(Uuids.timeBased());
@@ -8997,10 +8997,10 @@ @@ -8997,10 +8997,10 @@
8997 "integrity": "sha512-4O3GWAYJaauMCILm07weko2rHA8a4kjn7+8Lg4s1d7SxwS/3IpkVD/GljbRrIJ1c1W/XGJ3GbuK7RyYZEJChhw==" 8997 "integrity": "sha512-4O3GWAYJaauMCILm07weko2rHA8a4kjn7+8Lg4s1d7SxwS/3IpkVD/GljbRrIJ1c1W/XGJ3GbuK7RyYZEJChhw=="
8998 }, 8998 },
8999 "ngx-flowchart": { 8999 "ngx-flowchart": {
9000 - "version": "git://github.com/thingsboard/ngx-flowchart.git#7a02f4748b5e7821a883c903107af5f20415d026", 9000 + "version": "git://github.com/thingsboard/ngx-flowchart.git#a4157b0eef2eb3646ef920447c7b06b39d54f87f",
9001 "from": "git://github.com/thingsboard/ngx-flowchart.git#master", 9001 "from": "git://github.com/thingsboard/ngx-flowchart.git#master",
9002 "requires": { 9002 "requires": {
9003 - "tslib": "^1.13.0" 9003 + "tslib": "^1.10.0"
9004 }, 9004 },
9005 "dependencies": { 9005 "dependencies": {
9006 "tslib": { 9006 "tslib": {