Browse Source

Deprecation of the alarm status

pull/7942/head
Andrii Shvaika 4 years ago
parent
commit
feb84ace48
  1. 63
      application/src/main/data/upgrade/3.4.4/schema_update.sql
  2. 16
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  3. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java
  4. 4
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java
  5. 21
      application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java
  6. 2
      application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java
  7. 2
      application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java
  8. 1
      application/src/test/java/org/thingsboard/server/edge/BaseAlarmEdgeTest.java
  9. 3
      application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java
  10. 48
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
  11. 24
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSearchStatus.java
  12. 118
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatusFilter.java
  13. 3
      common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmData.java
  14. 3
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
  15. 24
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  16. 4
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  17. 24
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java
  18. 3
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java
  19. 43
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java
  20. 39
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  21. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java
  22. 122
      dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java
  23. 3
      dao/src/main/resources/sql/schema-entities-idx.sql
  24. 9
      dao/src/main/resources/sql/schema-entities.sql
  25. 2
      dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmCommentServiceTest.java
  26. 36
      dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java
  27. 1
      dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java
  28. 3
      dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java
  29. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java
  30. 6
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java
  31. 5
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java
  32. 32
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java

63
application/src/main/data/upgrade/3.4.4/schema_update.sql

@ -24,6 +24,31 @@ CREATE INDEX IF NOT EXISTS idx_alarm_tenant_assignee_created_time ON alarm(tenan
-- ALARM ASSIGN TO USER END -- ALARM ASSIGN TO USER END
-- ALARM STATUS REFACTORING START
ALTER TABLE alarm ADD COLUMN IF NOT EXISTS acknowledged boolean;
ALTER TABLE alarm ADD COLUMN IF NOT EXISTS cleared boolean;
UPDATE alarm SET acknowledged = true, cleared = true WHERE status = 'CLEARED_ACK';
UPDATE alarm SET acknowledged = true, cleared = false WHERE status = 'ACTIVE_ACK';
UPDATE alarm SET acknowledged = false, cleared = true WHERE status = 'CLEARED_UNACK';
UPDATE alarm SET acknowledged = false, cleared = false WHERE status = 'ACTIVE_UNACK';
-- Drop index by 'status' column and replace with new one that has only active alarms;
DROP INDEX IF EXISTS idx_alarm_tenant_status_created_time;
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_alarm_type_created_time_active
ON alarm USING btree (tenant_id, type, created_time DESC) WHERE cleared = false;
-- Cover index by alarm type to optimize propagated alarm queries;
CREATE INDEX IF NOT EXISTS idx_entity_alarm_entity_id_alarm_type_created_time_alarm_id ON entity_alarm
USING btree (tenant_id, entity_id, alarm_type, created_time DESC) INCLUDE(alarm_id);
ALTER TABLE alarm DROP COLUMN status;
-- ALARM STATUS REFACTORING END
-- ALARM COMMENTS START
CREATE TABLE IF NOT EXISTS alarm_comment ( CREATE TABLE IF NOT EXISTS alarm_comment (
id uuid NOT NULL, id uuid NOT NULL,
created_time bigint NOT NULL, created_time bigint NOT NULL,
@ -40,3 +65,41 @@ CREATE TABLE IF NOT EXISTS user_settings (
settings varchar(100000), settings varchar(100000),
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE
); );
-- ALARM COMMENTS END
-- ALARM INFO VIEW
DROP VIEW IF EXISTS alarm_info;
CREATE VIEW alarm_info AS
SELECT a.*,
(CASE WHEN a.acknowledged AND a.cleared THEN 'CLEARED_ACK'
WHEN NOT a.acknowledged AND a.cleared THEN 'CLEARED_UNACK'
WHEN a.acknowledged AND NOT a.cleared THEN 'ACTIVE_ACK'
WHEN NOT a.acknowledged AND NOT a.cleared THEN 'ACTIVE_UNACK' END) as status,
COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id)
WHEN a.originator_type = 1 THEN (select title from customer where id = a.originator_id)
WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id)
WHEN a.originator_type = 3 THEN (select title from dashboard where id = a.originator_id)
WHEN a.originator_type = 4 THEN (select name from asset where id = a.originator_id)
WHEN a.originator_type = 5 THEN (select name from device where id = a.originator_id)
WHEN a.originator_type = 9 THEN (select name from entity_view where id = a.originator_id)
WHEN a.originator_type = 13 THEN (select name from device_profile where id = a.originator_id)
WHEN a.originator_type = 14 THEN (select name from asset_profile where id = a.originator_id)
WHEN a.originator_type = 18 THEN (select name from edge where id = a.originator_id) END
, 'Deleted') originator_name,
COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id)
WHEN a.originator_type = 1 THEN (select COALESCE(title, email) from customer where id = a.originator_id)
WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id)
WHEN a.originator_type = 3 THEN (select title from dashboard where id = a.originator_id)
WHEN a.originator_type = 4 THEN (select COALESCE(label, name) from asset where id = a.originator_id)
WHEN a.originator_type = 5 THEN (select COALESCE(label, name) from device where id = a.originator_id)
WHEN a.originator_type = 9 THEN (select name from entity_view where id = a.originator_id)
WHEN a.originator_type = 13 THEN (select name from device_profile where id = a.originator_id)
WHEN a.originator_type = 14 THEN (select name from asset_profile where id = a.originator_id)
WHEN a.originator_type = 18 THEN (select COALESCE(label, name) from edge where id = a.originator_id) END
, 'Deleted') as originator_label,
u.first_name as assignee_first_name, u.last_name as assignee_last_name, u.email as assignee_email
FROM alarm a
LEFT JOIN tb_user u ON u.id = a.assignee_id;

16
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -102,12 +102,8 @@ public class AlarmController extends BaseController {
public Alarm getAlarmById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) public Alarm getAlarmById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId); checkParameter(ALARM_ID, strAlarmId);
try { AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); return checkAlarmId(alarmId, Operation.READ);
return checkAlarmId(alarmId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
} }
@ApiOperation(value = "Get Alarm Info (getAlarmInfoById)", @ApiOperation(value = "Get Alarm Info (getAlarmInfoById)",
@ -119,12 +115,8 @@ public class AlarmController extends BaseController {
public AlarmInfo getAlarmInfoById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) public AlarmInfo getAlarmInfoById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException { @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId); checkParameter(ALARM_ID, strAlarmId);
try { AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); return checkAlarmInfoId(alarmId, Operation.READ);
return checkAlarmInfoId(alarmId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
} }
@ApiOperation(value = "Create or update Alarm (saveAlarm)", @ApiOperation(value = "Create or update Alarm (saveAlarm)",

4
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java

@ -62,7 +62,9 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor {
existentAlarm.setClearTs(alarmUpdateMsg.getClearTs()); existentAlarm.setClearTs(alarmUpdateMsg.getClearTs());
existentAlarm.setPropagate(alarmUpdateMsg.getPropagate()); existentAlarm.setPropagate(alarmUpdateMsg.getPropagate());
} }
existentAlarm.setStatus(AlarmStatus.valueOf(alarmUpdateMsg.getStatus())); var alarmStatus = AlarmStatus.valueOf(alarmUpdateMsg.getStatus());
existentAlarm.setCleared(alarmStatus.isCleared());
existentAlarm.setAcknowledged(alarmStatus.isAck());
existentAlarm.setAckTs(alarmUpdateMsg.getAckTs()); existentAlarm.setAckTs(alarmUpdateMsg.getAckTs());
existentAlarm.setEndTs(alarmUpdateMsg.getEndTs()); existentAlarm.setEndTs(alarmUpdateMsg.getEndTs());
existentAlarm.setDetails(JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails())); existentAlarm.setDetails(JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()));

4
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java

@ -69,7 +69,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
.build(); .build();
alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment); alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment);
alarm.setAckTs(ackTs); alarm.setAckTs(ackTs);
alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK); alarm.setAcknowledged(true);
notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_ACK, user); notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_ACK, user);
return null; return null;
}, MoreExecutors.directExecutor()); }, MoreExecutors.directExecutor());
@ -89,7 +89,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
.build(); .build();
alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment); alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment);
alarm.setClearTs(clearTs); alarm.setClearTs(clearTs);
alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK); alarm.setCleared(true);
notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_CLEAR, user); notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_CLEAR, user);
return null; return null;
}, MoreExecutors.directExecutor()); }, MoreExecutors.directExecutor());

21
application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java

@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.Aggregation;
@ -269,8 +270,24 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx<AlarmDataQuery> {
if (filter.getStatusList() != null && !filter.getStatusList().isEmpty()) { if (filter.getStatusList() != null && !filter.getStatusList().isEmpty()) {
boolean matches = false; boolean matches = false;
for (AlarmSearchStatus status : filter.getStatusList()) { for (AlarmSearchStatus status : filter.getStatusList()) {
if (status.getStatuses().contains(alarm.getStatus())) { switch (status) {
matches = true; case ANY:
matches = true;
break;
case ACK:
matches = alarm.isAcknowledged();
break;
case UNACK:
matches = !alarm.isAcknowledged();
break;
case CLEARED:
matches = alarm.isCleared();
break;
case ACTIVE:
matches = !alarm.isCleared();
break;
}
if (matches) {
break; break;
} }
} }

2
application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java

@ -79,7 +79,6 @@ public abstract class BaseAlarmCommentControllerTest extends AbstractControllerT
.tenantId(tenantId) .tenantId(tenantId)
.customerId(customerId) .customerId(customerId)
.originator(customerDevice.getId()) .originator(customerDevice.getId())
.status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL) .severity(AlarmSeverity.CRITICAL)
.type("test alarm type") .type("test alarm type")
.build(); .build();
@ -316,7 +315,6 @@ public abstract class BaseAlarmCommentControllerTest extends AbstractControllerT
Alarm alarm = Alarm.builder() Alarm alarm = Alarm.builder()
.originator(device.getId()) .originator(device.getId())
.status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL) .severity(AlarmSeverity.CRITICAL)
.type("Test") .type("Test")
.build(); .build();

2
application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java

@ -536,7 +536,6 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
Alarm alarm = Alarm.builder() Alarm alarm = Alarm.builder()
.originator(device.getId()) .originator(device.getId())
.status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL) .severity(AlarmSeverity.CRITICAL)
.type("Test") .type("Test")
.build(); .build();
@ -586,7 +585,6 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
.tenantId(tenantId) .tenantId(tenantId)
.customerId(customerId) .customerId(customerId)
.originator(customerDevice.getId()) .originator(customerDevice.getId())
.status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL) .severity(AlarmSeverity.CRITICAL)
.type(type) .type(type)
.build(); .build();

1
application/src/test/java/org/thingsboard/server/edge/BaseAlarmEdgeTest.java

@ -76,7 +76,6 @@ abstract public class BaseAlarmEdgeTest extends AbstractEdgeTest {
Device device = findDeviceByName("Edge Device 1"); Device device = findDeviceByName("Edge Device 1");
Alarm alarm = new Alarm(); Alarm alarm = new Alarm();
alarm.setOriginator(device.getId()); alarm.setOriginator(device.getId());
alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
alarm.setType("alarm"); alarm.setType("alarm");
alarm.setSeverity(AlarmSeverity.CRITICAL); alarm.setSeverity(AlarmSeverity.CRITICAL);
edgeImitator.expectMessageAmount(1); edgeImitator.expectMessageAmount(1);

3
application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java

@ -93,7 +93,6 @@ public class DefaultTbAlarmServiceTest {
@Test @Test
public void testAck() { public void testAck() {
var alarm = new Alarm(); var alarm = new Alarm();
alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
when(alarmSubscriptionService.ackAlarm(any(), any(), anyLong())).thenReturn(Futures.immediateFuture(true)); when(alarmSubscriptionService.ackAlarm(any(), any(), anyLong())).thenReturn(Futures.immediateFuture(true));
service.ack(alarm, new User(new UserId(UUID.randomUUID()))); service.ack(alarm, new User(new UserId(UUID.randomUUID())));
@ -105,7 +104,7 @@ public class DefaultTbAlarmServiceTest {
@Test @Test
public void testClear() { public void testClear() {
var alarm = new Alarm(); var alarm = new Alarm();
alarm.setStatus(AlarmStatus.ACTIVE_ACK); alarm.setAcknowledged(true);
when(alarmSubscriptionService.clearAlarm(any(), any(), any(), anyLong())).thenReturn(Futures.immediateFuture(true)); when(alarmSubscriptionService.clearAlarm(any(), any(), any(), anyLong())).thenReturn(Futures.immediateFuture(true));
service.clear(alarm, new User(new UserId(UUID.randomUUID()))); service.clear(alarm, new User(new UserId(UUID.randomUUID())));

48
common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java

@ -63,29 +63,31 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
private EntityId originator; private EntityId originator;
@ApiModelProperty(position = 8, required = true, value = "Alarm severity", example = "CRITICAL") @ApiModelProperty(position = 8, required = true, value = "Alarm severity", example = "CRITICAL")
private AlarmSeverity severity; private AlarmSeverity severity;
@ApiModelProperty(position = 9, required = true, value = "Alarm status", example = "CLEARED_UNACK") @ApiModelProperty(position = 9, required = true, value = "Acknowledged", example = "true")
private AlarmStatus status; private boolean acknowledged;
@ApiModelProperty(position = 10, value = "Alarm assignee user id") @ApiModelProperty(position = 10, required = true, value = "Cleared", example = "false")
private boolean cleared;
@ApiModelProperty(position = 11, value = "Alarm assignee user id")
private UserId assigneeId; private UserId assigneeId;
@ApiModelProperty(position = 11, value = "Timestamp of the alarm start time, in milliseconds", example = "1634058704565") @ApiModelProperty(position = 12, value = "Timestamp of the alarm start time, in milliseconds", example = "1634058704565")
private long startTs; private long startTs;
@ApiModelProperty(position = 12, value = "Timestamp of the alarm end time(last time update), in milliseconds", example = "1634111163522") @ApiModelProperty(position = 13, value = "Timestamp of the alarm end time(last time update), in milliseconds", example = "1634111163522")
private long endTs; private long endTs;
@ApiModelProperty(position = 13, value = "Timestamp of the alarm acknowledgement, in milliseconds", example = "1634115221948") @ApiModelProperty(position = 14, value = "Timestamp of the alarm acknowledgement, in milliseconds", example = "1634115221948")
private long ackTs; private long ackTs;
@ApiModelProperty(position = 14, value = "Timestamp of the alarm clearing, in milliseconds", example = "1634114528465") @ApiModelProperty(position = 15, value = "Timestamp of the alarm clearing, in milliseconds", example = "1634114528465")
private long clearTs; private long clearTs;
@ApiModelProperty(position = 15, value = "Timestamp of the alarm assignment, in milliseconds", example = "1634115928465") @ApiModelProperty(position = 16, value = "Timestamp of the alarm assignment, in milliseconds", example = "1634115928465")
private long assignTs; private long assignTs;
@ApiModelProperty(position = 16, value = "JSON object with alarm details") @ApiModelProperty(position = 17, value = "JSON object with alarm details")
private transient JsonNode details; private transient JsonNode details;
@ApiModelProperty(position = 17, value = "Propagation flag to specify if alarm should be propagated to parent entities of alarm originator", example = "true") @ApiModelProperty(position = 18, value = "Propagation flag to specify if alarm should be propagated to parent entities of alarm originator", example = "true")
private boolean propagate; private boolean propagate;
@ApiModelProperty(position = 18, value = "Propagation flag to specify if alarm should be propagated to the owner (tenant or customer) of alarm originator", example = "true") @ApiModelProperty(position = 19, value = "Propagation flag to specify if alarm should be propagated to the owner (tenant or customer) of alarm originator", example = "true")
private boolean propagateToOwner; private boolean propagateToOwner;
@ApiModelProperty(position = 19, value = "Propagation flag to specify if alarm should be propagated to the tenant entity", example = "true") @ApiModelProperty(position = 20, value = "Propagation flag to specify if alarm should be propagated to the tenant entity", example = "true")
private boolean propagateToTenant; private boolean propagateToTenant;
@ApiModelProperty(position = 20, value = "JSON array of relation types that should be used for propagation. " + @ApiModelProperty(position = 21, value = "JSON array of relation types that should be used for propagation. " +
"By default, 'propagateRelationTypes' array is empty which means that the alarm will be propagated based on any relation type to parent entities. " + "By default, 'propagateRelationTypes' array is empty which means that the alarm will be propagated based on any relation type to parent entities. " +
"This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will be ignored.") "This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will be ignored.")
private List<String> propagateRelationTypes; private List<String> propagateRelationTypes;
@ -106,12 +108,13 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
this.type = alarm.getType(); this.type = alarm.getType();
this.originator = alarm.getOriginator(); this.originator = alarm.getOriginator();
this.severity = alarm.getSeverity(); this.severity = alarm.getSeverity();
this.status = alarm.getStatus();
this.assigneeId = alarm.getAssigneeId(); this.assigneeId = alarm.getAssigneeId();
this.startTs = alarm.getStartTs(); this.startTs = alarm.getStartTs();
this.endTs = alarm.getEndTs(); this.endTs = alarm.getEndTs();
this.acknowledged = alarm.isAcknowledged();
this.ackTs = alarm.getAckTs(); this.ackTs = alarm.getAckTs();
this.clearTs = alarm.getClearTs(); this.clearTs = alarm.getClearTs();
this.cleared = alarm.isCleared();
this.assignTs = alarm.getAssignTs(); this.assignTs = alarm.getAssignTs();
this.details = alarm.getDetails(); this.details = alarm.getDetails();
this.propagate = alarm.isPropagate(); this.propagate = alarm.isPropagate();
@ -130,7 +133,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
@ApiModelProperty(position = 1, value = "JSON object with the alarm Id. " + @ApiModelProperty(position = 1, value = "JSON object with the alarm Id. " +
"Specify this field to update the alarm. " + "Specify this field to update the alarm. " +
"Referencing non-existing alarm Id will cause error. " + "Referencing non-existing alarm Id will cause error. " +
"Omit this field to create new alarm." ) "Omit this field to create new alarm.")
@Override @Override
public AlarmId getId() { public AlarmId getId() {
return super.getId(); return super.getId();
@ -143,4 +146,19 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
return super.getCreatedTime(); return super.getCreatedTime();
} }
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@ApiModelProperty(position = 22, required = true, value = "status of the Alarm", example = "ACTIVE_UNACK", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
public AlarmStatus getStatus() {
return toStatus(cleared, acknowledged);
}
public static AlarmStatus toStatus(boolean cleared, boolean acknowledged) {
if (cleared) {
return acknowledged ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK;
} else {
return acknowledged ? AlarmStatus.ACTIVE_ACK : AlarmStatus.ACTIVE_UNACK;
}
}
} }

24
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSearchStatus.java

@ -15,26 +15,12 @@
*/ */
package org.thingsboard.server.common.data.alarm; package org.thingsboard.server.common.data.alarm;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
public enum AlarmSearchStatus { public enum AlarmSearchStatus {
ANY(AlarmStatus.values()), ANY,
ACTIVE(AlarmStatus.ACTIVE_ACK, AlarmStatus.ACTIVE_UNACK), ACTIVE,
CLEARED(AlarmStatus.CLEARED_ACK, AlarmStatus.CLEARED_UNACK), CLEARED,
ACK(AlarmStatus.ACTIVE_ACK, AlarmStatus.CLEARED_ACK), ACK,
UNACK(AlarmStatus.ACTIVE_UNACK, AlarmStatus.CLEARED_UNACK); UNACK;
@JsonIgnore
@Getter
private Set<AlarmStatus> statuses;
AlarmSearchStatus(AlarmStatus... statuses) {
this.statuses = new LinkedHashSet<>(Arrays.asList(statuses));
}
} }

118
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatusFilter.java

@ -0,0 +1,118 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.alarm;
import java.util.List;
import java.util.Optional;
public class AlarmStatusFilter {
private static final AlarmStatusFilter EMPTY = new AlarmStatusFilter(Optional.empty(), Optional.empty());
private final Optional<Boolean> clearFilter;
private final Optional<Boolean> ackFilter;
private AlarmStatusFilter(Optional<Boolean> clearFilter, Optional<Boolean> ackFilter) {
this.clearFilter = clearFilter;
this.ackFilter = ackFilter;
}
public static AlarmStatusFilter from(AlarmQuery query) {
if (query.getSearchStatus() != null) {
return AlarmStatusFilter.from(query.getSearchStatus());
} else if (query.getStatus() != null) {
return AlarmStatusFilter.from(query.getStatus());
}
return AlarmStatusFilter.empty();
}
public static AlarmStatusFilter from(AlarmSearchStatus alarmSearchStatus) {
switch (alarmSearchStatus) {
case ACK:
return new AlarmStatusFilter(Optional.empty(), Optional.of(true));
case UNACK:
return new AlarmStatusFilter(Optional.empty(), Optional.of(false));
case ACTIVE:
return new AlarmStatusFilter(Optional.of(false), Optional.empty());
case CLEARED:
return new AlarmStatusFilter(Optional.of(true), Optional.empty());
default:
return EMPTY;
}
}
public static AlarmStatusFilter from(AlarmStatus alarmStatus) {
switch (alarmStatus) {
case ACTIVE_UNACK:
return new AlarmStatusFilter(Optional.of(false), Optional.of(false));
case ACTIVE_ACK:
return new AlarmStatusFilter(Optional.of(false), Optional.of(true));
case CLEARED_UNACK:
return new AlarmStatusFilter(Optional.of(true), Optional.of(false));
case CLEARED_ACK:
return new AlarmStatusFilter(Optional.of(true), Optional.of(true));
default:
return EMPTY;
}
}
public static AlarmStatusFilter empty() {
return EMPTY;
}
public boolean hasAnyFilter() {
return clearFilter.isPresent() || ackFilter.isPresent();
}
public boolean hasClearFilter() {
return clearFilter.isPresent();
}
public boolean hasAckFilter() {
return ackFilter.isPresent();
}
public boolean getClearFilter() {
return clearFilter.orElseThrow(() -> new RuntimeException("Clear filter is not set! Use `hasClearFilter` to check."));
}
public boolean getAckFilter() {
return ackFilter.orElseThrow(() -> new RuntimeException("Ack filter is not set! Use `hasAckFilter` to check."));
}
public static AlarmStatusFilter fromList(List<AlarmSearchStatus> list) {
if (list == null || list.isEmpty() || list.contains(AlarmSearchStatus.ANY)) {
return EMPTY;
}
boolean clearFilter = list.contains(AlarmSearchStatus.CLEARED);
boolean activeFilter = list.contains(AlarmSearchStatus.ACTIVE);
Optional<Boolean> clear = Optional.empty();
if (clearFilter && !activeFilter || !clearFilter && activeFilter) {
clear = Optional.of(clearFilter);
}
boolean ackFilter = list.contains(AlarmSearchStatus.ACK);
boolean unackFilter = list.contains(AlarmSearchStatus.UNACK);
Optional<Boolean> ack = Optional.empty();
if (ackFilter && !unackFilter || !ackFilter && unackFilter) {
ack = Optional.of(ackFilter);
}
return new AlarmStatusFilter(clear, ack);
}
}

3
common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmData.java

@ -39,7 +39,8 @@ public class AlarmData extends AlarmInfo {
public AlarmData update(Alarm alarm, AlarmAssigneeUpdate assigneeUpdate) { public AlarmData update(Alarm alarm, AlarmAssigneeUpdate assigneeUpdate) {
this.setEndTs(alarm.getEndTs()); this.setEndTs(alarm.getEndTs());
this.setSeverity(alarm.getSeverity()); this.setSeverity(alarm.getSeverity());
this.setStatus(alarm.getStatus()); this.setAcknowledged(alarm.isAcknowledged());
this.setCleared(alarm.isCleared());
this.setDetails(alarm.getDetails()); this.setDetails(alarm.getDetails());
this.setPropagate(alarm.isPropagate()); this.setPropagate(alarm.isPropagate());
this.setPropagateToOwner(alarm.isPropagateToOwner()); this.setPropagateToOwner(alarm.isPropagateToOwner());

3
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java

@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
import org.thingsboard.server.common.data.alarm.EntityAlarm; import org.thingsboard.server.common.data.alarm.EntityAlarm;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
@ -61,7 +62,7 @@ public interface AlarmDao extends Dao<Alarm> {
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds); PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, Set<AlarmStatus> status, String assigneeId); Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, AlarmStatusFilter asf, String assigneeId);
PageData<AlarmId> findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); PageData<AlarmId> findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink);

24
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmAssignee; import org.thingsboard.server.common.data.alarm.AlarmAssignee;
import org.thingsboard.server.common.data.alarm.AlarmAssigneeUpdate; import org.thingsboard.server.common.data.alarm.AlarmAssigneeUpdate;
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmQuery;
@ -123,6 +124,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} }
alarm.setCustomerId(entityService.fetchEntityCustomerId(alarm.getTenantId(), alarm.getOriginator()).orElse(null)); alarm.setCustomerId(entityService.fetchEntityCustomerId(alarm.getTenantId(), alarm.getOriginator()).orElse(null));
if (alarm.getId() == null) { if (alarm.getId() == null) {
// Atomic update and return alarm + assignee.
Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()); Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType());
if (existing == null || existing.getStatus().isCleared()) { if (existing == null || existing.getStatus().isCleared()) {
if (!alarmCreationEnabled) { if (!alarmCreationEnabled) {
@ -235,9 +237,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (alarm == null || alarm.getStatus().isAck()) { if (alarm == null || alarm.getStatus().isAck()) {
return Futures.immediateFuture(new AlarmOperationResult(alarm, false)); return Futures.immediateFuture(new AlarmOperationResult(alarm, false));
} else { } else {
AlarmStatus oldStatus = alarm.getStatus(); alarm.setAcknowledged(true);
AlarmStatus newStatus = oldStatus.isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK;
alarm.setStatus(newStatus);
alarm.setAckTs(ackTime); alarm.setAckTs(ackTime);
alarm = alarmDao.save(alarm.getTenantId(), alarm); alarm = alarmDao.save(alarm.getTenantId(), alarm);
return Futures.immediateFuture(new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)))); return Futures.immediateFuture(new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))));
@ -250,9 +250,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (alarm == null || alarm.getStatus().isCleared()) { if (alarm == null || alarm.getStatus().isCleared()) {
return Futures.immediateFuture(new AlarmOperationResult(alarm, false)); return Futures.immediateFuture(new AlarmOperationResult(alarm, false));
} else { } else {
AlarmStatus oldStatus = alarm.getStatus(); alarm.setCleared(true);
AlarmStatus newStatus = oldStatus.isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK;
alarm.setStatus(newStatus);
alarm.setClearTs(clearTime); alarm.setClearTs(clearTime);
if (details != null) { if (details != null) {
alarm.setDetails(details); alarm.setDetails(details);
@ -336,15 +334,16 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
@Override @Override
public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
AlarmStatus alarmStatus, String assigneeId) { AlarmStatus alarmStatus, String assigneeId) {
Set<AlarmStatus> statusList = null; AlarmStatusFilter asf;
if (alarmSearchStatus != null) { if (alarmSearchStatus != null) {
statusList = alarmSearchStatus.getStatuses(); asf = AlarmStatusFilter.from(alarmSearchStatus);
} else if (alarmStatus != null) { } else if (alarmStatus != null) {
statusList = Collections.singleton(alarmStatus); asf = AlarmStatusFilter.from(alarmStatus);
} else {
asf= AlarmStatusFilter.empty();
} }
Set<AlarmSeverity> alarmSeverities = alarmDao.findAlarmSeverities(tenantId, entityId, statusList, assigneeId); Set<AlarmSeverity> alarmSeverities = alarmDao.findAlarmSeverities(tenantId, entityId, asf, assigneeId);
return alarmSeverities.stream().min(AlarmSeverity::compareTo).orElse(null); return alarmSeverities.stream().min(AlarmSeverity::compareTo).orElse(null);
} }
@ -369,7 +368,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (alarm.getAssignTs() > existing.getAssignTs()) { if (alarm.getAssignTs() > existing.getAssignTs()) {
existing.setAssignTs(alarm.getAssignTs()); existing.setAssignTs(alarm.getAssignTs());
} }
existing.setStatus(alarm.getStatus()); existing.setAcknowledged(alarm.isAcknowledged());
existing.setCleared(alarm.isCleared());
existing.setSeverity(alarm.getSeverity()); existing.setSeverity(alarm.getSeverity());
existing.setDetails(alarm.getDetails()); existing.setDetails(alarm.getDetails());
existing.setCustomerId(alarm.getCustomerId()); existing.setCustomerId(alarm.getCustomerId());

4
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -292,19 +292,21 @@ public class ModelConstants {
public static final String ALARM_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; public static final String ALARM_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
public static final String ALARM_TYPE_PROPERTY = "type"; public static final String ALARM_TYPE_PROPERTY = "type";
public static final String ALARM_DETAILS_PROPERTY = "details"; public static final String ALARM_DETAILS_PROPERTY = "details";
public static final String ALARM_STATUS_PROPERTY = "status";
public static final String ALARM_ORIGINATOR_ID_PROPERTY = "originator_id"; public static final String ALARM_ORIGINATOR_ID_PROPERTY = "originator_id";
public static final String ALARM_ORIGINATOR_NAME_PROPERTY = "originator_name"; public static final String ALARM_ORIGINATOR_NAME_PROPERTY = "originator_name";
public static final String ALARM_ORIGINATOR_LABEL_PROPERTY = "originator_label"; public static final String ALARM_ORIGINATOR_LABEL_PROPERTY = "originator_label";
public static final String ALARM_ORIGINATOR_TYPE_PROPERTY = "originator_type"; public static final String ALARM_ORIGINATOR_TYPE_PROPERTY = "originator_type";
public static final String ALARM_SEVERITY_PROPERTY = "severity"; public static final String ALARM_SEVERITY_PROPERTY = "severity";
public static final String ALARM_STATUS_PROPERTY = "status";
public static final String ALARM_ASSIGNEE_ID_PROPERTY = "assignee_id"; public static final String ALARM_ASSIGNEE_ID_PROPERTY = "assignee_id";
public static final String ALARM_ASSIGNEE_FIRST_NAME_PROPERTY = "assignee_first_name"; public static final String ALARM_ASSIGNEE_FIRST_NAME_PROPERTY = "assignee_first_name";
public static final String ALARM_ASSIGNEE_LAST_NAME_PROPERTY = "assignee_last_name"; public static final String ALARM_ASSIGNEE_LAST_NAME_PROPERTY = "assignee_last_name";
public static final String ALARM_ASSIGNEE_EMAIL_PROPERTY = "assignee_email"; public static final String ALARM_ASSIGNEE_EMAIL_PROPERTY = "assignee_email";
public static final String ALARM_START_TS_PROPERTY = "start_ts"; public static final String ALARM_START_TS_PROPERTY = "start_ts";
public static final String ALARM_END_TS_PROPERTY = "end_ts"; public static final String ALARM_END_TS_PROPERTY = "end_ts";
public static final String ALARM_ACKNOWLEDGED_PROPERTY = "acknowledged";
public static final String ALARM_ACK_TS_PROPERTY = "ack_ts"; public static final String ALARM_ACK_TS_PROPERTY = "ack_ts";
public static final String ALARM_CLEARED_PROPERTY = "cleared";
public static final String ALARM_CLEAR_TS_PROPERTY = "clear_ts"; public static final String ALARM_CLEAR_TS_PROPERTY = "clear_ts";
public static final String ALARM_ASSIGN_TS_PROPERTY = "assign_ts"; public static final String ALARM_ASSIGN_TS_PROPERTY = "assign_ts";
public static final String ALARM_PROPAGATE_PROPERTY = "propagate"; public static final String ALARM_PROPAGATE_PROPERTY = "propagate";

24
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmEntity.java

@ -20,13 +20,11 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.springframework.data.annotation.Id;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -45,9 +43,11 @@ import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACKNOWLEDGED_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGN_TS_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGN_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEARED_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CUSTOMER_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CUSTOMER_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPERTY;
@ -59,7 +59,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_TO
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_TO_TENANT_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_TO_TENANT_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_SEVERITY_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_SEVERITY_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_START_TS_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_START_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_STATUS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TENANT_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TYPE_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_TYPE_PROPERTY;
@ -88,10 +87,6 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
@Column(name = ALARM_SEVERITY_PROPERTY) @Column(name = ALARM_SEVERITY_PROPERTY)
private AlarmSeverity severity; private AlarmSeverity severity;
@Enumerated(EnumType.STRING)
@Column(name = ALARM_STATUS_PROPERTY)
private AlarmStatus status;
@Type(type="pg-uuid") @Type(type="pg-uuid")
@Column(name = ALARM_ASSIGNEE_ID_PROPERTY) @Column(name = ALARM_ASSIGNEE_ID_PROPERTY)
private UUID assigneeId; private UUID assigneeId;
@ -102,9 +97,15 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
@Column(name = ALARM_END_TS_PROPERTY) @Column(name = ALARM_END_TS_PROPERTY)
private Long endTs; private Long endTs;
@Column(name = ALARM_ACKNOWLEDGED_PROPERTY)
private boolean acknowledged;
@Column(name = ALARM_ACK_TS_PROPERTY) @Column(name = ALARM_ACK_TS_PROPERTY)
private Long ackTs; private Long ackTs;
@Column(name = ALARM_CLEARED_PROPERTY)
private boolean cleared;
@Column(name = ALARM_CLEAR_TS_PROPERTY) @Column(name = ALARM_CLEAR_TS_PROPERTY)
private Long clearTs; private Long clearTs;
@ -147,7 +148,8 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
this.originatorType = alarm.getOriginator().getEntityType(); this.originatorType = alarm.getOriginator().getEntityType();
this.type = alarm.getType(); this.type = alarm.getType();
this.severity = alarm.getSeverity(); this.severity = alarm.getSeverity();
this.status = alarm.getStatus(); this.acknowledged = alarm.isAcknowledged();
this.cleared = alarm.isCleared();
if (alarm.getAssigneeId() != null) { if (alarm.getAssigneeId() != null) {
this.assigneeId = alarm.getAssigneeId().getId(); this.assigneeId = alarm.getAssigneeId().getId();
} }
@ -177,7 +179,8 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
this.originatorType = alarmEntity.getOriginatorType(); this.originatorType = alarmEntity.getOriginatorType();
this.type = alarmEntity.getType(); this.type = alarmEntity.getType();
this.severity = alarmEntity.getSeverity(); this.severity = alarmEntity.getSeverity();
this.status = alarmEntity.getStatus(); this.acknowledged = alarmEntity.isAcknowledged();
this.cleared = alarmEntity.isCleared();
this.assigneeId = alarmEntity.getAssigneeId(); this.assigneeId = alarmEntity.getAssigneeId();
this.propagate = alarmEntity.getPropagate(); this.propagate = alarmEntity.getPropagate();
this.propagateToOwner = alarmEntity.getPropagateToOwner(); this.propagateToOwner = alarmEntity.getPropagateToOwner();
@ -203,7 +206,8 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, originatorId)); alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, originatorId));
alarm.setType(type); alarm.setType(type);
alarm.setSeverity(severity); alarm.setSeverity(severity);
alarm.setStatus(status); alarm.setAcknowledged(acknowledged);
alarm.setCleared(cleared);
if (assigneeId != null) { if (assigneeId != null) {
alarm.setAssigneeId(new UserId(assigneeId)); alarm.setAssigneeId(new UserId(assigneeId));
} }

3
dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java

@ -30,6 +30,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_FIR
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_LAST_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_LAST_NAME_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_LABEL_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_LABEL_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_NAME_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_STATUS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_VIEW_NAME; import static org.thingsboard.server.dao.model.ModelConstants.ALARM_VIEW_NAME;
@Data @Data
@ -48,6 +49,8 @@ public class AlarmInfoEntity extends AbstractAlarmEntity<AlarmInfo> {
private String assigneeLastName; private String assigneeLastName;
@Column(name = ALARM_ASSIGNEE_EMAIL_PROPERTY) @Column(name = ALARM_ASSIGNEE_EMAIL_PROPERTY)
private String assigneeEmail; private String assigneeEmail;
@Column(name = ALARM_STATUS_PROPERTY)
private String status;
public AlarmInfoEntity() { public AlarmInfoEntity() {
super(); super();

43
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java

@ -48,7 +48,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"AND ea.entityType = :affectedEntityType " + "AND ea.entityType = :affectedEntityType " +
"AND (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " + "AND (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " +
"AND (:endTime IS NULL OR (a.createdTime <= :endTime AND ea.createdTime <= :endTime)) " + "AND (:endTime IS NULL OR (a.createdTime <= :endTime AND ea.createdTime <= :endTime)) " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " +
"AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
@ -64,7 +65,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"AND ea.entityType = :affectedEntityType " + "AND ea.entityType = :affectedEntityType " +
"AND (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " + "AND (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " +
"AND (:endTime IS NULL OR (a.createdTime <= :endTime AND ea.createdTime <= :endTime)) " + "AND (:endTime IS NULL OR (a.createdTime <= :endTime AND ea.createdTime <= :endTime)) " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " +
"AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
@ -74,7 +76,10 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
@Param("affectedEntityType") String affectedEntityType, @Param("affectedEntityType") String affectedEntityType,
@Param("startTime") Long startTime, @Param("startTime") Long startTime,
@Param("endTime") Long endTime, @Param("endTime") Long endTime,
@Param("alarmStatuses") Set<AlarmStatus> alarmStatuses, @Param("clearFilterEnabled") boolean clearFilterEnabled,
@Param("clearFilter") boolean clearFilter,
@Param("ackFilterEnabled") boolean ackFilterEnabled,
@Param("ackFilter") boolean ackFilter,
@Param("assigneeId") String assigneeId, @Param("assigneeId") String assigneeId,
@Param("searchText") String searchText, @Param("searchText") String searchText,
Pageable pageable); Pageable pageable);
@ -84,18 +89,20 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"WHERE a.tenantId = :tenantId " + "WHERE a.tenantId = :tenantId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " +
"AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ", " OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ",
countQuery = "" + countQuery = "" +
"SELECT count(a) " + "SELECT count(a) " +
"FROM AlarmEntity a " + "FROM AlarmInfoEntity a " +
"WHERE a.tenantId = :tenantId " + "WHERE a.tenantId = :tenantId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " +
"AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
@ -103,7 +110,10 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
Page<AlarmInfoEntity> findAllAlarms(@Param("tenantId") UUID tenantId, Page<AlarmInfoEntity> findAllAlarms(@Param("tenantId") UUID tenantId,
@Param("startTime") Long startTime, @Param("startTime") Long startTime,
@Param("endTime") Long endTime, @Param("endTime") Long endTime,
@Param("alarmStatuses") Set<AlarmStatus> alarmStatuses, @Param("clearFilterEnabled") boolean clearFilterEnabled,
@Param("clearFilter") boolean clearFilter,
@Param("ackFilterEnabled") boolean ackFilterEnabled,
@Param("ackFilter") boolean ackFilter,
@Param("assigneeId") String assigneeId, @Param("assigneeId") String assigneeId,
@Param("searchText") String searchText, @Param("searchText") String searchText,
Pageable pageable); Pageable pageable);
@ -113,7 +123,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"WHERE a.tenantId = :tenantId AND a.customerId = :customerId " + "WHERE a.tenantId = :tenantId AND a.customerId = :customerId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " +
"AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
@ -125,7 +136,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"WHERE a.tenantId = :tenantId AND a.customerId = :customerId " + "WHERE a.tenantId = :tenantId AND a.customerId = :customerId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " + "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId)) " +
"AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " + "AND (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " + " OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
@ -134,7 +146,10 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
@Param("customerId") UUID customerId, @Param("customerId") UUID customerId,
@Param("startTime") Long startTime, @Param("startTime") Long startTime,
@Param("endTime") Long endTime, @Param("endTime") Long endTime,
@Param("alarmStatuses") Set<AlarmStatus> alarmStatuses, @Param("clearFilterEnabled") boolean clearFilterEnabled,
@Param("clearFilter") boolean clearFilter,
@Param("ackFilterEnabled") boolean ackFilterEnabled,
@Param("ackFilter") boolean ackFilter,
@Param("assigneeId") String assigneeId, @Param("assigneeId") String assigneeId,
@Param("searchText") String searchText, @Param("searchText") String searchText,
Pageable pageable); Pageable pageable);
@ -145,12 +160,16 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"AND ea.tenantId = :tenantId " + "AND ea.tenantId = :tenantId " +
"AND ea.entityId = :affectedEntityId " + "AND ea.entityId = :affectedEntityId " +
"AND ea.entityType = :affectedEntityType " + "AND ea.entityType = :affectedEntityType " +
"AND ((:alarmStatuses) IS NULL OR a.status in (:alarmStatuses)) " + "AND ((:clearFilterEnabled) IS FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) IS FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId))") "AND (:assigneeId IS NULL OR a.assigneeId = uuid(:assigneeId))")
Set<AlarmSeverity> findAlarmSeverities(@Param("tenantId") UUID tenantId, Set<AlarmSeverity> findAlarmSeverities(@Param("tenantId") UUID tenantId,
@Param("affectedEntityId") UUID affectedEntityId, @Param("affectedEntityId") UUID affectedEntityId,
@Param("affectedEntityType") String affectedEntityType, @Param("affectedEntityType") String affectedEntityType,
@Param("alarmStatuses") Set<AlarmStatus> alarmStatuses, @Param("clearFilterEnabled") boolean clearFilterEnabled,
@Param("clearFilter") boolean clearFilter,
@Param("ackFilterEnabled") boolean ackFilterEnabled,
@Param("ackFilter") boolean ackFilter,
@Param("assigneeId") String assigneeId); @Param("assigneeId") String assigneeId);
@Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.createdTime < :time AND a.endTs < :time") @Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.createdTime < :time AND a.endTs < :time")

39
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java

@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
import org.thingsboard.server.common.data.alarm.EntityAlarm; import org.thingsboard.server.common.data.alarm.EntityAlarm;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
@ -114,12 +115,7 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
public PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query) { public PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query) {
log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink()); log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink());
EntityId affectedEntity = query.getAffectedEntityId(); EntityId affectedEntity = query.getAffectedEntityId();
Set<AlarmStatus> statusSet = null; AlarmStatusFilter asf = AlarmStatusFilter.from(query);
if (query.getSearchStatus() != null) {
statusSet = query.getSearchStatus().getStatuses();
} else if (query.getStatus() != null) {
statusSet = Collections.singleton(query.getStatus());
}
String assigneeId = null; String assigneeId = null;
if (query.getAssigneeId() != null) { if (query.getAssigneeId() != null) {
assigneeId = query.getAssigneeId().toString(); assigneeId = query.getAssigneeId().toString();
@ -132,7 +128,10 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
affectedEntity.getEntityType().name(), affectedEntity.getEntityType().name(),
query.getPageLink().getStartTime(), query.getPageLink().getStartTime(),
query.getPageLink().getEndTime(), query.getPageLink().getEndTime(),
statusSet, asf.hasClearFilter(),
asf.hasClearFilter() && asf.getClearFilter(),
asf.hasAckFilter(),
asf.hasAckFilter() && asf.getAckFilter(),
assigneeId, assigneeId,
Objects.toString(query.getPageLink().getTextSearch(), ""), Objects.toString(query.getPageLink().getTextSearch(), ""),
DaoUtil.toPageable(query.getPageLink()) DaoUtil.toPageable(query.getPageLink())
@ -144,7 +143,10 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
tenantId.getId(), tenantId.getId(),
query.getPageLink().getStartTime(), query.getPageLink().getStartTime(),
query.getPageLink().getEndTime(), query.getPageLink().getEndTime(),
statusSet, asf.hasClearFilter(),
asf.hasClearFilter() && asf.getClearFilter(),
asf.hasAckFilter(),
asf.hasAckFilter() && asf.getAckFilter(),
assigneeId, assigneeId,
Objects.toString(query.getPageLink().getTextSearch(), ""), Objects.toString(query.getPageLink().getTextSearch(), ""),
DaoUtil.toPageable(query.getPageLink()) DaoUtil.toPageable(query.getPageLink())
@ -156,12 +158,7 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
@Override @Override
public PageData<AlarmInfo> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) { public PageData<AlarmInfo> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) {
log.trace("Try to find customer alarms by status [{}] and pageLink [{}]", query.getStatus(), query.getPageLink()); log.trace("Try to find customer alarms by status [{}] and pageLink [{}]", query.getStatus(), query.getPageLink());
Set<AlarmStatus> statusSet = null; AlarmStatusFilter asf = AlarmStatusFilter.from(query);
if (query.getSearchStatus() != null) {
statusSet = query.getSearchStatus().getStatuses();
} else if (query.getStatus() != null) {
statusSet = Collections.singleton(query.getStatus());
}
String assigneeId = null; String assigneeId = null;
if (query.getAssigneeId() != null) { if (query.getAssigneeId() != null) {
assigneeId = query.getAssigneeId().toString(); assigneeId = query.getAssigneeId().toString();
@ -172,7 +169,10 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
customerId.getId(), customerId.getId(),
query.getPageLink().getStartTime(), query.getPageLink().getStartTime(),
query.getPageLink().getEndTime(), query.getPageLink().getEndTime(),
statusSet, asf.hasClearFilter(),
asf.hasClearFilter() && asf.getClearFilter(),
asf.hasAckFilter(),
asf.hasAckFilter() && asf.getAckFilter(),
assigneeId, assigneeId,
Objects.toString(query.getPageLink().getTextSearch(), ""), Objects.toString(query.getPageLink().getTextSearch(), ""),
DaoUtil.toPageable(query.getPageLink()) DaoUtil.toPageable(query.getPageLink())
@ -186,8 +186,13 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
} }
@Override @Override
public Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, Set<AlarmStatus> statuses, String assigneeId) { public Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, AlarmStatusFilter asf, String assigneeId) {
return alarmRepository.findAlarmSeverities(tenantId.getId(), entityId.getId(), entityId.getEntityType().name(), statuses, assigneeId); return alarmRepository.findAlarmSeverities(tenantId.getId(), entityId.getId(), entityId.getEntityType().name(),
asf.hasClearFilter(),
asf.hasClearFilter() && asf.getClearFilter(),
asf.hasAckFilter(),
asf.hasAckFilter() && asf.getAckFilter(),
assigneeId);
} }
@Override @Override

3
dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java

@ -99,7 +99,8 @@ public class AlarmDataAdapter {
alarm.setPropagateToTenant((boolean) row.get(ModelConstants.ALARM_PROPAGATE_TO_TENANT_PROPERTY)); alarm.setPropagateToTenant((boolean) row.get(ModelConstants.ALARM_PROPAGATE_TO_TENANT_PROPERTY));
alarm.setType(row.get(ModelConstants.ALARM_TYPE_PROPERTY).toString()); alarm.setType(row.get(ModelConstants.ALARM_TYPE_PROPERTY).toString());
alarm.setSeverity(AlarmSeverity.valueOf(row.get(ModelConstants.ALARM_SEVERITY_PROPERTY).toString())); alarm.setSeverity(AlarmSeverity.valueOf(row.get(ModelConstants.ALARM_SEVERITY_PROPERTY).toString()));
alarm.setStatus(AlarmStatus.valueOf(row.get(ModelConstants.ALARM_STATUS_PROPERTY).toString())); alarm.setAcknowledged((boolean) row.get(ModelConstants.ALARM_ACKNOWLEDGED_PROPERTY));
alarm.setCleared((boolean) row.get(ModelConstants.ALARM_CLEARED_PROPERTY));
alarm.setTenantId(TenantId.fromUUID((UUID) row.get(ModelConstants.TENANT_ID_PROPERTY))); alarm.setTenantId(TenantId.fromUUID((UUID) row.get(ModelConstants.TENANT_ID_PROPERTY)));
Object customerIdObj = row.get(ModelConstants.CUSTOMER_ID_PROPERTY); Object customerIdObj = row.get(ModelConstants.CUSTOMER_ID_PROPERTY);
CustomerId customerId = customerIdObj != null ? new CustomerId((UUID) customerIdObj) : null; CustomerId customerId = customerIdObj != null ? new CustomerId((UUID) customerIdObj) : null;

122
dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java

@ -19,11 +19,13 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.AlarmStatusFilter;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
@ -70,7 +72,8 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
alarmFieldColumnMap.put("endTime", ModelConstants.ALARM_END_TS_PROPERTY); alarmFieldColumnMap.put("endTime", ModelConstants.ALARM_END_TS_PROPERTY);
alarmFieldColumnMap.put("startTs", ModelConstants.ALARM_START_TS_PROPERTY); alarmFieldColumnMap.put("startTs", ModelConstants.ALARM_START_TS_PROPERTY);
alarmFieldColumnMap.put("startTime", ModelConstants.ALARM_START_TS_PROPERTY); alarmFieldColumnMap.put("startTime", ModelConstants.ALARM_START_TS_PROPERTY);
alarmFieldColumnMap.put("status", ModelConstants.ALARM_STATUS_PROPERTY); alarmFieldColumnMap.put("acknowledged", ModelConstants.ALARM_ACKNOWLEDGED_PROPERTY);
alarmFieldColumnMap.put("cleared", ModelConstants.ALARM_CLEARED_PROPERTY);
alarmFieldColumnMap.put("type", ModelConstants.ALARM_TYPE_PROPERTY); alarmFieldColumnMap.put("type", ModelConstants.ALARM_TYPE_PROPERTY);
alarmFieldColumnMap.put("severity", ModelConstants.ALARM_SEVERITY_PROPERTY); alarmFieldColumnMap.put("severity", ModelConstants.ALARM_SEVERITY_PROPERTY);
alarmFieldColumnMap.put("originatorId", ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY); alarmFieldColumnMap.put("originatorId", ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY);
@ -83,56 +86,6 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
alarmFieldColumnMap.put(ASSIGNEE_EMAIL_KEY, ModelConstants.ALARM_ASSIGNEE_EMAIL_PROPERTY); alarmFieldColumnMap.put(ASSIGNEE_EMAIL_KEY, ModelConstants.ALARM_ASSIGNEE_EMAIL_PROPERTY);
} }
public static final String SELECT_ORIGINATOR_NAME = " COALESCE(CASE" +
" WHEN a.originator_type = " + EntityType.TENANT.ordinal() +
" THEN (select title from tenant where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() +
" THEN (select title from customer where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.USER.ordinal() +
" THEN (select email from tb_user where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.DASHBOARD.ordinal() +
" THEN (select title from dashboard where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.ASSET.ordinal() +
" THEN (select name from asset where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.DEVICE.ordinal() +
" THEN (select name from device where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.ENTITY_VIEW.ordinal() +
" THEN (select name from entity_view where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.DEVICE_PROFILE.ordinal() +
" THEN (select name from device_profile where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.ASSET_PROFILE.ordinal() +
" THEN (select name from asset_profile where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.EDGE.ordinal() +
" THEN (select name from edge where id = a.originator_id)" +
" END, 'Deleted') as " + ModelConstants.ALARM_ORIGINATOR_NAME_PROPERTY;
public static final String SELECT_ORIGINATOR_LABEL = " COALESCE(CASE" +
" WHEN a.originator_type = " + EntityType.TENANT.ordinal() +
" THEN (select title from tenant where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() +
" THEN (select COALESCE(title, email) from customer where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.USER.ordinal() +
" THEN (select email from tb_user where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.DASHBOARD.ordinal() +
" THEN (select title from dashboard where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.ASSET.ordinal() +
" THEN (select COALESCE(label, name) from asset where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.DEVICE.ordinal() +
" THEN (select COALESCE(label, name) from device where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.ENTITY_VIEW.ordinal() +
" THEN (select name from entity_view where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.DEVICE_PROFILE.ordinal() +
" THEN (select name from device_profile where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.ASSET_PROFILE.ordinal() +
" THEN (select name from asset_profile where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.EDGE.ordinal() +
" THEN (select COALESCE(label, name) from edge where id = a.originator_id)" +
" END, 'Deleted') as " + ModelConstants.ALARM_ORIGINATOR_LABEL_PROPERTY;
private static final String SELECT_ASSIGNEE_INFO = " tbu.first_name as assignee_first_name," +
" tbu.last_name as assignee_last_name," +
" tbu.email as assignee_email";
private static final String FIELDS_SELECTION = "select a.id as id," + private static final String FIELDS_SELECTION = "select a.id as id," +
" a.created_time as created_time," + " a.created_time as created_time," +
" a.ack_ts as ack_ts," + " a.ack_ts as ack_ts," +
@ -148,17 +101,19 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
" a.propagate_to_tenant as propagate_to_tenant," + " a.propagate_to_tenant as propagate_to_tenant," +
" a.severity as severity," + " a.severity as severity," +
" a.start_ts as start_ts," + " a.start_ts as start_ts," +
" a.status as status, " +
" a.tenant_id as tenant_id, " + " a.tenant_id as tenant_id, " +
" a.customer_id as customer_id, " + " a.customer_id as customer_id, " +
" a.propagate_relation_types as propagate_relation_types, " + " a.propagate_relation_types as propagate_relation_types, " +
" a.type as type, " + " a.type as type, " +
SELECT_ORIGINATOR_NAME + ", " + " a.originator_name as originator_name, " +
SELECT_ORIGINATOR_LABEL + ", " + " a.originator_label as originator_label, " +
SELECT_ASSIGNEE_INFO + ", "; " a.assignee_first_name as assignee_first_name, " +
" a.assignee_last_name as assignee_last_name, " +
" a.assignee_email as assignee_email, " +
" a.cleared as cleared, " +
" a.acknowledged as acknowledged, ";
private static final String JOIN_ENTITY_ALARMS = "inner join entity_alarm ea on a.id = ea.alarm_id "; private static final String JOIN_ENTITY_ALARMS = "inner join entity_alarm ea on a.id = ea.alarm_id ";
private static final String LEFT_JOIN_TB_USERS = "left join tb_user tbu on tbu.id = a.assignee_id ";
protected final NamedParameterJdbcTemplate jdbcTemplate; protected final NamedParameterJdbcTemplate jdbcTemplate;
private final TransactionTemplate transactionTemplate; private final TransactionTemplate transactionTemplate;
@ -173,12 +128,12 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
@Override @Override
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds) { public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
return transactionTemplate.execute(status -> { return transactionTemplate.execute(trStatus -> {
AlarmDataPageLink pageLink = query.getPageLink(); AlarmDataPageLink pageLink = query.getPageLink();
QueryContext ctx = new QueryContext(new QuerySecurityContext(tenantId, null, EntityType.ALARM)); QueryContext ctx = new QueryContext(new QuerySecurityContext(tenantId, null, EntityType.ALARM));
ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList())); ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList()));
StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION); StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION);
StringBuilder fromPart = new StringBuilder(" from alarm a "); StringBuilder fromPart = new StringBuilder(" from alarm_info a ");
StringBuilder wherePart = new StringBuilder(" where "); StringBuilder wherePart = new StringBuilder(" where ");
StringBuilder sortPart = new StringBuilder(" order by "); StringBuilder sortPart = new StringBuilder(" order by ");
StringBuilder joinPart = new StringBuilder(); StringBuilder joinPart = new StringBuilder();
@ -191,7 +146,6 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
} else { } else {
selectPart.append(" a.originator_id as entity_id "); selectPart.append(" a.originator_id as entity_id ");
} }
fromPart.append(LEFT_JOIN_TB_USERS);
EntityDataSortOrder sortOrder = pageLink.getSortOrder(); EntityDataSortOrder sortOrder = pageLink.getSortOrder();
List<EntityKey> alarmFields = new ArrayList<>(); List<EntityKey> alarmFields = new ArrayList<>();
@ -209,6 +163,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
String textSearchQuery = buildTextSearchQuery(ctx, alarmFields, pageLink.getTextSearch()); String textSearchQuery = buildTextSearchQuery(ctx, alarmFields, pageLink.getTextSearch());
if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) { if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) {
String sortOrderKey = sortOrder.getKey().getKey(); String sortOrderKey = sortOrder.getKey().getKey();
if ("status".equalsIgnoreCase(sortOrderKey)) {
selectPart.append(", a.status as status ");
}
sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey)) sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey))
.append(" ").append(sortOrder.getDirection().name()); .append(" ").append(sortOrder.getDirection().name());
if (pageLink.isSearchPropagatedAlarms()) { if (pageLink.isSearchPropagatedAlarms()) {
@ -291,13 +248,19 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
wherePart.append("a.severity in (:alarmSeverities)"); wherePart.append("a.severity in (:alarmSeverities)");
} }
if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) { AlarmStatusFilter asf = AlarmStatusFilter.fromList(pageLink.getStatusList());
Set<AlarmStatus> statusSet = toStatusSet(pageLink.getStatusList()); if (asf.hasAnyFilter()) {
if (!statusSet.isEmpty()) { if (asf.hasAckFilter()) {
addAndIfNeeded(wherePart, addAnd); addAndIfNeeded(wherePart, addAnd);
addAnd = true; addAnd = true;
ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList())); ctx.addBooleanParameter("ackStatus", asf.getAckFilter());
wherePart.append(" a.status in (:alarmStatuses)"); wherePart.append(" a.acknowledged = :ackStatus");
}
if (asf.hasClearFilter()) {
addAndIfNeeded(wherePart, addAnd);
// addAnd = true; // not needed but stored as an example if someone adds new conditions
ctx.addBooleanParameter("clearStatus", asf.getClearFilter());
wherePart.append(" a.cleared = :clearStatus");
} }
} }
@ -366,37 +329,6 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
return permissionsQuery.toString(); return permissionsQuery.toString();
} }
private Set<AlarmStatus> toStatusSet(List<AlarmSearchStatus> statusList) {
Set<AlarmStatus> result = new HashSet<>();
for (AlarmSearchStatus searchStatus : statusList) {
switch (searchStatus) {
case ACK:
result.add(AlarmStatus.ACTIVE_ACK);
result.add(AlarmStatus.CLEARED_ACK);
break;
case UNACK:
result.add(AlarmStatus.ACTIVE_UNACK);
result.add(AlarmStatus.CLEARED_UNACK);
break;
case CLEARED:
result.add(AlarmStatus.CLEARED_ACK);
result.add(AlarmStatus.CLEARED_UNACK);
break;
case ACTIVE:
result.add(AlarmStatus.ACTIVE_ACK);
result.add(AlarmStatus.ACTIVE_UNACK);
break;
default:
break;
}
if (searchStatus == AlarmSearchStatus.ANY || result.size() == AlarmStatus.values().length) {
result.clear();
return result;
}
}
return result;
}
private void addAndIfNeeded(StringBuilder wherePart, boolean addAnd) { private void addAndIfNeeded(StringBuilder wherePart, boolean addAnd) {
if (addAnd) { if (addAnd) {
wherePart.append(" and "); wherePart.append(" and ");

3
dao/src/main/resources/sql/schema-entities-idx.sql

@ -20,7 +20,8 @@ CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC); CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_status_created_time ON alarm(tenant_id, status, created_time DESC); CREATE INDEX IF NOT EXISTS idx_alarm_tenant_alarm_type_created_time_active
ON alarm USING btree (tenant_id, type, created_time DESC) WHERE cleared = false;
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_alarm_type_created_time ON alarm(tenant_id, type, created_time DESC); CREATE INDEX IF NOT EXISTS idx_alarm_tenant_alarm_type_created_time ON alarm(tenant_id, type, created_time DESC);

9
dao/src/main/resources/sql/schema-entities.sql

@ -53,14 +53,15 @@ CREATE TABLE IF NOT EXISTS alarm (
severity varchar(255), severity varchar(255),
start_ts bigint, start_ts bigint,
assign_ts bigint, assign_ts bigint,
status varchar(255),
assignee_id uuid, assignee_id uuid,
tenant_id uuid, tenant_id uuid,
customer_id uuid, customer_id uuid,
propagate_relation_types varchar, propagate_relation_types varchar,
type varchar(255), type varchar(255),
propagate_to_owner boolean, propagate_to_owner boolean,
propagate_to_tenant boolean propagate_to_tenant boolean,
acknowledged boolean,
cleared boolean
); );
CREATE TABLE IF NOT EXISTS alarm_comment ( CREATE TABLE IF NOT EXISTS alarm_comment (
@ -802,6 +803,10 @@ CREATE TABLE IF NOT EXISTS user_settings (
DROP VIEW IF EXISTS alarm_info; DROP VIEW IF EXISTS alarm_info;
CREATE VIEW alarm_info AS CREATE VIEW alarm_info AS
SELECT a.*, SELECT a.*,
(CASE WHEN a.acknowledged AND a.cleared THEN 'CLEARED_ACK'
WHEN NOT a.acknowledged AND a.cleared THEN 'CLEARED_UNACK'
WHEN a.acknowledged AND NOT a.cleared THEN 'ACTIVE_ACK'
WHEN NOT a.acknowledged AND NOT a.cleared THEN 'ACTIVE_UNACK' END) as status,
COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id) COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id)
WHEN a.originator_type = 1 THEN (select title from customer where id = a.originator_id) WHEN a.originator_type = 1 THEN (select title from customer where id = a.originator_id)
WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id) WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id)

2
dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmCommentServiceTest.java

@ -58,7 +58,7 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
alarm = Alarm.builder().tenantId(tenantId).originator(new AssetId(Uuids.timeBased())) alarm = Alarm.builder().tenantId(tenantId).originator(new AssetId(Uuids.timeBased()))
.type(TEST_ALARM) .type(TEST_ALARM)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.startTs(System.currentTimeMillis()).build(); .startTs(System.currentTimeMillis()).build();
alarm = alarmService.createOrUpdateAlarm(alarm).getAlarm(); alarm = alarmService.createOrUpdateAlarm(alarm).getAlarm();

36
dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java

@ -92,7 +92,9 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId) Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId)
.type(TEST_ALARM) .type(TEST_ALARM)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.acknowledged(false)
.cleared(false)
.startTs(ts).build(); .startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm); AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm);
@ -131,7 +133,9 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId) Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId)
.type(TEST_ALARM) .type(TEST_ALARM)
.propagate(false) .propagate(false)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.acknowledged(false)
.cleared(false)
.startTs(ts).build(); .startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm); AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm);
@ -235,7 +239,9 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId) Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId)
.type(TEST_ALARM) .type(TEST_ALARM)
.propagate(false) .propagate(false)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.acknowledged(false)
.cleared(false)
.startTs(ts).build(); .startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm); AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm);
@ -316,7 +322,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(tenantDevice.getId()) .originator(tenantDevice.getId())
.type(TEST_ALARM) .type(TEST_ALARM)
.propagate(true) .propagate(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.startTs(ts).build(); .startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm); AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm);
tenantAlarm = result.getAlarm(); tenantAlarm = result.getAlarm();
@ -325,7 +331,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(customerDevice.getId()) .originator(customerDevice.getId())
.type(TEST_ALARM) .type(TEST_ALARM)
.propagate(true) .propagate(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.startTs(ts).build(); .startTs(ts).build();
result = alarmService.createOrUpdateAlarm(deviceAlarm); result = alarmService.createOrUpdateAlarm(deviceAlarm);
deviceAlarm = result.getAlarm(); deviceAlarm = result.getAlarm();
@ -392,7 +398,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(tenantDevice.getId()) .originator(tenantDevice.getId())
.type("Not Propagated") .type("Not Propagated")
.propagate(false) .propagate(false)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.startTs(ts).build(); .startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm); AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm);
tenantAlarm = result.getAlarm(); tenantAlarm = result.getAlarm();
@ -401,7 +407,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(tenantDevice.getId()) .originator(tenantDevice.getId())
.type("Propagated") .type("Propagated")
.propagate(true) .propagate(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.startTs(ts).build(); .startTs(ts).build();
result = alarmService.createOrUpdateAlarm(customerAlarm); result = alarmService.createOrUpdateAlarm(customerAlarm);
customerAlarm = result.getAlarm(); customerAlarm = result.getAlarm();
@ -442,7 +448,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(device.getId()) .originator(device.getId())
.type("Propagated To Tenant") .type("Propagated To Tenant")
.propagateToTenant(true) .propagateToTenant(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.startTs(ts).build(); .startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm); AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm);
tenantAlarm = result.getAlarm(); tenantAlarm = result.getAlarm();
@ -452,7 +458,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.type("Propagated to Customer") .type("Propagated to Customer")
.propagate(false) .propagate(false)
.propagateToOwner(true) .propagateToOwner(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.startTs(ts).build(); .startTs(ts).build();
result = alarmService.createOrUpdateAlarm(customerAlarm); result = alarmService.createOrUpdateAlarm(customerAlarm);
customerAlarm = result.getAlarm(); customerAlarm = result.getAlarm();
@ -509,7 +515,8 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(customerDevice.getId()) .originator(customerDevice.getId())
.type(TEST_ALARM) .type(TEST_ALARM)
.severity(AlarmSeverity.MAJOR) .severity(AlarmSeverity.MAJOR)
.status(AlarmStatus.ACTIVE_UNACK) .acknowledged(false)
.cleared(false)
.startTs(System.currentTimeMillis()) .startTs(System.currentTimeMillis())
.build(); .build();
alarm1 = alarmService.createOrUpdateAlarm(alarm1).getAlarm(); alarm1 = alarmService.createOrUpdateAlarm(alarm1).getAlarm();
@ -520,7 +527,8 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(customerDevice.getId()) .originator(customerDevice.getId())
.type(TEST_ALARM) .type(TEST_ALARM)
.severity(AlarmSeverity.MINOR) .severity(AlarmSeverity.MINOR)
.status(AlarmStatus.ACTIVE_ACK) .acknowledged(true)
.cleared(false)
.startTs(System.currentTimeMillis()) .startTs(System.currentTimeMillis())
.build(); .build();
alarm2 = alarmService.createOrUpdateAlarm(alarm2).getAlarm(); alarm2 = alarmService.createOrUpdateAlarm(alarm2).getAlarm();
@ -531,7 +539,8 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(customerDevice.getId()) .originator(customerDevice.getId())
.type(TEST_ALARM) .type(TEST_ALARM)
.severity(AlarmSeverity.CRITICAL) .severity(AlarmSeverity.CRITICAL)
.status(AlarmStatus.ACTIVE_ACK) .acknowledged(true)
.cleared(false)
.startTs(System.currentTimeMillis()) .startTs(System.currentTimeMillis())
.build(); .build();
alarm3 = alarmService.createOrUpdateAlarm(alarm3).getAlarm(); alarm3 = alarmService.createOrUpdateAlarm(alarm3).getAlarm();
@ -560,7 +569,6 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.type(TEST_ALARM) .type(TEST_ALARM)
.propagate(false) .propagate(false)
.severity(AlarmSeverity.CRITICAL) .severity(AlarmSeverity.CRITICAL)
.status(AlarmStatus.ACTIVE_UNACK)
.startTs(ts).build(); .startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm); AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm);
@ -720,7 +728,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId) Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId)
.type(TEST_ALARM) .type(TEST_ALARM)
.propagate(true) .propagate(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK) .severity(AlarmSeverity.CRITICAL)
.startTs(ts).build(); .startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm); AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm);

1
dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java

@ -76,7 +76,6 @@ public class JpaAlarmCommentDaoTest extends AbstractJpaDaoTest {
alarm.setPropagate(true); alarm.setPropagate(true);
alarm.setStartTs(System.currentTimeMillis()); alarm.setStartTs(System.currentTimeMillis());
alarm.setEndTs(System.currentTimeMillis()); alarm.setEndTs(System.currentTimeMillis());
alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
alarmDao.save(TenantId.fromUUID(tenantId), alarm); alarmDao.save(TenantId.fromUUID(tenantId), alarm);
} }
private void saveAlarmComment(UUID id, UUID alarmId, UUID userId, AlarmCommentType type) { private void saveAlarmComment(UUID id, UUID alarmId, UUID userId, AlarmCommentType type) {

3
dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java

@ -78,7 +78,8 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest {
alarm.setPropagate(true); alarm.setPropagate(true);
alarm.setStartTs(System.currentTimeMillis()); alarm.setStartTs(System.currentTimeMillis());
alarm.setEndTs(System.currentTimeMillis()); alarm.setEndTs(System.currentTimeMillis());
alarm.setStatus(AlarmStatus.ACTIVE_UNACK); alarm.setAcknowledged(false);
alarm.setCleared(false);
alarmDao.save(TenantId.fromUUID(tenantId), alarm); alarmDao.save(TenantId.fromUUID(tenantId), alarm);
} }
} }

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java

@ -85,7 +85,8 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig
alarm.setEndTs(savedAlarm.getEndTs()); alarm.setEndTs(savedAlarm.getEndTs());
alarm.setClearTs(savedAlarm.getClearTs()); alarm.setClearTs(savedAlarm.getClearTs());
} }
alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK); //TODO: remove and return the alarm from a DB call.
alarm.setCleared(true);
return Futures.immediateFuture(new TbAlarmResult(false, false, true, alarm)); return Futures.immediateFuture(new TbAlarmResult(false, false, true, alarm));
}, ctx.getDbCallbackExecutor()); }, ctx.getDbCallbackExecutor());
}, ctx.getDbCallbackExecutor()); }, ctx.getDbCallbackExecutor());

6
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java

@ -116,9 +116,6 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
if (msgAlarm.getOriginator() == null) { if (msgAlarm.getOriginator() == null) {
msgAlarm.setOriginator(msg.getOriginator()); msgAlarm.setOriginator(msg.getOriginator());
} }
if (msgAlarm.getStatus() == null) {
msgAlarm.setStatus(AlarmStatus.ACTIVE_UNACK);
}
return msgAlarm; return msgAlarm;
} }
@ -195,7 +192,8 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
return Alarm.builder() return Alarm.builder()
.tenantId(tenantId) .tenantId(tenantId)
.originator(msg.getOriginator()) .originator(msg.getOriginator())
.status(AlarmStatus.ACTIVE_UNACK) .cleared(false)
.acknowledged(false)
.severity(this.config.isDynamicSeverity() ? processAlarmSeverity(msg) : notDynamicAlarmSeverity) .severity(this.config.isDynamicSeverity() ? processAlarmSeverity(msg) : notDynamicAlarmSeverity)
.propagate(config.isPropagate()) .propagate(config.isPropagate())
.propagateToOwner(config.isPropagateToOwner()) .propagateToOwner(config.isPropagateToOwner())

5
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/AlarmState.java

@ -255,7 +255,8 @@ class AlarmState {
} else { } else {
currentAlarm = new Alarm(); currentAlarm = new Alarm();
currentAlarm.setType(alarmDefinition.getAlarmType()); currentAlarm.setType(alarmDefinition.getAlarmType());
currentAlarm.setStatus(AlarmStatus.ACTIVE_UNACK); currentAlarm.setAcknowledged(false);
currentAlarm.setCleared(false);
currentAlarm.setSeverity(severity); currentAlarm.setSeverity(severity);
long startTs = dataSnapshot.getTs(); long startTs = dataSnapshot.getTs();
if (startTs == 0L) { if (startTs == 0L) {
@ -342,7 +343,7 @@ class AlarmState {
public void processAckAlarm(Alarm alarm) { public void processAckAlarm(Alarm alarm) {
if (currentAlarm != null && currentAlarm.getId().equals(alarm.getId())) { if (currentAlarm != null && currentAlarm.getId().equals(alarm.getId())) {
currentAlarm.setStatus(alarm.getStatus()); currentAlarm.setAcknowledged(alarm.isAcknowledged());
currentAlarm.setAckTs(alarm.getAckTs()); currentAlarm.setAckTs(alarm.getAckTs());
} }
} }

32
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java

@ -93,16 +93,16 @@ public class TbAlarmNodeTest {
@Captor @Captor
private ArgumentCaptor<Consumer<Throwable>> failureCaptor; private ArgumentCaptor<Consumer<Throwable>> failureCaptor;
private RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); private final RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased());
private RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); private final RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased());
private ListeningExecutor dbExecutor; private ListeningExecutor dbExecutor;
private EntityId originator = new DeviceId(Uuids.timeBased()); private final EntityId originator = new DeviceId(Uuids.timeBased());
private EntityId alarmOriginator = new AlarmId(Uuids.timeBased()); private final EntityId alarmOriginator = new AlarmId(Uuids.timeBased());
private TenantId tenantId = TenantId.fromUUID(Uuids.timeBased()); private final TenantId tenantId = TenantId.fromUUID(Uuids.timeBased());
private TbMsgMetaData metaData = new TbMsgMetaData(); private final TbMsgMetaData metaData = new TbMsgMetaData();
private String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; private final String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
@Before @Before
public void before() { public void before() {
@ -158,7 +158,6 @@ public class TbAlarmNodeTest {
.endTs(ts) .endTs(ts)
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(ACTIVE_UNACK)
.severity(CRITICAL) .severity(CRITICAL)
.propagate(true) .propagate(true)
.type("SomeType") .type("SomeType")
@ -197,7 +196,7 @@ public class TbAlarmNodeTest {
metaData.putValue("key", "value"); metaData.putValue("key", "value");
TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
long ts = msg.getTs(); long ts = msg.getTs();
Alarm clearedAlarm = Alarm.builder().status(CLEARED_ACK).build(); Alarm clearedAlarm = Alarm.builder().cleared(true).acknowledged(true).build();
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(clearedAlarm)); when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(clearedAlarm));
@ -230,7 +229,6 @@ public class TbAlarmNodeTest {
.endTs(ts) .endTs(ts)
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(ACTIVE_UNACK)
.severity(CRITICAL) .severity(CRITICAL)
.propagate(true) .propagate(true)
.type("SomeType") .type("SomeType")
@ -247,7 +245,7 @@ public class TbAlarmNodeTest {
TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
long oldEndDate = System.currentTimeMillis(); long oldEndDate = System.currentTimeMillis();
Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).status(ACTIVE_UNACK).severity(WARNING).endTs(oldEndDate).build(); Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(WARNING).endTs(oldEndDate).build();
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(activeAlarm)); when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(activeAlarm));
@ -278,7 +276,6 @@ public class TbAlarmNodeTest {
Alarm expectedAlarm = Alarm.builder() Alarm expectedAlarm = Alarm.builder()
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(ACTIVE_UNACK)
.severity(CRITICAL) .severity(CRITICAL)
.propagate(true) .propagate(true)
.type("SomeType") .type("SomeType")
@ -296,7 +293,7 @@ public class TbAlarmNodeTest {
TbMsg msg = TbMsg.newMsg( "USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); TbMsg msg = TbMsg.newMsg( "USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
long oldEndDate = System.currentTimeMillis(); long oldEndDate = System.currentTimeMillis();
Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).status(ACTIVE_UNACK).severity(WARNING).endTs(oldEndDate).build(); Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(WARNING).endTs(oldEndDate).build();
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(activeAlarm)); when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(activeAlarm));
@ -328,7 +325,7 @@ public class TbAlarmNodeTest {
Alarm expectedAlarm = Alarm.builder() Alarm expectedAlarm = Alarm.builder()
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(CLEARED_UNACK) .cleared(true)
.severity(WARNING) .severity(WARNING)
.propagate(false) .propagate(false)
.type("SomeType") .type("SomeType")
@ -347,7 +344,7 @@ public class TbAlarmNodeTest {
long oldEndDate = System.currentTimeMillis(); long oldEndDate = System.currentTimeMillis();
AlarmId id = new AlarmId(alarmOriginator.getId()); AlarmId id = new AlarmId(alarmOriginator.getId());
Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).status(ACTIVE_UNACK).severity(WARNING).endTs(oldEndDate).build(); Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(WARNING).endTs(oldEndDate).build();
activeAlarm.setId(id); activeAlarm.setId(id);
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
@ -378,7 +375,7 @@ public class TbAlarmNodeTest {
Alarm expectedAlarm = Alarm.builder() Alarm expectedAlarm = Alarm.builder()
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(CLEARED_UNACK) .cleared(true)
.severity(WARNING) .severity(WARNING)
.propagate(false) .propagate(false)
.type("SomeType") .type("SomeType")
@ -444,7 +441,6 @@ public class TbAlarmNodeTest {
.endTs(ts) .endTs(ts)
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(ACTIVE_UNACK)
.severity(WARNING) .severity(WARNING)
.propagate(true) .propagate(true)
.type("SomeType") .type("SomeType")
@ -507,7 +503,6 @@ public class TbAlarmNodeTest {
.endTs(ts) .endTs(ts)
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(ACTIVE_UNACK)
.severity(WARNING) .severity(WARNING)
.propagate(true) .propagate(true)
.type("SomeType") .type("SomeType")
@ -571,7 +566,6 @@ public class TbAlarmNodeTest {
.endTs(ts) .endTs(ts)
.tenantId(tenantId) .tenantId(tenantId)
.originator(originator) .originator(originator)
.status(ACTIVE_UNACK)
.severity(CRITICAL) .severity(CRITICAL)
.propagateToTenant(true) .propagateToTenant(true)
.type("SomeType" + i) .type("SomeType" + i)

Loading…
Cancel
Save