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 34 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
35 35 import org.thingsboard.server.common.data.kv.TsKvEntry;
36 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 39 import org.thingsboard.server.common.data.query.EntityData;
  40 +import org.thingsboard.server.common.data.query.EntityDataPageLink;
38 41 import org.thingsboard.server.common.data.query.EntityDataQuery;
  42 +import org.thingsboard.server.common.data.query.EntityDataSortOrder;
39 43 import org.thingsboard.server.common.data.query.EntityKey;
40 44 import org.thingsboard.server.common.data.query.EntityKeyType;
41 45 import org.thingsboard.server.common.data.query.TsValue;
  46 +import org.thingsboard.server.dao.alarm.AlarmService;
42 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 49 import org.thingsboard.server.dao.timeseries.TimeseriesService;
45 50 import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
46 51 import org.thingsboard.server.queue.util.TbCoreComponent;
47 52 import org.thingsboard.server.service.executors.DbCallbackExecutorService;
48   -import org.thingsboard.server.service.telemetry.DefaultTelemetryWebSocketService;
49 53 import org.thingsboard.server.service.telemetry.TelemetryWebSocketService;
50 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 57 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
52 58 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
53 59 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
... ... @@ -63,7 +69,6 @@ import java.util.ArrayList;
63 69 import java.util.Arrays;
64 70 import java.util.Collection;
65 71 import java.util.Collections;
66   -import java.util.Comparator;
67 72 import java.util.HashMap;
68 73 import java.util.LinkedHashMap;
69 74 import java.util.LinkedHashSet;
... ... @@ -88,7 +93,7 @@ import java.util.stream.Collectors;
88 93 public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubscriptionService {
89 94
90 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 98 @Autowired
94 99 private TelemetryWebSocketService wsService;
... ... @@ -97,6 +102,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
97 102 private EntityService entityService;
98 103
99 104 @Autowired
  105 + private AlarmService alarmService;
  106 +
  107 + @Autowired
100 108 @Lazy
101 109 private TbLocalSubscriptionService localSubscriptionService;
102 110
... ... @@ -114,10 +122,12 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
114 122
115 123 @Value("${database.ts.type}")
116 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 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 128 private int dynamicPageLinkRefreshPoolSize;
  129 + @Value("${server.ws.max_entities_per_alarm_subscription:1000}")
  130 + private int maxEntitiesPerAlarmSubscription;
121 131
122 132 private ExecutorService wsCallBackExecutor;
123 133 private boolean tsInSqlDB;
... ... @@ -195,6 +205,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
195 205 ctx.setData(data);
196 206 ctx.cancelRefreshTask();
197 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 209 TbEntityDataSubCtx finalCtx = ctx;
199 210 ScheduledFuture<?> task = scheduler.scheduleWithFixedDelay(
200 211 () -> refreshDynamicQuery(tenantId, customerId, finalCtx),
... ... @@ -230,6 +241,39 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
230 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 277 private void refreshDynamicQuery(TenantId tenantId, CustomerId customerId, TbEntityDataSubCtx finalCtx) {
234 278 try {
235 279 long start = System.currentTimeMillis();
... ... @@ -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 292 public void printStats() {
249 293 int regularQueryInvocationCntValue = regularQueryInvocationCnt.getAndSet(0);
250 294 long regularQueryInvocationTimeValue = regularQueryTimeSpent.getAndSet(0);
... ... @@ -263,17 +307,25 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
263 307 }
264 308
265 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 311 TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, sessionRef, cmd.getCmdId());
268 312 ctx.setQuery(cmd.getQuery());
269 313 sessionSubs.put(cmd.getCmdId(), ctx);
270 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 327 if (sessionSubs != null) {
276   - return sessionSubs.get(cmdId);
  328 + return (T) sessionSubs.get(cmdId);
277 329 } else {
278 330 return null;
279 331 }
... ... @@ -420,14 +472,6 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
420 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 475 @Override
432 476 public void cancelSubscription(String sessionId, EntityDataUnsubscribeCmd cmd) {
433 477 cleanupAndCancel(getSubCtx(sessionId, cmd.getCmdId()));
... ... @@ -442,9 +486,9 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
442 486
443 487 @Override
444 488 public void cancelAllSessionSubscriptions(String sessionId) {
445   - Map<Integer, TbEntityDataSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId);
  489 + Map<Integer, TbAbstractDataSubCtx> sessionSubs = subscriptionsBySessionId.remove(sessionId);
446 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 17
18 18 import lombok.AllArgsConstructor;
19 19 import lombok.Data;
  20 +import lombok.Getter;
  21 +import lombok.RequiredArgsConstructor;
  22 +import lombok.Setter;
20 23 import lombok.extern.slf4j.Slf4j;
21 24 import org.thingsboard.server.common.data.id.CustomerId;
22 25 import org.thingsboard.server.common.data.id.EntityId;
... ... @@ -51,17 +54,13 @@ import java.util.function.Function;
51 54 import java.util.stream.Collectors;
52 55
53 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 60 private TimeSeriesCmd tsCmd;
  61 + @Getter
64 62 private PageData<EntityData> data;
  63 + @Getter @Setter
65 64 private boolean initialDataSent;
66 65 private Map<Integer, EntityId> subToEntityIdMap;
67 66 private volatile ScheduledFuture<?> refreshTask;
... ... @@ -69,22 +68,7 @@ public class TbEntityDataSubCtx {
69 68 private LatestValueCmd latestValueCmd;
70 69
71 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 74 public void setData(PageData<EntityData> data) {
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.service.subscription;
17 17
18 18 import org.thingsboard.server.service.telemetry.TelemetryWebSocketSessionRef;
  19 +import org.thingsboard.server.service.telemetry.cmd.v2.AlarmDataCmd;
19 20 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
20 21 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
21 22
... ... @@ -23,6 +24,8 @@ public interface TbEntityDataSubscriptionService {
23 24
24 25 void handleCmd(TelemetryWebSocketSessionRef sessionId, EntityDataCmd cmd);
25 26
  27 + void handleCmd(TelemetryWebSocketSessionRef sessionId, AlarmDataCmd cmd);
  28 +
26 29 void cancelSubscription(String sessionId, EntityDataUnsubscribeCmd subscriptionId);
27 30
28 31 void cancelAllSessionSubscriptions(String sessionId);
... ...
... ... @@ -63,6 +63,9 @@ import org.thingsboard.server.service.telemetry.cmd.v1.SubscriptionCmd;
63 63 import org.thingsboard.server.service.telemetry.cmd.v1.TelemetryPluginCmd;
64 64 import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
65 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 69 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
67 70 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
68 71 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
... ... @@ -210,6 +213,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
210 213 if (cmdsWrapper.getEntityDataCmds() != null) {
211 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 219 if (cmdsWrapper.getEntityDataUnsubscribeCmds() != null) {
214 220 cmdsWrapper.getEntityDataUnsubscribeCmds().forEach(cmd -> handleWsEntityDataUnsubscribeCmd(sessionRef, cmd));
215 221 }
... ... @@ -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 250 private void handleWsEntityDataUnsubscribeCmd(TelemetryWebSocketSessionRef sessionRef, EntityDataUnsubscribeCmd cmd) {
235 251 String sessionId = sessionRef.getSessionId();
236 252 log.debug("[{}] Processing: {}", sessionId, cmd);
... ... @@ -246,7 +262,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
246 262 }
247 263
248 264 @Override
249   - public void sendWsMsg(String sessionId, EntityDataUpdate update) {
  265 + public void sendWsMsg(String sessionId, DataUpdate update) {
250 266 sendWsMsg(sessionId, update.getCmdId(), update);
251 267 }
252 268
... ... @@ -661,6 +677,21 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
661 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 695 private boolean validateSubscriptionCmd(TelemetryWebSocketSessionRef sessionRef, SubscriptionCmd cmd) {
665 696 if (cmd.getEntityId() == null || cmd.getEntityId().isEmpty()) {
666 697 SubscriptionUpdate update = new SubscriptionUpdate(cmd.getCmdId(), SubscriptionErrorCode.BAD_REQUEST,
... ...
... ... @@ -15,6 +15,7 @@
15 15 */
16 16 package org.thingsboard.server.service.telemetry;
17 17
  18 +import org.thingsboard.server.service.telemetry.cmd.v2.DataUpdate;
18 19 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
19 20 import org.thingsboard.server.service.telemetry.sub.SubscriptionUpdate;
20 21
... ... @@ -29,6 +30,6 @@ public interface TelemetryWebSocketService {
29 30
30 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 19 import org.thingsboard.server.service.telemetry.cmd.v1.AttributesSubscriptionCmd;
20 20 import org.thingsboard.server.service.telemetry.cmd.v1.GetHistoryCmd;
21 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 24 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
23 25 import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUnsubscribeCmd;
24 26
... ... @@ -40,4 +42,8 @@ public class TelemetryPluginCmdsWrapper {
40 42
41 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 15 */
16 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 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 26 private final EntityDataQuery query;
  27 + @Getter
26 28 private final EntityHistoryCmd historyCmd;
  29 + @Getter
27 30 private final LatestValueCmd latestCmd;
  31 + @Getter
28 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 15 */
16 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 20 import org.thingsboard.server.common.data.page.PageData;
21 21 import org.thingsboard.server.common.data.query.EntityData;
22 22 import org.thingsboard.server.service.telemetry.sub.SubscriptionErrorCode;
23 23
24 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 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 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 46 max_subscriptions_per_regular_user: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SUBSCRIPTIONS_PER_REGULAR_USER:0}"
47 47 max_subscriptions_per_public_user: "${TB_SERVER_WS_TENANT_RATE_LIMITS_MAX_SUBSCRIPTIONS_PER_PUBLIC_USER:0}"
48 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 54 rest:
52 55 limits:
53 56 tenant:
... ...
... ... @@ -26,9 +26,9 @@ import java.util.Arrays;
26 26
27 27 @RunWith(ClasspathSuite.class)
28 28 @ClasspathSuite.ClassnameFilters({
29   -// "org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
  29 + "org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
30 30 // "org.thingsboard.server.controller.sql.EntityQueryControllerSqlTest",
31   - "org.thingsboard.server.controller.sql.*Test",
  31 +// "org.thingsboard.server.controller.sql.*Test",
32 32 })
33 33 public class ControllerSqlTestSuite {
34 34
... ...
... ... @@ -24,9 +24,15 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery;
24 24 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
25 25 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
26 26 import org.thingsboard.server.common.data.alarm.AlarmStatus;
  27 +import org.thingsboard.server.common.data.id.CustomerId;
27 28 import org.thingsboard.server.common.data.id.EntityId;
28 29 import org.thingsboard.server.common.data.id.TenantId;
29 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 38 * Created by ashvayka on 11.05.17.
... ... @@ -52,4 +58,6 @@ public interface AlarmService {
52 58
53 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 38
39 39 @JsonIgnore
40 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 22 import java.util.List;
23 23
24 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 27 public EntityDataQuery() {
37   - super();
38 28 }
39 29
40 30 public EntityDataQuery(EntityFilter entityFilter) {
41 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 38 @JsonIgnore
57 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 21 SHARED_ATTRIBUTE,
22 22 SERVER_ATTRIBUTE,
23 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 19 import org.thingsboard.server.common.data.alarm.Alarm;
20 20 import org.thingsboard.server.common.data.alarm.AlarmInfo;
21 21 import org.thingsboard.server.common.data.alarm.AlarmQuery;
  22 +import org.thingsboard.server.common.data.id.CustomerId;
22 23 import org.thingsboard.server.common.data.id.EntityId;
23 24 import org.thingsboard.server.common.data.id.TenantId;
24 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 29 import org.thingsboard.server.dao.Dao;
26 30
  31 +import java.util.Collection;
27 32 import java.util.UUID;
28 33
29 34 /**
... ... @@ -40,4 +45,7 @@ public interface AlarmDao extends Dao<Alarm> {
40 45 Alarm save(TenantId tenantId, Alarm alarm);
41 46
42 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 35 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
36 36 import org.thingsboard.server.common.data.alarm.AlarmStatus;
37 37 import org.thingsboard.server.common.data.id.AlarmId;
  38 +import org.thingsboard.server.common.data.id.CustomerId;
38 39 import org.thingsboard.server.common.data.id.EntityId;
39 40 import org.thingsboard.server.common.data.id.TenantId;
40 41 import org.thingsboard.server.common.data.page.PageData;
41 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 46 import org.thingsboard.server.common.data.relation.EntityRelation;
43 47 import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
44 48 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
... ... @@ -54,6 +58,7 @@ import javax.annotation.Nullable;
54 58 import javax.annotation.PostConstruct;
55 59 import javax.annotation.PreDestroy;
56 60 import java.util.ArrayList;
  61 +import java.util.Collection;
57 62 import java.util.Comparator;
58 63 import java.util.List;
59 64 import java.util.Set;
... ... @@ -69,6 +74,8 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
69 74 @Slf4j
70 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 79 public static final String ALARM_RELATION_PREFIX = "ALARM_";
73 80
74 81 @Autowired
... ... @@ -124,6 +131,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
124 131 }
125 132
126 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 142 public Boolean deleteAlarm(TenantId tenantId, AlarmId alarmId) {
128 143 try {
129 144 log.debug("Deleting Alarm Id: {}", alarmId);
... ...
... ... @@ -227,6 +227,7 @@ public class ModelConstants {
227 227 public static final String ALARM_TYPE_PROPERTY = "type";
228 228 public static final String ALARM_DETAILS_PROPERTY = "details";
229 229 public static final String ALARM_ORIGINATOR_ID_PROPERTY = "originator_id";
  230 + public static final String ALARM_ORIGINATOR_NAME_PROPERTY = "originator_name";
230 231 public static final String ALARM_ORIGINATOR_TYPE_PROPERTY = "originator_type";
231 232 public static final String ALARM_SEVERITY_PROPERTY = "severity";
232 233 public static final String ALARM_STATUS_PROPERTY = "status";
... ...
... ... @@ -20,10 +20,17 @@ import org.springframework.data.domain.Pageable;
20 20 import org.springframework.data.jpa.repository.Query;
21 21 import org.springframework.data.repository.CrudRepository;
22 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 29 import org.thingsboard.server.dao.model.sql.AlarmEntity;
24 30 import org.thingsboard.server.dao.model.sql.AlarmInfoEntity;
25 31 import org.thingsboard.server.dao.util.SqlDao;
26 32
  33 +import java.util.Collection;
27 34 import java.util.List;
28 35 import java.util.UUID;
29 36
... ... @@ -72,4 +79,6 @@ public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> {
72 79 @Param("endTime") Long endTime,
73 80 @Param("searchText") String searchText,
74 81 Pageable pageable);
  82 +
  83 +
75 84 }
... ...
... ... @@ -26,17 +26,23 @@ import org.thingsboard.server.common.data.alarm.Alarm;
26 26 import org.thingsboard.server.common.data.alarm.AlarmInfo;
27 27 import org.thingsboard.server.common.data.alarm.AlarmQuery;
28 28 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
  29 +import org.thingsboard.server.common.data.id.CustomerId;
29 30 import org.thingsboard.server.common.data.id.EntityId;
30 31 import org.thingsboard.server.common.data.id.TenantId;
31 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 36 import org.thingsboard.server.dao.DaoUtil;
33 37 import org.thingsboard.server.dao.alarm.AlarmDao;
34 38 import org.thingsboard.server.dao.alarm.BaseAlarmService;
35 39 import org.thingsboard.server.dao.model.sql.AlarmEntity;
36 40 import org.thingsboard.server.dao.relation.RelationDao;
37 41 import org.thingsboard.server.dao.sql.JpaAbstractDao;
  42 +import org.thingsboard.server.dao.sql.query.AlarmQueryRepository;
38 43 import org.thingsboard.server.dao.util.SqlDao;
39 44
  45 +import java.util.Collection;
40 46 import java.util.List;
41 47 import java.util.Objects;
42 48 import java.util.UUID;
... ... @@ -56,6 +62,9 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
56 62 private AlarmRepository alarmRepository;
57 63
58 64 @Autowired
  65 + private AlarmQueryRepository alarmQueryRepository;
  66 +
  67 + @Autowired
59 68 private RelationDao relationDao;
60 69
61 70 @Override
... ... @@ -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 218 @Override
219 219 public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) {
220 220 EntityType entityType = resolveEntityType(query.getEntityFilter());
221   - EntityQueryContext ctx = new EntityQueryContext();
  221 + QueryContext ctx = new QueryContext();
222 222 ctx.append("select count(e.id) from ");
223 223 ctx.append(addEntityTableQuery(ctx, query.getEntityFilter(), entityType));
224 224 ctx.append(" e where ");
... ... @@ -228,7 +228,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
228 228
229 229 @Override
230 230 public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) {
231   - EntityQueryContext ctx = new EntityQueryContext();
  231 + QueryContext ctx = new QueryContext();
232 232 EntityType entityType = resolveEntityType(query.getEntityFilter());
233 233 EntityDataPageLink pageLink = query.getPageLink();
234 234
... ... @@ -308,7 +308,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
308 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 312 TenantId tenantId,
313 313 CustomerId customerId,
314 314 EntityFilter entityFilter,
... ... @@ -327,7 +327,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
327 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 331 switch (entityFilter.getType()) {
332 332 case RELATIONS_QUERY:
333 333 case DEVICE_SEARCH_QUERY:
... ... @@ -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 347 ctx.addUuidParameter("permissions_tenant_id", tenantId.getId());
348 348 if (customerId != null && !customerId.isNullUid()) {
349 349 ctx.addUuidParameter("permissions_customer_id", customerId.getId());
... ... @@ -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 361 switch (entityFilter.getType()) {
362 362 case SINGLE_ENTITY:
363 363 return this.singleEntityQuery(ctx, (SingleEntityFilter) entityFilter);
... ... @@ -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 382 switch (entityFilter.getType()) {
383 383 case RELATIONS_QUERY:
384 384 return relationQuery(ctx, (RelationsQueryFilter) entityFilter);
... ... @@ -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 397 EntityId rootId = entityFilter.getRootEntity();
398 398 //TODO: fetch last level only.
399 399 //TODO: fetch distinct records.
... ... @@ -416,7 +416,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
416 416 return query;
417 417 }
418 418
419   - private String relationQuery(EntityQueryContext ctx, RelationsQueryFilter entityFilter) {
  419 + private String relationQuery(QueryContext ctx, RelationsQueryFilter entityFilter) {
420 420 EntityId rootId = entityFilter.getRootEntity();
421 421 String lvlFilter = getLvlFilter(entityFilter.getMaxLevel());
422 422 String selectFields = SELECT_TENANT_ID + ", " + SELECT_CUSTOMER_ID
... ... @@ -478,7 +478,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
478 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 482 String latestFilters = EntityKeyMapping.buildQuery(ctx, latestFiltersMapping);
483 483 if (!StringUtils.isEmpty(latestFilters)) {
484 484 return String.format("where %s", latestFilters);
... ... @@ -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 491 if (!StringUtils.isEmpty(searchText) && !selectionMapping.isEmpty()) {
492 492 String lowerSearchText = searchText.toLowerCase() + "%";
493 493 List<String> searchPredicates = selectionMapping.stream().map(mapping -> {
... ... @@ -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 506 ctx.addUuidParameter("entity_filter_single_entity_id", filter.getSingleEntity().getId());
507 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 511 ctx.addUuidListParameter("entity_filter_entity_ids", filter.getEntityList().stream().map(UUID::fromString).collect(Collectors.toList()));
512 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 516 ctx.addStringParameter("entity_filter_name_filter", filter.getEntityNameFilter());
517 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 521 String type;
522 522 String name;
523 523 switch (filter.getType()) {
... ...
... ... @@ -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 198 if (hasFilter()) {
199 199 String keyAlias = entityKey.getType().equals(EntityKeyType.ENTITY_FIELD) ? "e" : alias;
200 200 return keyFilters.stream().map(keyFilter ->
... ... @@ -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 208 String entityTypeStr;
209 209 if (entityFilter.getType().equals(EntityFilterType.RELATIONS_QUERY)) {
210 210 entityTypeStr = "entities.entity_type";
... ... @@ -239,12 +239,12 @@ public class EntityKeyMapping {
239 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 243 return latestMappings.stream().map(mapping -> mapping.toLatestJoin(ctx, entityFilter, entityType)).collect(
244 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 248 return mappings.stream().flatMap(mapping -> mapping.toQueries(ctx)).collect(
249 249 Collectors.joining(" AND "));
250 250 }
... ... @@ -357,11 +357,11 @@ public class EntityKeyMapping {
357 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 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 365 if (predicate.getType().equals(FilterPredicateType.COMPLEX)) {
366 366 return this.buildComplexPredicateQuery(ctx, alias, key, (ComplexFilterPredicate) predicate);
367 367 } else {
... ... @@ -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 373 return predicate.getPredicates().stream()
374 374 .map(keyFilterPredicate -> this.buildPredicateQuery(ctx, alias, key, keyFilterPredicate)).collect(Collectors.joining(
375 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 380 if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
381 381 if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
382 382 String column = entityFieldColumnMap.get(key.getKey());
... ... @@ -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 406 String operationField = field;
407 407 String paramName = getNextParameterName(field);
408 408 String value = stringFilterPredicate.getValue();
... ... @@ -439,7 +439,7 @@ public class EntityKeyMapping {
439 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 443 String paramName = getNextParameterName(field);
444 444 ctx.addDoubleParameter(paramName, numericFilterPredicate.getValue());
445 445 String numericOperationQuery = "";
... ... @@ -466,7 +466,7 @@ public class EntityKeyMapping {
466 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 470 BooleanFilterPredicate booleanFilterPredicate) {
471 471 String paramName = getNextParameterName(field);
472 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 24 import java.util.Map;
25 25 import java.util.UUID;
26 26
27   -public class EntityQueryContext implements SqlParameterSource {
  27 +public class QueryContext implements SqlParameterSource {
28 28 private static final PostgresUUIDType UUID_TYPE = new PostgresUUIDType();
29 29
30 30 private final StringBuilder query;
31 31 private final Map<String, Parameter> params;
32 32
33   - public EntityQueryContext() {
  33 + public QueryContext() {
34 34 query = new StringBuilder();
35 35 params = new HashMap<>();
36 36 }
... ... @@ -91,6 +91,10 @@ public class EntityQueryContext implements SqlParameterSource {
91 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 98 public void addStringListParameter(String name, List<String> value) {
95 99 addParameter(name, value, Types.VARCHAR, "VARCHAR");
96 100 }
... ...
... ... @@ -24,16 +24,26 @@ import org.thingsboard.server.common.data.Tenant;
24 24 import org.thingsboard.server.common.data.alarm.Alarm;
25 25 import org.thingsboard.server.common.data.alarm.AlarmInfo;
26 26 import org.thingsboard.server.common.data.alarm.AlarmQuery;
  27 +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
27 28 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
28 29 import org.thingsboard.server.common.data.alarm.AlarmStatus;
29 30 import org.thingsboard.server.common.data.id.AssetId;
  31 +import org.thingsboard.server.common.data.id.CustomerId;
30 32 import org.thingsboard.server.common.data.id.TenantId;
31 33 import org.thingsboard.server.common.data.page.PageData;
32 34 import org.thingsboard.server.common.data.page.SortOrder;
33 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 42 import org.thingsboard.server.common.data.relation.EntityRelation;
35 43 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
36 44
  45 +import java.util.Arrays;
  46 +import java.util.Collections;
37 47 import java.util.List;
38 48 import java.util.concurrent.ExecutionException;
39 49
... ... @@ -196,6 +206,128 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
196 206 }
197 207
198 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 331 public void testDeleteAlarm() throws ExecutionException, InterruptedException {
200 332 AssetId parentId = new AssetId(Uuids.timeBased());
201 333 AssetId childId = new AssetId(Uuids.timeBased());
... ...
... ... @@ -8997,10 +8997,10 @@
8997 8997 "integrity": "sha512-4O3GWAYJaauMCILm07weko2rHA8a4kjn7+8Lg4s1d7SxwS/3IpkVD/GljbRrIJ1c1W/XGJ3GbuK7RyYZEJChhw=="
8998 8998 },
8999 8999 "ngx-flowchart": {
9000   - "version": "git://github.com/thingsboard/ngx-flowchart.git#7a02f4748b5e7821a883c903107af5f20415d026",
  9000 + "version": "git://github.com/thingsboard/ngx-flowchart.git#a4157b0eef2eb3646ef920447c7b06b39d54f87f",
9001 9001 "from": "git://github.com/thingsboard/ngx-flowchart.git#master",
9002 9002 "requires": {
9003   - "tslib": "^1.13.0"
  9003 + "tslib": "^1.10.0"
9004 9004 },
9005 9005 "dependencies": {
9006 9006 "tslib": {
... ...