Commit 351684a6cdefc028213ace04234b49e6dc13661d

Authored by Igor Kulikov
1 parent 2cb71c15

TB-63: Update Alarms search query with AlarmSearchStatus parameter.

... ... @@ -22,10 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
22 22 import org.springframework.web.bind.annotation.*;
23 23 import org.thingsboard.server.common.data.Customer;
24 24 import org.thingsboard.server.common.data.Event;
25   -import org.thingsboard.server.common.data.alarm.Alarm;
26   -import org.thingsboard.server.common.data.alarm.AlarmId;
27   -import org.thingsboard.server.common.data.alarm.AlarmQuery;
28   -import org.thingsboard.server.common.data.alarm.AlarmStatus;
  25 +import org.thingsboard.server.common.data.alarm.*;
29 26 import org.thingsboard.server.common.data.asset.Asset;
30 27 import org.thingsboard.server.common.data.id.*;
31 28 import org.thingsboard.server.common.data.page.TextPageData;
... ... @@ -103,24 +100,31 @@ public class AlarmController extends BaseController {
103 100 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
104 101 @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
105 102 @ResponseBody
106   - public TimePageData<Alarm> getAlarms(
  103 + public TimePageData<AlarmInfo> getAlarms(
107 104 @PathVariable("entityType") String strEntityType,
108 105 @PathVariable("entityId") String strEntityId,
  106 + @RequestParam(required = false) String searchStatus,
109 107 @RequestParam(required = false) String status,
110 108 @RequestParam int limit,
111 109 @RequestParam(required = false) Long startTime,
112 110 @RequestParam(required = false) Long endTime,
113 111 @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
114   - @RequestParam(required = false) String offset
  112 + @RequestParam(required = false) String offset,
  113 + @RequestParam(required = false) Boolean fetchOriginator
115 114 ) throws ThingsboardException {
116 115 checkParameter("EntityId", strEntityId);
117 116 checkParameter("EntityType", strEntityType);
118 117 EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
  118 + AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus);
119 119 AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status);
  120 + if (alarmSearchStatus != null && alarmStatus != null) {
  121 + throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " +
  122 + "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
  123 + }
120 124 checkEntityId(entityId);
121 125 try {
122 126 TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
123   - return checkNotNull(alarmService.findAlarms(new AlarmQuery(entityId, pageLink, alarmStatus)).get());
  127 + return checkNotNull(alarmService.findAlarms(new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
124 128 } catch (Exception e) {
125 129 throw handleException(e);
126 130 }
... ...
... ... @@ -53,6 +53,21 @@ public class Alarm extends BaseData<AlarmId> implements HasName {
53 53 super(id);
54 54 }
55 55
  56 + public Alarm(Alarm alarm) {
  57 + super(alarm.getId());
  58 + this.tenantId = alarm.getTenantId();
  59 + this.type = alarm.getType();
  60 + this.originator = alarm.getOriginator();
  61 + this.severity = alarm.getSeverity();
  62 + this.status = alarm.getStatus();
  63 + this.startTs = alarm.getStartTs();
  64 + this.endTs = alarm.getEndTs();
  65 + this.ackTs = alarm.getAckTs();
  66 + this.clearTs = alarm.getClearTs();
  67 + this.details = alarm.getDetails();
  68 + this.propagate = alarm.isPropagate();
  69 + }
  70 +
56 71 @Override
57 72 @JsonProperty(access = JsonProperty.Access.READ_ONLY)
58 73 public String getName() {
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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.alarm;
  17 +
  18 +public class AlarmInfo extends Alarm {
  19 +
  20 + private static final long serialVersionUID = 2807343093519543363L;
  21 +
  22 + private String originatorName;
  23 +
  24 + public AlarmInfo() {
  25 + super();
  26 + }
  27 +
  28 + public AlarmInfo(Alarm alarm) {
  29 + super(alarm);
  30 + }
  31 +
  32 + public String getOriginatorName() {
  33 + return originatorName;
  34 + }
  35 +
  36 + public void setOriginatorName(String originatorName) {
  37 + this.originatorName = originatorName;
  38 + }
  39 +
  40 + @Override
  41 + public boolean equals(Object o) {
  42 + if (this == o) return true;
  43 + if (o == null || getClass() != o.getClass()) return false;
  44 + if (!super.equals(o)) return false;
  45 +
  46 + AlarmInfo alarmInfo = (AlarmInfo) o;
  47 +
  48 + return originatorName != null ? originatorName.equals(alarmInfo.originatorName) : alarmInfo.originatorName == null;
  49 +
  50 + }
  51 +
  52 + @Override
  53 + public int hashCode() {
  54 + int result = super.hashCode();
  55 + result = 31 * result + (originatorName != null ? originatorName.hashCode() : 0);
  56 + return result;
  57 + }
  58 +}
... ...
... ... @@ -32,6 +32,8 @@ public class AlarmQuery {
32 32
33 33 private EntityId affectedEntityId;
34 34 private TimePageLink pageLink;
  35 + private AlarmSearchStatus searchStatus;
35 36 private AlarmStatus status;
  37 + private Boolean fetchOriginator;
36 38
37 39 }
... ...
  1 +/**
  2 + * Copyright © 2016-2017 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 +
  17 +package org.thingsboard.server.common.data.alarm;
  18 +
  19 +public enum AlarmSearchStatus {
  20 +
  21 + ANY, ACTIVE, CLEARED, ACK, UNACK
  22 +
  23 +}
... ...
... ... @@ -30,4 +30,13 @@ public enum AlarmStatus {
30 30 return this == CLEARED_ACK || this == CLEARED_UNACK;
31 31 }
32 32
  33 + public AlarmSearchStatus getClearSearchStatus() {
  34 + return this.isCleared() ? AlarmSearchStatus.CLEARED : AlarmSearchStatus.ACTIVE;
  35 + }
  36 +
  37 + public AlarmSearchStatus getAckSearchStatus() {
  38 + return this.isAck() ? AlarmSearchStatus.ACK : AlarmSearchStatus.UNACK;
  39 + }
  40 +
  41 +
33 42 }
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm;
17 17
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 19 import org.thingsboard.server.common.data.alarm.Alarm;
  20 +import org.thingsboard.server.common.data.alarm.AlarmInfo;
20 21 import org.thingsboard.server.common.data.alarm.AlarmQuery;
21 22 import org.thingsboard.server.common.data.id.EntityId;
22 23 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -37,5 +38,5 @@ public interface AlarmDao extends Dao<AlarmEntity> {
37 38
38 39 AlarmEntity save(Alarm alarm);
39 40
40   - ListenableFuture<List<Alarm>> findAlarms(AlarmQuery query);
  41 + ListenableFuture<List<AlarmInfo>> findAlarms(AlarmQuery query);
41 42 }
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm;
17 17
18 18 import com.datastax.driver.core.querybuilder.QueryBuilder;
19 19 import com.datastax.driver.core.querybuilder.Select;
  20 +import com.google.common.base.Function;
20 21 import com.google.common.util.concurrent.AsyncFunction;
21 22 import com.google.common.util.concurrent.Futures;
22 23 import com.google.common.util.concurrent.ListenableFuture;
... ... @@ -25,7 +26,9 @@ import org.springframework.beans.factory.annotation.Autowired;
25 26 import org.springframework.stereotype.Component;
26 27 import org.thingsboard.server.common.data.EntityType;
27 28 import org.thingsboard.server.common.data.alarm.Alarm;
  29 +import org.thingsboard.server.common.data.alarm.AlarmInfo;
28 30 import org.thingsboard.server.common.data.alarm.AlarmQuery;
  31 +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
29 32 import org.thingsboard.server.common.data.id.EntityId;
30 33 import org.thingsboard.server.common.data.id.TenantId;
31 34 import org.thingsboard.server.common.data.relation.EntityRelation;
... ... @@ -94,15 +97,25 @@ public class AlarmDaoImpl extends AbstractModelDao<AlarmEntity> implements Alarm
94 97 }
95 98
96 99 @Override
97   - public ListenableFuture<List<Alarm>> findAlarms(AlarmQuery query) {
98   - log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink());
  100 + public ListenableFuture<List<AlarmInfo>> findAlarms(AlarmQuery query) {
  101 + log.trace("Try to find alarms by entity [{}], searchStatus [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getSearchStatus(), query.getStatus(), query.getPageLink());
99 102 EntityId affectedEntity = query.getAffectedEntityId();
100   - String relationType = query.getStatus() == null ? BaseAlarmService.ALARM_RELATION : BaseAlarmService.ALARM_RELATION_PREFIX + query.getStatus().name();
  103 + String searchStatusName;
  104 + if (query.getSearchStatus() == null && query.getStatus() == null) {
  105 + searchStatusName = AlarmSearchStatus.ANY.name();
  106 + } else if (query.getSearchStatus() != null) {
  107 + searchStatusName = query.getSearchStatus().name();
  108 + } else {
  109 + searchStatusName = query.getStatus().name();
  110 + }
  111 + String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName;
101 112 ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink());
102   - return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<Alarm>>) input -> {
103   - List<ListenableFuture<Alarm>> alarmFutures = new ArrayList<>(input.size());
  113 + return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<AlarmInfo>>) input -> {
  114 + List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
104 115 for (EntityRelation relation : input) {
105   - alarmFutures.add(findAlarmByIdAsync(relation.getTo().getId()));
  116 + alarmFutures.add(Futures.transform(
  117 + findAlarmByIdAsync(relation.getTo().getId()), (Function<Alarm, AlarmInfo>)
  118 + alarm1 -> new AlarmInfo(alarm1)));
106 119 }
107 120 return Futures.successfulAsList(alarmFutures);
108 121 });
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.alarm;
18 18 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.AlarmId;
  21 +import org.thingsboard.server.common.data.alarm.AlarmInfo;
21 22 import org.thingsboard.server.common.data.alarm.AlarmQuery;
22 23 import org.thingsboard.server.common.data.page.TimePageData;
23 24
... ... @@ -34,6 +35,6 @@ public interface AlarmService {
34 35
35 36 ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId);
36 37
37   - ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query);
  38 + ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query);
38 39
39 40 }
... ...
... ... @@ -17,22 +17,21 @@ package org.thingsboard.server.dao.alarm;
17 17
18 18
19 19 import com.google.common.base.Function;
  20 +import com.google.common.util.concurrent.AsyncFunction;
20 21 import com.google.common.util.concurrent.Futures;
21 22 import com.google.common.util.concurrent.ListenableFuture;
22 23 import lombok.extern.slf4j.Slf4j;
23 24 import org.springframework.beans.factory.annotation.Autowired;
24 25 import org.springframework.stereotype.Service;
25 26 import org.springframework.util.StringUtils;
26   -import org.thingsboard.server.common.data.alarm.Alarm;
27   -import org.thingsboard.server.common.data.alarm.AlarmId;
28   -import org.thingsboard.server.common.data.alarm.AlarmQuery;
29   -import org.thingsboard.server.common.data.alarm.AlarmStatus;
  27 +import org.thingsboard.server.common.data.alarm.*;
30 28 import org.thingsboard.server.common.data.id.EntityId;
31 29 import org.thingsboard.server.common.data.page.TimePageData;
32 30 import org.thingsboard.server.common.data.relation.EntityRelation;
33 31 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
34 32 import org.thingsboard.server.dao.entity.AbstractEntityService;
35 33 import org.thingsboard.server.dao.entity.BaseEntityService;
  34 +import org.thingsboard.server.dao.entity.EntityService;
36 35 import org.thingsboard.server.dao.exception.DataValidationException;
37 36 import org.thingsboard.server.dao.model.*;
38 37 import org.thingsboard.server.dao.relation.EntityRelationsQuery;
... ... @@ -45,6 +44,7 @@ import org.thingsboard.server.dao.tenant.TenantDao;
45 44 import javax.annotation.Nullable;
46 45 import javax.annotation.PostConstruct;
47 46 import javax.annotation.PreDestroy;
  47 +import java.util.ArrayList;
48 48 import java.util.List;
49 49 import java.util.concurrent.ExecutionException;
50 50 import java.util.concurrent.ExecutorService;
... ... @@ -59,7 +59,6 @@ import static org.thingsboard.server.dao.service.Validator.*;
59 59 public class BaseAlarmService extends AbstractEntityService implements AlarmService {
60 60
61 61 public static final String ALARM_RELATION_PREFIX = "ALARM_";
62   - public static final String ALARM_RELATION = "ALARM_ANY";
63 62
64 63 @Autowired
65 64 private AlarmDao alarmDao;
... ... @@ -70,6 +69,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
70 69 @Autowired
71 70 private RelationService relationService;
72 71
  72 + @Autowired
  73 + private EntityService entityService;
  74 +
73 75 protected ExecutorService readResultsProcessingExecutor;
74 76
75 77 @PostConstruct
... ... @@ -116,11 +118,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
116 118 query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
117 119 List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
118 120 for (EntityId parentId : parentEntities) {
119   - createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION, RelationTypeGroup.ALARM));
120   - createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name(), RelationTypeGroup.ALARM));
  121 + createAlarmRelation(parentId, saved.getId(), saved.getStatus(), true);
121 122 }
122   - createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION, RelationTypeGroup.ALARM));
123   - createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name(), RelationTypeGroup.ALARM));
  123 + createAlarmRelation(alarm.getOriginator(), saved.getId(), saved.getStatus(), true);
124 124 return saved;
125 125 }
126 126
... ... @@ -199,12 +199,27 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
199 199 }
200 200
201 201 @Override
202   - public ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query) {
203   - ListenableFuture<List<Alarm>> alarms = alarmDao.findAlarms(query);
204   - return Futures.transform(alarms, new Function<List<Alarm>, TimePageData<Alarm>>() {
  202 + public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) {
  203 + ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query);
  204 + if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
  205 + alarms = Futures.transform(alarms, (AsyncFunction<List<AlarmInfo>, List<AlarmInfo>>) input -> {
  206 + List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
  207 + for (AlarmInfo alarmInfo : input) {
  208 + alarmFutures.add(Futures.transform(
  209 + entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>)
  210 + originatorName -> {
  211 + alarmInfo.setOriginatorName(originatorName);
  212 + return alarmInfo;
  213 + }
  214 + ));
  215 + }
  216 + return Futures.successfulAsList(alarmFutures);
  217 + });
  218 + }
  219 + return Futures.transform(alarms, new Function<List<AlarmInfo>, TimePageData<AlarmInfo>>() {
205 220 @Nullable
206 221 @Override
207   - public TimePageData<Alarm> apply(@Nullable List<Alarm> alarms) {
  222 + public TimePageData<AlarmInfo> apply(@Nullable List<AlarmInfo> alarms) {
208 223 return new TimePageData<>(alarms, query.getPageLink());
209 224 }
210 225 });
... ... @@ -245,17 +260,45 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
245 260 query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
246 261 List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
247 262 for (EntityId parentId : parentEntities) {
248   - deleteRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name(), RelationTypeGroup.ALARM));
249   - createRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name(), RelationTypeGroup.ALARM));
250   - }
251   - deleteRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name(), RelationTypeGroup.ALARM));
252   - createRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name(), RelationTypeGroup.ALARM));
  263 + updateAlarmRelation(parentId, alarm.getId(), oldStatus, newStatus);
  264 + }
  265 + updateAlarmRelation(alarm.getOriginator(), alarm.getId(), oldStatus, newStatus);
253 266 } catch (ExecutionException | InterruptedException e) {
254 267 log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus);
255 268 throw new RuntimeException(e);
256 269 }
257 270 }
258 271
  272 + private void createAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) {
  273 + try {
  274 + if (createAnyRelation) {
  275 + createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM));
  276 + }
  277 + createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
  278 + createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
  279 + createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
  280 + } catch (ExecutionException | InterruptedException e) {
  281 + log.warn("[{}] Failed to create relation. Status: [{}]", alarmId, status);
  282 + throw new RuntimeException(e);
  283 + }
  284 + }
  285 +
  286 + private void deleteAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus status) {
  287 + try {
  288 + deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
  289 + deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
  290 + deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
  291 + } catch (ExecutionException | InterruptedException e) {
  292 + log.warn("[{}] Failed to delete relation. Status: [{}]", alarmId, status);
  293 + throw new RuntimeException(e);
  294 + }
  295 + }
  296 +
  297 + private void updateAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) {
  298 + deleteAlarmRelation(entityId, alarmId, oldStatus);
  299 + createAlarmRelation(entityId, alarmId, newStatus, false);
  300 + }
  301 +
259 302 private <T> ListenableFuture<T> getAndUpdate(AlarmId alarmId, Function<Alarm, T> function) {
260 303 validateId(alarmId, "Alarm id should be specified!");
261 304 ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId());
... ...
... ... @@ -178,9 +178,14 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao {
178 178 eq(ModelConstants.RELATION_TYPE_GROUP_PROPERTY, typeGroup.name()),
179 179 eq(ModelConstants.RELATION_TYPE_PROPERTY, relationType),
180 180 eq(ModelConstants.RELATION_TO_TYPE_PROPERTY, childType.name())),
181   - Arrays.asList(QueryBuilder.asc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY),
182   - QueryBuilder.asc(ModelConstants.RELATION_TYPE_PROPERTY),
183   - QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY)),
  181 + Arrays.asList(
  182 + pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY) :
  183 + QueryBuilder.asc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY),
  184 + pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TYPE_PROPERTY) :
  185 + QueryBuilder.asc(ModelConstants.RELATION_TYPE_PROPERTY),
  186 + pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TO_TYPE_PROPERTY) :
  187 + QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY)
  188 + ),
184 189 pageLink, ModelConstants.RELATION_TO_ID_PROPERTY);
185 190 return getFuture(executeAsyncRead(query), rs -> getEntityRelations(rs));
186 191 }
... ...
... ... @@ -22,10 +22,7 @@ import org.junit.Before;
22 22 import org.junit.Test;
23 23 import org.thingsboard.server.common.data.EntityType;
24 24 import org.thingsboard.server.common.data.Tenant;
25   -import org.thingsboard.server.common.data.alarm.Alarm;
26   -import org.thingsboard.server.common.data.alarm.AlarmQuery;
27   -import org.thingsboard.server.common.data.alarm.AlarmSeverity;
28   -import org.thingsboard.server.common.data.alarm.AlarmStatus;
  25 +import org.thingsboard.server.common.data.alarm.*;
29 26 import org.thingsboard.server.common.data.id.AssetId;
30 27 import org.thingsboard.server.common.data.id.DeviceId;
31 28 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -117,7 +114,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
117 114 Alarm created = alarmService.createOrUpdateAlarm(alarm);
118 115
119 116 // Check child relation
120   - TimePageData<Alarm> alarms = alarmService.findAlarms(AlarmQuery.builder()
  117 + TimePageData<AlarmInfo> alarms = alarmService.findAlarms(AlarmQuery.builder()
121 118 .affectedEntityId(childId)
122 119 .status(AlarmStatus.ACTIVE_UNACK).pageLink(
123 120 new TimePageLink(1, 0L, System.currentTimeMillis(), false)
... ...
... ... @@ -91,7 +91,7 @@ function AlarmService($http, $q, $interval, $filter) {
91 91 return deferred.promise;
92 92 }
93 93
94   - function getAlarms(entityType, entityId, pageLink, alarmStatus, ascOrder, config) {
  94 + function getAlarms(entityType, entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator, ascOrder, config) {
95 95 var deferred = $q.defer();
96 96 var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit;
97 97
... ... @@ -104,9 +104,15 @@ function AlarmService($http, $q, $interval, $filter) {
104 104 if (angular.isDefined(pageLink.idOffset)) {
105 105 url += '&offset=' + pageLink.idOffset;
106 106 }
  107 + if (alarmSearchStatus) {
  108 + url += '&searchStatus=' + alarmSearchStatus;
  109 + }
107 110 if (alarmStatus) {
108 111 url += '&status=' + alarmStatus;
109 112 }
  113 + if (fetchOriginator) {
  114 + url += '&fetchOriginator=' + ((fetchOriginator===true) ? 'true' : 'false');
  115 + }
110 116 if (angular.isDefined(ascOrder) && ascOrder != null) {
111 117 url += '&ascOrder=' + (ascOrder ? 'true' : 'false');
112 118 }
... ... @@ -121,7 +127,8 @@ function AlarmService($http, $q, $interval, $filter) {
121 127
122 128 function fetchAlarms(alarmsQuery, pageLink, deferred, alarmsList) {
123 129 getAlarms(alarmsQuery.entityType, alarmsQuery.entityId,
124   - pageLink, alarmsQuery.alarmStatus, false, {ignoreLoading: true}).then(
  130 + pageLink, alarmsQuery.alarmSearchStatus, alarmsQuery.alarmStatus,
  131 + alarmsQuery.fetchOriginator, false, {ignoreLoading: true}).then(
125 132 function success(alarms) {
126 133 if (!alarmsList) {
127 134 alarmsList = [];
... ... @@ -171,7 +178,9 @@ function AlarmService($http, $q, $interval, $filter) {
171 178 var alarmsQuery = {
172 179 entityType: entityType,
173 180 entityId: entityId,
  181 + alarmSearchStatus: null,
174 182 alarmStatus: alarmStatus,
  183 + fetchOriginator: false,
175 184 interval: interval,
176 185 limit: limit,
177 186 onAlarms: onAlarms
... ...
... ... @@ -65,6 +65,13 @@ export default angular.module('thingsboard.types', [])
65 65 clearedUnack: "CLEARED_UNACK",
66 66 clearedAck: "CLEARED_ACK"
67 67 },
  68 + alarmSearchStatus: {
  69 + any: "ANY",
  70 + active: "ACTIVE",
  71 + cleared: "CLEARED",
  72 + ack: "ACK",
  73 + unack: "UNACK"
  74 + },
68 75 aliasFilterType: {
69 76 entityList: {
70 77 value: 'entityList',
... ...