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,10 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
22 import org.springframework.web.bind.annotation.*; 22 import org.springframework.web.bind.annotation.*;
23 import org.thingsboard.server.common.data.Customer; 23 import org.thingsboard.server.common.data.Customer;
24 import org.thingsboard.server.common.data.Event; 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 import org.thingsboard.server.common.data.asset.Asset; 26 import org.thingsboard.server.common.data.asset.Asset;
30 import org.thingsboard.server.common.data.id.*; 27 import org.thingsboard.server.common.data.id.*;
31 import org.thingsboard.server.common.data.page.TextPageData; 28 import org.thingsboard.server.common.data.page.TextPageData;
@@ -103,24 +100,31 @@ public class AlarmController extends BaseController { @@ -103,24 +100,31 @@ public class AlarmController extends BaseController {
103 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 100 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
104 @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET) 101 @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
105 @ResponseBody 102 @ResponseBody
106 - public TimePageData<Alarm> getAlarms( 103 + public TimePageData<AlarmInfo> getAlarms(
107 @PathVariable("entityType") String strEntityType, 104 @PathVariable("entityType") String strEntityType,
108 @PathVariable("entityId") String strEntityId, 105 @PathVariable("entityId") String strEntityId,
  106 + @RequestParam(required = false) String searchStatus,
109 @RequestParam(required = false) String status, 107 @RequestParam(required = false) String status,
110 @RequestParam int limit, 108 @RequestParam int limit,
111 @RequestParam(required = false) Long startTime, 109 @RequestParam(required = false) Long startTime,
112 @RequestParam(required = false) Long endTime, 110 @RequestParam(required = false) Long endTime,
113 @RequestParam(required = false, defaultValue = "false") boolean ascOrder, 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 ) throws ThingsboardException { 114 ) throws ThingsboardException {
116 checkParameter("EntityId", strEntityId); 115 checkParameter("EntityId", strEntityId);
117 checkParameter("EntityType", strEntityType); 116 checkParameter("EntityType", strEntityType);
118 EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); 117 EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
  118 + AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus);
119 AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status); 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 checkEntityId(entityId); 124 checkEntityId(entityId);
121 try { 125 try {
122 TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset); 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 } catch (Exception e) { 128 } catch (Exception e) {
125 throw handleException(e); 129 throw handleException(e);
126 } 130 }
@@ -53,6 +53,21 @@ public class Alarm extends BaseData<AlarmId> implements HasName { @@ -53,6 +53,21 @@ public class Alarm extends BaseData<AlarmId> implements HasName {
53 super(id); 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 @Override 71 @Override
57 @JsonProperty(access = JsonProperty.Access.READ_ONLY) 72 @JsonProperty(access = JsonProperty.Access.READ_ONLY)
58 public String getName() { 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,6 +32,8 @@ public class AlarmQuery {
32 32
33 private EntityId affectedEntityId; 33 private EntityId affectedEntityId;
34 private TimePageLink pageLink; 34 private TimePageLink pageLink;
  35 + private AlarmSearchStatus searchStatus;
35 private AlarmStatus status; 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,4 +30,13 @@ public enum AlarmStatus {
30 return this == CLEARED_ACK || this == CLEARED_UNACK; 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,6 +17,7 @@ package org.thingsboard.server.dao.alarm;
17 17
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.server.common.data.alarm.Alarm; 19 import org.thingsboard.server.common.data.alarm.Alarm;
  20 +import org.thingsboard.server.common.data.alarm.AlarmInfo;
20 import org.thingsboard.server.common.data.alarm.AlarmQuery; 21 import org.thingsboard.server.common.data.alarm.AlarmQuery;
21 import org.thingsboard.server.common.data.id.EntityId; 22 import org.thingsboard.server.common.data.id.EntityId;
22 import org.thingsboard.server.common.data.id.TenantId; 23 import org.thingsboard.server.common.data.id.TenantId;
@@ -37,5 +38,5 @@ public interface AlarmDao extends Dao<AlarmEntity> { @@ -37,5 +38,5 @@ public interface AlarmDao extends Dao<AlarmEntity> {
37 38
38 AlarmEntity save(Alarm alarm); 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,6 +17,7 @@ package org.thingsboard.server.dao.alarm;
17 17
18 import com.datastax.driver.core.querybuilder.QueryBuilder; 18 import com.datastax.driver.core.querybuilder.QueryBuilder;
19 import com.datastax.driver.core.querybuilder.Select; 19 import com.datastax.driver.core.querybuilder.Select;
  20 +import com.google.common.base.Function;
20 import com.google.common.util.concurrent.AsyncFunction; 21 import com.google.common.util.concurrent.AsyncFunction;
21 import com.google.common.util.concurrent.Futures; 22 import com.google.common.util.concurrent.Futures;
22 import com.google.common.util.concurrent.ListenableFuture; 23 import com.google.common.util.concurrent.ListenableFuture;
@@ -25,7 +26,9 @@ import org.springframework.beans.factory.annotation.Autowired; @@ -25,7 +26,9 @@ import org.springframework.beans.factory.annotation.Autowired;
25 import org.springframework.stereotype.Component; 26 import org.springframework.stereotype.Component;
26 import org.thingsboard.server.common.data.EntityType; 27 import org.thingsboard.server.common.data.EntityType;
27 import org.thingsboard.server.common.data.alarm.Alarm; 28 import org.thingsboard.server.common.data.alarm.Alarm;
  29 +import org.thingsboard.server.common.data.alarm.AlarmInfo;
28 import org.thingsboard.server.common.data.alarm.AlarmQuery; 30 import org.thingsboard.server.common.data.alarm.AlarmQuery;
  31 +import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
29 import org.thingsboard.server.common.data.id.EntityId; 32 import org.thingsboard.server.common.data.id.EntityId;
30 import org.thingsboard.server.common.data.id.TenantId; 33 import org.thingsboard.server.common.data.id.TenantId;
31 import org.thingsboard.server.common.data.relation.EntityRelation; 34 import org.thingsboard.server.common.data.relation.EntityRelation;
@@ -94,15 +97,25 @@ public class AlarmDaoImpl extends AbstractModelDao<AlarmEntity> implements Alarm @@ -94,15 +97,25 @@ public class AlarmDaoImpl extends AbstractModelDao<AlarmEntity> implements Alarm
94 } 97 }
95 98
96 @Override 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 EntityId affectedEntity = query.getAffectedEntityId(); 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 ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink()); 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 for (EntityRelation relation : input) { 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 return Futures.successfulAsList(alarmFutures); 120 return Futures.successfulAsList(alarmFutures);
108 }); 121 });
@@ -18,6 +18,7 @@ package org.thingsboard.server.dao.alarm; @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.alarm;
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.server.common.data.alarm.Alarm; 19 import org.thingsboard.server.common.data.alarm.Alarm;
20 import org.thingsboard.server.common.data.alarm.AlarmId; 20 import org.thingsboard.server.common.data.alarm.AlarmId;
  21 +import org.thingsboard.server.common.data.alarm.AlarmInfo;
21 import org.thingsboard.server.common.data.alarm.AlarmQuery; 22 import org.thingsboard.server.common.data.alarm.AlarmQuery;
22 import org.thingsboard.server.common.data.page.TimePageData; 23 import org.thingsboard.server.common.data.page.TimePageData;
23 24
@@ -34,6 +35,6 @@ public interface AlarmService { @@ -34,6 +35,6 @@ public interface AlarmService {
34 35
35 ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId); 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,22 +17,21 @@ package org.thingsboard.server.dao.alarm;
17 17
18 18
19 import com.google.common.base.Function; 19 import com.google.common.base.Function;
  20 +import com.google.common.util.concurrent.AsyncFunction;
20 import com.google.common.util.concurrent.Futures; 21 import com.google.common.util.concurrent.Futures;
21 import com.google.common.util.concurrent.ListenableFuture; 22 import com.google.common.util.concurrent.ListenableFuture;
22 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
23 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
25 import org.springframework.util.StringUtils; 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 import org.thingsboard.server.common.data.id.EntityId; 28 import org.thingsboard.server.common.data.id.EntityId;
31 import org.thingsboard.server.common.data.page.TimePageData; 29 import org.thingsboard.server.common.data.page.TimePageData;
32 import org.thingsboard.server.common.data.relation.EntityRelation; 30 import org.thingsboard.server.common.data.relation.EntityRelation;
33 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 31 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
34 import org.thingsboard.server.dao.entity.AbstractEntityService; 32 import org.thingsboard.server.dao.entity.AbstractEntityService;
35 import org.thingsboard.server.dao.entity.BaseEntityService; 33 import org.thingsboard.server.dao.entity.BaseEntityService;
  34 +import org.thingsboard.server.dao.entity.EntityService;
36 import org.thingsboard.server.dao.exception.DataValidationException; 35 import org.thingsboard.server.dao.exception.DataValidationException;
37 import org.thingsboard.server.dao.model.*; 36 import org.thingsboard.server.dao.model.*;
38 import org.thingsboard.server.dao.relation.EntityRelationsQuery; 37 import org.thingsboard.server.dao.relation.EntityRelationsQuery;
@@ -45,6 +44,7 @@ import org.thingsboard.server.dao.tenant.TenantDao; @@ -45,6 +44,7 @@ import org.thingsboard.server.dao.tenant.TenantDao;
45 import javax.annotation.Nullable; 44 import javax.annotation.Nullable;
46 import javax.annotation.PostConstruct; 45 import javax.annotation.PostConstruct;
47 import javax.annotation.PreDestroy; 46 import javax.annotation.PreDestroy;
  47 +import java.util.ArrayList;
48 import java.util.List; 48 import java.util.List;
49 import java.util.concurrent.ExecutionException; 49 import java.util.concurrent.ExecutionException;
50 import java.util.concurrent.ExecutorService; 50 import java.util.concurrent.ExecutorService;
@@ -59,7 +59,6 @@ import static org.thingsboard.server.dao.service.Validator.*; @@ -59,7 +59,6 @@ import static org.thingsboard.server.dao.service.Validator.*;
59 public class BaseAlarmService extends AbstractEntityService implements AlarmService { 59 public class BaseAlarmService extends AbstractEntityService implements AlarmService {
60 60
61 public static final String ALARM_RELATION_PREFIX = "ALARM_"; 61 public static final String ALARM_RELATION_PREFIX = "ALARM_";
62 - public static final String ALARM_RELATION = "ALARM_ANY";  
63 62
64 @Autowired 63 @Autowired
65 private AlarmDao alarmDao; 64 private AlarmDao alarmDao;
@@ -70,6 +69,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -70,6 +69,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
70 @Autowired 69 @Autowired
71 private RelationService relationService; 70 private RelationService relationService;
72 71
  72 + @Autowired
  73 + private EntityService entityService;
  74 +
73 protected ExecutorService readResultsProcessingExecutor; 75 protected ExecutorService readResultsProcessingExecutor;
74 76
75 @PostConstruct 77 @PostConstruct
@@ -116,11 +118,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -116,11 +118,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
116 query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE)); 118 query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
117 List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList()); 119 List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
118 for (EntityId parentId : parentEntities) { 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 return saved; 124 return saved;
125 } 125 }
126 126
@@ -199,12 +199,27 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -199,12 +199,27 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
199 } 199 }
200 200
201 @Override 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 @Nullable 220 @Nullable
206 @Override 221 @Override
207 - public TimePageData<Alarm> apply(@Nullable List<Alarm> alarms) { 222 + public TimePageData<AlarmInfo> apply(@Nullable List<AlarmInfo> alarms) {
208 return new TimePageData<>(alarms, query.getPageLink()); 223 return new TimePageData<>(alarms, query.getPageLink());
209 } 224 }
210 }); 225 });
@@ -245,17 +260,45 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @@ -245,17 +260,45 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
245 query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE)); 260 query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
246 List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList()); 261 List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
247 for (EntityId parentId : parentEntities) { 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 } catch (ExecutionException | InterruptedException e) { 266 } catch (ExecutionException | InterruptedException e) {
254 log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus); 267 log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus);
255 throw new RuntimeException(e); 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 private <T> ListenableFuture<T> getAndUpdate(AlarmId alarmId, Function<Alarm, T> function) { 302 private <T> ListenableFuture<T> getAndUpdate(AlarmId alarmId, Function<Alarm, T> function) {
260 validateId(alarmId, "Alarm id should be specified!"); 303 validateId(alarmId, "Alarm id should be specified!");
261 ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId()); 304 ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId());
@@ -178,9 +178,14 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao { @@ -178,9 +178,14 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao {
178 eq(ModelConstants.RELATION_TYPE_GROUP_PROPERTY, typeGroup.name()), 178 eq(ModelConstants.RELATION_TYPE_GROUP_PROPERTY, typeGroup.name()),
179 eq(ModelConstants.RELATION_TYPE_PROPERTY, relationType), 179 eq(ModelConstants.RELATION_TYPE_PROPERTY, relationType),
180 eq(ModelConstants.RELATION_TO_TYPE_PROPERTY, childType.name())), 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 pageLink, ModelConstants.RELATION_TO_ID_PROPERTY); 189 pageLink, ModelConstants.RELATION_TO_ID_PROPERTY);
185 return getFuture(executeAsyncRead(query), rs -> getEntityRelations(rs)); 190 return getFuture(executeAsyncRead(query), rs -> getEntityRelations(rs));
186 } 191 }
@@ -22,10 +22,7 @@ import org.junit.Before; @@ -22,10 +22,7 @@ import org.junit.Before;
22 import org.junit.Test; 22 import org.junit.Test;
23 import org.thingsboard.server.common.data.EntityType; 23 import org.thingsboard.server.common.data.EntityType;
24 import org.thingsboard.server.common.data.Tenant; 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 import org.thingsboard.server.common.data.id.AssetId; 26 import org.thingsboard.server.common.data.id.AssetId;
30 import org.thingsboard.server.common.data.id.DeviceId; 27 import org.thingsboard.server.common.data.id.DeviceId;
31 import org.thingsboard.server.common.data.id.TenantId; 28 import org.thingsboard.server.common.data.id.TenantId;
@@ -117,7 +114,7 @@ public class AlarmServiceTest extends AbstractServiceTest { @@ -117,7 +114,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
117 Alarm created = alarmService.createOrUpdateAlarm(alarm); 114 Alarm created = alarmService.createOrUpdateAlarm(alarm);
118 115
119 // Check child relation 116 // Check child relation
120 - TimePageData<Alarm> alarms = alarmService.findAlarms(AlarmQuery.builder() 117 + TimePageData<AlarmInfo> alarms = alarmService.findAlarms(AlarmQuery.builder()
121 .affectedEntityId(childId) 118 .affectedEntityId(childId)
122 .status(AlarmStatus.ACTIVE_UNACK).pageLink( 119 .status(AlarmStatus.ACTIVE_UNACK).pageLink(
123 new TimePageLink(1, 0L, System.currentTimeMillis(), false) 120 new TimePageLink(1, 0L, System.currentTimeMillis(), false)
@@ -91,7 +91,7 @@ function AlarmService($http, $q, $interval, $filter) { @@ -91,7 +91,7 @@ function AlarmService($http, $q, $interval, $filter) {
91 return deferred.promise; 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 var deferred = $q.defer(); 95 var deferred = $q.defer();
96 var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit; 96 var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit;
97 97
@@ -104,9 +104,15 @@ function AlarmService($http, $q, $interval, $filter) { @@ -104,9 +104,15 @@ function AlarmService($http, $q, $interval, $filter) {
104 if (angular.isDefined(pageLink.idOffset)) { 104 if (angular.isDefined(pageLink.idOffset)) {
105 url += '&offset=' + pageLink.idOffset; 105 url += '&offset=' + pageLink.idOffset;
106 } 106 }
  107 + if (alarmSearchStatus) {
  108 + url += '&searchStatus=' + alarmSearchStatus;
  109 + }
107 if (alarmStatus) { 110 if (alarmStatus) {
108 url += '&status=' + alarmStatus; 111 url += '&status=' + alarmStatus;
109 } 112 }
  113 + if (fetchOriginator) {
  114 + url += '&fetchOriginator=' + ((fetchOriginator===true) ? 'true' : 'false');
  115 + }
110 if (angular.isDefined(ascOrder) && ascOrder != null) { 116 if (angular.isDefined(ascOrder) && ascOrder != null) {
111 url += '&ascOrder=' + (ascOrder ? 'true' : 'false'); 117 url += '&ascOrder=' + (ascOrder ? 'true' : 'false');
112 } 118 }
@@ -121,7 +127,8 @@ function AlarmService($http, $q, $interval, $filter) { @@ -121,7 +127,8 @@ function AlarmService($http, $q, $interval, $filter) {
121 127
122 function fetchAlarms(alarmsQuery, pageLink, deferred, alarmsList) { 128 function fetchAlarms(alarmsQuery, pageLink, deferred, alarmsList) {
123 getAlarms(alarmsQuery.entityType, alarmsQuery.entityId, 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 function success(alarms) { 132 function success(alarms) {
126 if (!alarmsList) { 133 if (!alarmsList) {
127 alarmsList = []; 134 alarmsList = [];
@@ -171,7 +178,9 @@ function AlarmService($http, $q, $interval, $filter) { @@ -171,7 +178,9 @@ function AlarmService($http, $q, $interval, $filter) {
171 var alarmsQuery = { 178 var alarmsQuery = {
172 entityType: entityType, 179 entityType: entityType,
173 entityId: entityId, 180 entityId: entityId,
  181 + alarmSearchStatus: null,
174 alarmStatus: alarmStatus, 182 alarmStatus: alarmStatus,
  183 + fetchOriginator: false,
175 interval: interval, 184 interval: interval,
176 limit: limit, 185 limit: limit,
177 onAlarms: onAlarms 186 onAlarms: onAlarms
@@ -65,6 +65,13 @@ export default angular.module('thingsboard.types', []) @@ -65,6 +65,13 @@ export default angular.module('thingsboard.types', [])
65 clearedUnack: "CLEARED_UNACK", 65 clearedUnack: "CLEARED_UNACK",
66 clearedAck: "CLEARED_ACK" 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 aliasFilterType: { 75 aliasFilterType: {
69 entityList: { 76 entityList: {
70 value: 'entityList', 77 value: 'entityList',