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 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 (
id uuid NOT NULL,
created_time bigint NOT NULL,
@ -40,3 +65,41 @@ CREATE TABLE IF NOT EXISTS user_settings (
settings varchar(100000),
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)
@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId);
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
return checkAlarmId(alarmId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
return checkAlarmId(alarmId, Operation.READ);
}
@ApiOperation(value = "Get Alarm Info (getAlarmInfoById)",
@ -119,12 +115,8 @@ public class AlarmController extends BaseController {
public AlarmInfo getAlarmInfoById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId);
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
return checkAlarmInfoId(alarmId, Operation.READ);
} catch (Exception e) {
throw handleException(e);
}
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
return checkAlarmInfoId(alarmId, Operation.READ);
}
@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.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.setEndTs(alarmUpdateMsg.getEndTs());
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();
alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment);
alarm.setAckTs(ackTs);
alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK);
alarm.setAcknowledged(true);
notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_ACK, user);
return null;
}, MoreExecutors.directExecutor());
@ -89,7 +89,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
.build();
alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment);
alarm.setClearTs(clearTs);
alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK);
alarm.setCleared(true);
notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_CLEAR, user);
return null;
}, 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.AlarmInfo;
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.EntityId;
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()) {
boolean matches = false;
for (AlarmSearchStatus status : filter.getStatusList()) {
if (status.getStatuses().contains(alarm.getStatus())) {
matches = true;
switch (status) {
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;
}
}

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

@ -79,7 +79,6 @@ public abstract class BaseAlarmCommentControllerTest extends AbstractControllerT
.tenantId(tenantId)
.customerId(customerId)
.originator(customerDevice.getId())
.status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.type("test alarm type")
.build();
@ -316,7 +315,6 @@ public abstract class BaseAlarmCommentControllerTest extends AbstractControllerT
Alarm alarm = Alarm.builder()
.originator(device.getId())
.status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.type("Test")
.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()
.originator(device.getId())
.status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.type("Test")
.build();
@ -586,7 +585,6 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
.tenantId(tenantId)
.customerId(customerId)
.originator(customerDevice.getId())
.status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.type(type)
.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");
Alarm alarm = new Alarm();
alarm.setOriginator(device.getId());
alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
alarm.setType("alarm");
alarm.setSeverity(AlarmSeverity.CRITICAL);
edgeImitator.expectMessageAmount(1);

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

@ -93,7 +93,6 @@ public class DefaultTbAlarmServiceTest {
@Test
public void testAck() {
var alarm = new Alarm();
alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
when(alarmSubscriptionService.ackAlarm(any(), any(), anyLong())).thenReturn(Futures.immediateFuture(true));
service.ack(alarm, new User(new UserId(UUID.randomUUID())));
@ -105,7 +104,7 @@ public class DefaultTbAlarmServiceTest {
@Test
public void testClear() {
var alarm = new Alarm();
alarm.setStatus(AlarmStatus.ACTIVE_ACK);
alarm.setAcknowledged(true);
when(alarmSubscriptionService.clearAlarm(any(), any(), any(), anyLong())).thenReturn(Futures.immediateFuture(true));
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;
@ApiModelProperty(position = 8, required = true, value = "Alarm severity", example = "CRITICAL")
private AlarmSeverity severity;
@ApiModelProperty(position = 9, required = true, value = "Alarm status", example = "CLEARED_UNACK")
private AlarmStatus status;
@ApiModelProperty(position = 10, value = "Alarm assignee user id")
@ApiModelProperty(position = 9, required = true, value = "Acknowledged", example = "true")
private boolean acknowledged;
@ApiModelProperty(position = 10, required = true, value = "Cleared", example = "false")
private boolean cleared;
@ApiModelProperty(position = 11, value = "Alarm assignee user id")
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;
@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;
@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;
@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;
@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;
@ApiModelProperty(position = 16, value = "JSON object with alarm details")
@ApiModelProperty(position = 17, value = "JSON object with alarm 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;
@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;
@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;
@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. " +
"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;
@ -106,12 +108,13 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
this.type = alarm.getType();
this.originator = alarm.getOriginator();
this.severity = alarm.getSeverity();
this.status = alarm.getStatus();
this.assigneeId = alarm.getAssigneeId();
this.startTs = alarm.getStartTs();
this.endTs = alarm.getEndTs();
this.acknowledged = alarm.isAcknowledged();
this.ackTs = alarm.getAckTs();
this.clearTs = alarm.getClearTs();
this.cleared = alarm.isCleared();
this.assignTs = alarm.getAssignTs();
this.details = alarm.getDetails();
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. " +
"Specify this field to update the alarm. " +
"Referencing non-existing alarm Id will cause error. " +
"Omit this field to create new alarm." )
"Omit this field to create new alarm.")
@Override
public AlarmId getId() {
return super.getId();
@ -143,4 +146,19 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
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;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
public enum AlarmSearchStatus {
ANY(AlarmStatus.values()),
ACTIVE(AlarmStatus.ACTIVE_ACK, AlarmStatus.ACTIVE_UNACK),
CLEARED(AlarmStatus.CLEARED_ACK, AlarmStatus.CLEARED_UNACK),
ACK(AlarmStatus.ACTIVE_ACK, AlarmStatus.CLEARED_ACK),
UNACK(AlarmStatus.ACTIVE_UNACK, AlarmStatus.CLEARED_UNACK);
@JsonIgnore
@Getter
private Set<AlarmStatus> statuses;
ANY,
ACTIVE,
CLEARED,
ACK,
UNACK;
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) {
this.setEndTs(alarm.getEndTs());
this.setSeverity(alarm.getSeverity());
this.setStatus(alarm.getStatus());
this.setAcknowledged(alarm.isAcknowledged());
this.setCleared(alarm.isCleared());
this.setDetails(alarm.getDetails());
this.setPropagate(alarm.isPropagate());
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.AlarmSeverity;
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.id.AlarmId;
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);
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);

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.AlarmAssignee;
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.AlarmInfo;
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));
if (alarm.getId() == null) {
// Atomic update and return alarm + assignee.
Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType());
if (existing == null || existing.getStatus().isCleared()) {
if (!alarmCreationEnabled) {
@ -235,9 +237,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (alarm == null || alarm.getStatus().isAck()) {
return Futures.immediateFuture(new AlarmOperationResult(alarm, false));
} else {
AlarmStatus oldStatus = alarm.getStatus();
AlarmStatus newStatus = oldStatus.isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK;
alarm.setStatus(newStatus);
alarm.setAcknowledged(true);
alarm.setAckTs(ackTime);
alarm = alarmDao.save(alarm.getTenantId(), 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()) {
return Futures.immediateFuture(new AlarmOperationResult(alarm, false));
} else {
AlarmStatus oldStatus = alarm.getStatus();
AlarmStatus newStatus = oldStatus.isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK;
alarm.setStatus(newStatus);
alarm.setCleared(true);
alarm.setClearTs(clearTime);
if (details != null) {
alarm.setDetails(details);
@ -336,15 +334,16 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
@Override
public AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus,
AlarmStatus alarmStatus, String assigneeId) {
Set<AlarmStatus> statusList = null;
AlarmStatusFilter asf;
if (alarmSearchStatus != null) {
statusList = alarmSearchStatus.getStatuses();
asf = AlarmStatusFilter.from(alarmSearchStatus);
} 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);
}
@ -369,7 +368,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
if (alarm.getAssignTs() > existing.getAssignTs()) {
existing.setAssignTs(alarm.getAssignTs());
}
existing.setStatus(alarm.getStatus());
existing.setAcknowledged(alarm.isAcknowledged());
existing.setCleared(alarm.isCleared());
existing.setSeverity(alarm.getSeverity());
existing.setDetails(alarm.getDetails());
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_TYPE_PROPERTY = "type";
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_NAME_PROPERTY = "originator_name";
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_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_FIRST_NAME_PROPERTY = "assignee_first_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_START_TS_PROPERTY = "start_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_CLEARED_PROPERTY = "cleared";
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_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 org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.springframework.data.annotation.Id;
import org.springframework.util.CollectionUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.alarm.Alarm;
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.CustomerId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -45,9 +43,11 @@ import java.util.Arrays;
import java.util.Collections;
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_ASSIGNEE_ID_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_CUSTOMER_ID_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_SEVERITY_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_TYPE_PROPERTY;
@ -88,10 +87,6 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
@Column(name = ALARM_SEVERITY_PROPERTY)
private AlarmSeverity severity;
@Enumerated(EnumType.STRING)
@Column(name = ALARM_STATUS_PROPERTY)
private AlarmStatus status;
@Type(type="pg-uuid")
@Column(name = ALARM_ASSIGNEE_ID_PROPERTY)
private UUID assigneeId;
@ -102,9 +97,15 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
@Column(name = ALARM_END_TS_PROPERTY)
private Long endTs;
@Column(name = ALARM_ACKNOWLEDGED_PROPERTY)
private boolean acknowledged;
@Column(name = ALARM_ACK_TS_PROPERTY)
private Long ackTs;
@Column(name = ALARM_CLEARED_PROPERTY)
private boolean cleared;
@Column(name = ALARM_CLEAR_TS_PROPERTY)
private Long clearTs;
@ -147,7 +148,8 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
this.originatorType = alarm.getOriginator().getEntityType();
this.type = alarm.getType();
this.severity = alarm.getSeverity();
this.status = alarm.getStatus();
this.acknowledged = alarm.isAcknowledged();
this.cleared = alarm.isCleared();
if (alarm.getAssigneeId() != null) {
this.assigneeId = alarm.getAssigneeId().getId();
}
@ -177,7 +179,8 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
this.originatorType = alarmEntity.getOriginatorType();
this.type = alarmEntity.getType();
this.severity = alarmEntity.getSeverity();
this.status = alarmEntity.getStatus();
this.acknowledged = alarmEntity.isAcknowledged();
this.cleared = alarmEntity.isCleared();
this.assigneeId = alarmEntity.getAssigneeId();
this.propagate = alarmEntity.getPropagate();
this.propagateToOwner = alarmEntity.getPropagateToOwner();
@ -203,7 +206,8 @@ public abstract class AbstractAlarmEntity<T extends Alarm> extends BaseSqlEntity
alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, originatorId));
alarm.setType(type);
alarm.setSeverity(severity);
alarm.setStatus(status);
alarm.setAcknowledged(acknowledged);
alarm.setCleared(cleared);
if (assigneeId != null) {
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_ORIGINATOR_LABEL_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;
@Data
@ -48,6 +49,8 @@ public class AlarmInfoEntity extends AbstractAlarmEntity<AlarmInfo> {
private String assigneeLastName;
@Column(name = ALARM_ASSIGNEE_EMAIL_PROPERTY)
private String assigneeEmail;
@Column(name = ALARM_STATUS_PROPERTY)
private String status;
public AlarmInfoEntity() {
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 (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " +
"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 (LOWER(a.type) 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 (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " +
"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 (LOWER(a.type) 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("startTime") Long startTime,
@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("searchText") String searchText,
Pageable pageable);
@ -84,18 +89,20 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"WHERE a.tenantId = :tenantId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"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 (LOWER(a.type) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.severity) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
" OR LOWER(a.status) LIKE LOWER(CONCAT('%', :searchText, '%'))) ",
countQuery = "" +
"SELECT count(a) " +
"FROM AlarmEntity a " +
"FROM AlarmInfoEntity a " +
"WHERE a.tenantId = :tenantId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"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 (LOWER(a.type) 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,
@Param("startTime") Long startTime,
@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("searchText") String searchText,
Pageable pageable);
@ -113,7 +123,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"WHERE a.tenantId = :tenantId AND a.customerId = :customerId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"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 (LOWER(a.type) 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 " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"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 (LOWER(a.type) 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("startTime") Long startTime,
@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("searchText") String searchText,
Pageable pageable);
@ -145,12 +160,16 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"AND ea.tenantId = :tenantId " +
"AND ea.entityId = :affectedEntityId " +
"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))")
Set<AlarmSeverity> findAlarmSeverities(@Param("tenantId") UUID tenantId,
@Param("affectedEntityId") UUID affectedEntityId,
@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);
@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.AlarmSeverity;
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.id.AlarmId;
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) {
log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink());
EntityId affectedEntity = query.getAffectedEntityId();
Set<AlarmStatus> statusSet = null;
if (query.getSearchStatus() != null) {
statusSet = query.getSearchStatus().getStatuses();
} else if (query.getStatus() != null) {
statusSet = Collections.singleton(query.getStatus());
}
AlarmStatusFilter asf = AlarmStatusFilter.from(query);
String assigneeId = null;
if (query.getAssigneeId() != null) {
assigneeId = query.getAssigneeId().toString();
@ -132,7 +128,10 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
affectedEntity.getEntityType().name(),
query.getPageLink().getStartTime(),
query.getPageLink().getEndTime(),
statusSet,
asf.hasClearFilter(),
asf.hasClearFilter() && asf.getClearFilter(),
asf.hasAckFilter(),
asf.hasAckFilter() && asf.getAckFilter(),
assigneeId,
Objects.toString(query.getPageLink().getTextSearch(), ""),
DaoUtil.toPageable(query.getPageLink())
@ -144,7 +143,10 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
tenantId.getId(),
query.getPageLink().getStartTime(),
query.getPageLink().getEndTime(),
statusSet,
asf.hasClearFilter(),
asf.hasClearFilter() && asf.getClearFilter(),
asf.hasAckFilter(),
asf.hasAckFilter() && asf.getAckFilter(),
assigneeId,
Objects.toString(query.getPageLink().getTextSearch(), ""),
DaoUtil.toPageable(query.getPageLink())
@ -156,12 +158,7 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
@Override
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());
Set<AlarmStatus> statusSet = null;
if (query.getSearchStatus() != null) {
statusSet = query.getSearchStatus().getStatuses();
} else if (query.getStatus() != null) {
statusSet = Collections.singleton(query.getStatus());
}
AlarmStatusFilter asf = AlarmStatusFilter.from(query);
String assigneeId = null;
if (query.getAssigneeId() != null) {
assigneeId = query.getAssigneeId().toString();
@ -172,7 +169,10 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
customerId.getId(),
query.getPageLink().getStartTime(),
query.getPageLink().getEndTime(),
statusSet,
asf.hasClearFilter(),
asf.hasClearFilter() && asf.getClearFilter(),
asf.hasAckFilter(),
asf.hasAckFilter() && asf.getAckFilter(),
assigneeId,
Objects.toString(query.getPageLink().getTextSearch(), ""),
DaoUtil.toPageable(query.getPageLink())
@ -186,8 +186,13 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
}
@Override
public Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, Set<AlarmStatus> statuses, String assigneeId) {
return alarmRepository.findAlarmSeverities(tenantId.getId(), entityId.getId(), entityId.getEntityType().name(), statuses, assigneeId);
public Set<AlarmSeverity> findAlarmSeverities(TenantId tenantId, EntityId entityId, AlarmStatusFilter asf, String 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

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.setType(row.get(ModelConstants.ALARM_TYPE_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)));
Object customerIdObj = row.get(ModelConstants.CUSTOMER_ID_PROPERTY);
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.stereotype.Repository;
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.StringUtils;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
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.TenantId;
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("startTs", 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("severity", ModelConstants.ALARM_SEVERITY_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);
}
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," +
" a.created_time as created_time," +
" a.ack_ts as ack_ts," +
@ -148,17 +101,19 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
" a.propagate_to_tenant as propagate_to_tenant," +
" a.severity as severity," +
" a.start_ts as start_ts," +
" a.status as status, " +
" a.tenant_id as tenant_id, " +
" a.customer_id as customer_id, " +
" a.propagate_relation_types as propagate_relation_types, " +
" a.type as type, " +
SELECT_ORIGINATOR_NAME + ", " +
SELECT_ORIGINATOR_LABEL + ", " +
SELECT_ASSIGNEE_INFO + ", ";
" a.originator_name as originator_name, " +
" a.originator_label as originator_label, " +
" 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 LEFT_JOIN_TB_USERS = "left join tb_user tbu on tbu.id = a.assignee_id ";
protected final NamedParameterJdbcTemplate jdbcTemplate;
private final TransactionTemplate transactionTemplate;
@ -173,12 +128,12 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
@Override
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
return transactionTemplate.execute(status -> {
return transactionTemplate.execute(trStatus -> {
AlarmDataPageLink pageLink = query.getPageLink();
QueryContext ctx = new QueryContext(new QuerySecurityContext(tenantId, null, EntityType.ALARM));
ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList()));
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 sortPart = new StringBuilder(" order by ");
StringBuilder joinPart = new StringBuilder();
@ -191,7 +146,6 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
} else {
selectPart.append(" a.originator_id as entity_id ");
}
fromPart.append(LEFT_JOIN_TB_USERS);
EntityDataSortOrder sortOrder = pageLink.getSortOrder();
List<EntityKey> alarmFields = new ArrayList<>();
@ -209,6 +163,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
String textSearchQuery = buildTextSearchQuery(ctx, alarmFields, pageLink.getTextSearch());
if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) {
String sortOrderKey = sortOrder.getKey().getKey();
if ("status".equalsIgnoreCase(sortOrderKey)) {
selectPart.append(", a.status as status ");
}
sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey))
.append(" ").append(sortOrder.getDirection().name());
if (pageLink.isSearchPropagatedAlarms()) {
@ -291,13 +248,19 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
wherePart.append("a.severity in (:alarmSeverities)");
}
if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) {
Set<AlarmStatus> statusSet = toStatusSet(pageLink.getStatusList());
if (!statusSet.isEmpty()) {
AlarmStatusFilter asf = AlarmStatusFilter.fromList(pageLink.getStatusList());
if (asf.hasAnyFilter()) {
if (asf.hasAckFilter()) {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList()));
wherePart.append(" a.status in (:alarmStatuses)");
ctx.addBooleanParameter("ackStatus", asf.getAckFilter());
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();
}
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) {
if (addAnd) {
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_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);

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

@ -53,14 +53,15 @@ CREATE TABLE IF NOT EXISTS alarm (
severity varchar(255),
start_ts bigint,
assign_ts bigint,
status varchar(255),
assignee_id uuid,
tenant_id uuid,
customer_id uuid,
propagate_relation_types varchar,
type varchar(255),
propagate_to_owner boolean,
propagate_to_tenant boolean
propagate_to_tenant boolean,
acknowledged boolean,
cleared boolean
);
CREATE TABLE IF NOT EXISTS alarm_comment (
@ -802,6 +803,10 @@ CREATE TABLE IF NOT EXISTS user_settings (
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)

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()))
.type(TEST_ALARM)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.startTs(System.currentTimeMillis()).build();
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();
Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId)
.type(TEST_ALARM)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.acknowledged(false)
.cleared(false)
.startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm);
@ -131,7 +133,9 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId)
.type(TEST_ALARM)
.propagate(false)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.acknowledged(false)
.cleared(false)
.startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm);
@ -235,7 +239,9 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId)
.type(TEST_ALARM)
.propagate(false)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.acknowledged(false)
.cleared(false)
.startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm);
@ -316,7 +322,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(tenantDevice.getId())
.type(TEST_ALARM)
.propagate(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm);
tenantAlarm = result.getAlarm();
@ -325,7 +331,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(customerDevice.getId())
.type(TEST_ALARM)
.propagate(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.startTs(ts).build();
result = alarmService.createOrUpdateAlarm(deviceAlarm);
deviceAlarm = result.getAlarm();
@ -392,7 +398,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(tenantDevice.getId())
.type("Not Propagated")
.propagate(false)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm);
tenantAlarm = result.getAlarm();
@ -401,7 +407,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(tenantDevice.getId())
.type("Propagated")
.propagate(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.startTs(ts).build();
result = alarmService.createOrUpdateAlarm(customerAlarm);
customerAlarm = result.getAlarm();
@ -442,7 +448,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(device.getId())
.type("Propagated To Tenant")
.propagateToTenant(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm);
tenantAlarm = result.getAlarm();
@ -452,7 +458,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.type("Propagated to Customer")
.propagate(false)
.propagateToOwner(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.startTs(ts).build();
result = alarmService.createOrUpdateAlarm(customerAlarm);
customerAlarm = result.getAlarm();
@ -509,7 +515,8 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(customerDevice.getId())
.type(TEST_ALARM)
.severity(AlarmSeverity.MAJOR)
.status(AlarmStatus.ACTIVE_UNACK)
.acknowledged(false)
.cleared(false)
.startTs(System.currentTimeMillis())
.build();
alarm1 = alarmService.createOrUpdateAlarm(alarm1).getAlarm();
@ -520,7 +527,8 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(customerDevice.getId())
.type(TEST_ALARM)
.severity(AlarmSeverity.MINOR)
.status(AlarmStatus.ACTIVE_ACK)
.acknowledged(true)
.cleared(false)
.startTs(System.currentTimeMillis())
.build();
alarm2 = alarmService.createOrUpdateAlarm(alarm2).getAlarm();
@ -531,7 +539,8 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.originator(customerDevice.getId())
.type(TEST_ALARM)
.severity(AlarmSeverity.CRITICAL)
.status(AlarmStatus.ACTIVE_ACK)
.acknowledged(true)
.cleared(false)
.startTs(System.currentTimeMillis())
.build();
alarm3 = alarmService.createOrUpdateAlarm(alarm3).getAlarm();
@ -560,7 +569,6 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
.type(TEST_ALARM)
.propagate(false)
.severity(AlarmSeverity.CRITICAL)
.status(AlarmStatus.ACTIVE_UNACK)
.startTs(ts).build();
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm);
@ -720,7 +728,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId)
.type(TEST_ALARM)
.propagate(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.startTs(ts).build();
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.setStartTs(System.currentTimeMillis());
alarm.setEndTs(System.currentTimeMillis());
alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
alarmDao.save(TenantId.fromUUID(tenantId), alarm);
}
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.setStartTs(System.currentTimeMillis());
alarm.setEndTs(System.currentTimeMillis());
alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
alarm.setAcknowledged(false);
alarm.setCleared(false);
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.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));
}, 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) {
msgAlarm.setOriginator(msg.getOriginator());
}
if (msgAlarm.getStatus() == null) {
msgAlarm.setStatus(AlarmStatus.ACTIVE_UNACK);
}
return msgAlarm;
}
@ -195,7 +192,8 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
return Alarm.builder()
.tenantId(tenantId)
.originator(msg.getOriginator())
.status(AlarmStatus.ACTIVE_UNACK)
.cleared(false)
.acknowledged(false)
.severity(this.config.isDynamicSeverity() ? processAlarmSeverity(msg) : notDynamicAlarmSeverity)
.propagate(config.isPropagate())
.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 {
currentAlarm = new Alarm();
currentAlarm.setType(alarmDefinition.getAlarmType());
currentAlarm.setStatus(AlarmStatus.ACTIVE_UNACK);
currentAlarm.setAcknowledged(false);
currentAlarm.setCleared(false);
currentAlarm.setSeverity(severity);
long startTs = dataSnapshot.getTs();
if (startTs == 0L) {
@ -342,7 +343,7 @@ class AlarmState {
public void processAckAlarm(Alarm alarm) {
if (currentAlarm != null && currentAlarm.getId().equals(alarm.getId())) {
currentAlarm.setStatus(alarm.getStatus());
currentAlarm.setAcknowledged(alarm.isAcknowledged());
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
private ArgumentCaptor<Consumer<Throwable>> failureCaptor;
private RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased());
private RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased());
private final RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased());
private final RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased());
private ListeningExecutor dbExecutor;
private EntityId originator = new DeviceId(Uuids.timeBased());
private EntityId alarmOriginator = new AlarmId(Uuids.timeBased());
private TenantId tenantId = TenantId.fromUUID(Uuids.timeBased());
private TbMsgMetaData metaData = new TbMsgMetaData();
private String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
private final EntityId originator = new DeviceId(Uuids.timeBased());
private final EntityId alarmOriginator = new AlarmId(Uuids.timeBased());
private final TenantId tenantId = TenantId.fromUUID(Uuids.timeBased());
private final TbMsgMetaData metaData = new TbMsgMetaData();
private final String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
@Before
public void before() {
@ -158,7 +158,6 @@ public class TbAlarmNodeTest {
.endTs(ts)
.tenantId(tenantId)
.originator(originator)
.status(ACTIVE_UNACK)
.severity(CRITICAL)
.propagate(true)
.type("SomeType")
@ -197,7 +196,7 @@ public class TbAlarmNodeTest {
metaData.putValue("key", "value");
TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
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(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(clearedAlarm));
@ -230,7 +229,6 @@ public class TbAlarmNodeTest {
.endTs(ts)
.tenantId(tenantId)
.originator(originator)
.status(ACTIVE_UNACK)
.severity(CRITICAL)
.propagate(true)
.type("SomeType")
@ -247,7 +245,7 @@ public class TbAlarmNodeTest {
TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
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(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(activeAlarm));
@ -278,7 +276,6 @@ public class TbAlarmNodeTest {
Alarm expectedAlarm = Alarm.builder()
.tenantId(tenantId)
.originator(originator)
.status(ACTIVE_UNACK)
.severity(CRITICAL)
.propagate(true)
.type("SomeType")
@ -296,7 +293,7 @@ public class TbAlarmNodeTest {
TbMsg msg = TbMsg.newMsg( "USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
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(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(activeAlarm));
@ -328,7 +325,7 @@ public class TbAlarmNodeTest {
Alarm expectedAlarm = Alarm.builder()
.tenantId(tenantId)
.originator(originator)
.status(CLEARED_UNACK)
.cleared(true)
.severity(WARNING)
.propagate(false)
.type("SomeType")
@ -347,7 +344,7 @@ public class TbAlarmNodeTest {
long oldEndDate = System.currentTimeMillis();
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);
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
@ -378,7 +375,7 @@ public class TbAlarmNodeTest {
Alarm expectedAlarm = Alarm.builder()
.tenantId(tenantId)
.originator(originator)
.status(CLEARED_UNACK)
.cleared(true)
.severity(WARNING)
.propagate(false)
.type("SomeType")
@ -444,7 +441,6 @@ public class TbAlarmNodeTest {
.endTs(ts)
.tenantId(tenantId)
.originator(originator)
.status(ACTIVE_UNACK)
.severity(WARNING)
.propagate(true)
.type("SomeType")
@ -507,7 +503,6 @@ public class TbAlarmNodeTest {
.endTs(ts)
.tenantId(tenantId)
.originator(originator)
.status(ACTIVE_UNACK)
.severity(WARNING)
.propagate(true)
.type("SomeType")
@ -571,7 +566,6 @@ public class TbAlarmNodeTest {
.endTs(ts)
.tenantId(tenantId)
.originator(originator)
.status(ACTIVE_UNACK)
.severity(CRITICAL)
.propagateToTenant(true)
.type("SomeType" + i)

Loading…
Cancel
Save