From 69b5a5f4258f4004ba1ffb219b7e85ae3d62e06d Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 23 Feb 2023 19:03:04 +0200 Subject: [PATCH] Alarm Service refactoring --- .../main/data/upgrade/3.4.4/schema_update.sql | 223 +++++++++++++- .../processor/alarm/BaseAlarmProcessor.java | 6 +- .../entitiy/alarm/DefaultTbAlarmService.java | 14 +- .../queue/DefaultTbCoreConsumerService.java | 6 +- .../DefaultSubscriptionManagerService.java | 21 +- .../SubscriptionManagerService.java | 5 +- .../subscription/TbAlarmDataSubCtx.java | 3 +- .../subscription/TbSubscriptionUtils.java | 10 +- .../DefaultAlarmSubscriptionService.java | 35 ++- .../sub/AlarmSubscriptionUpdate.java | 12 +- .../controller/BaseAlarmControllerTest.java | 38 ++- .../alarm/DefaultTbAlarmServiceTest.java | 2 - common/cluster-api/src/main/proto/queue.proto | 2 - .../server/dao/alarm/AlarmApiCallResult.java | 8 +- .../server/dao/alarm/AlarmService.java | 11 +- ... => AlarmCreateOrUpdateActiveRequest.java} | 7 +- .../common/data/alarm/AlarmUpdateRequest.java | 1 + .../server/common/data/query/AlarmData.java | 30 +- .../server/dao/alarm/AlarmDao.java | 6 +- .../server/dao/alarm/BaseAlarmService.java | 17 +- .../server/dao/sql/alarm/AlarmRepository.java | 5 + .../server/dao/sql/alarm/JpaAlarmDao.java | 13 +- .../dao/sql/query/AlarmDataAdapter.java | 4 +- .../main/resources/sql/schema-entities.sql | 4 +- .../dao/service/BaseAlarmServiceTest.java | 226 +++++++-------- .../server/dao/sql/alarm/JpaAlarmDaoTest.java | 14 +- .../engine/api/RuleEngineAlarmService.java | 6 +- .../rule/engine/action/TbAlarmResult.java | 6 + .../rule/engine/action/TbClearAlarmNode.java | 17 +- .../rule/engine/action/TbCreateAlarmNode.java | 42 ++- .../rule/engine/profile/AlarmState.java | 24 +- .../rule/engine/action/TbAlarmNodeTest.java | 273 ++++++++++-------- .../rule/engine/profile/DeviceStateTest.java | 17 +- .../profile/TbDeviceProfileNodeTest.java | 122 ++++---- 34 files changed, 746 insertions(+), 484 deletions(-) rename common/data/src/main/java/org/thingsboard/server/common/data/alarm/{CreateOrUpdateActiveAlarmRequest.java => AlarmCreateOrUpdateActiveRequest.java} (93%) diff --git a/application/src/main/data/upgrade/3.4.4/schema_update.sql b/application/src/main/data/upgrade/3.4.4/schema_update.sql index c907145c5e..1cb9aaaf5b 100644 --- a/application/src/main/data/upgrade/3.4.4/schema_update.sql +++ b/application/src/main/data/upgrade/3.4.4/schema_update.sql @@ -35,11 +35,12 @@ UPDATE alarm SET acknowledged = false, cleared = true WHERE status = 'CLEARED_UN 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; +DROP INDEX IF EXISTS idx_alarm_originator_alarm_type_active; +CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type_active + ON alarm USING btree (originator_id, type, created_time DESC) WHERE cleared = false; -- Cover index by alarm type to optimize propagated alarm queries; +DROP INDEX IF EXISTS idx_entity_alarm_entity_id_alarm_type_created_time_alarm_id; 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); @@ -70,7 +71,7 @@ CREATE TABLE IF NOT EXISTS user_settings ( -- ALARM INFO VIEW -DROP VIEW IF EXISTS alarm_info; +DROP VIEW IF EXISTS alarm_info CASCADE; CREATE VIEW alarm_info AS SELECT a.*, (CASE WHEN a.acknowledged AND a.cleared THEN 'CLEARED_ACK' @@ -103,3 +104,217 @@ u.first_name as assignee_first_name, u.last_name as assignee_last_name, u.email FROM alarm a LEFT JOIN tb_user u ON u.id = a.assignee_id; +-- ALARM INFO VIEW END + +-- ALARM FUNCTIONS START + +CREATE OR REPLACE FUNCTION create_or_update_active_alarm( + t_id uuid, c_id uuid, a_id uuid, a_created_ts bigint, + a_o_id uuid, a_o_type integer, a_type varchar, + a_severity varchar, a_start_ts bigint, a_end_ts bigint, + a_details varchar, + a_propagate boolean, a_propagate_to_owner boolean, + a_propagate_to_tenant boolean, a_propagation_types varchar, + a_creation_enabled boolean) + RETURNS varchar + LANGUAGE plpgsql +AS +$$ +DECLARE + null_id constant uuid = '13814000-1dd2-11b2-8080-808080808080'::uuid; + existing alarm; + result alarm_info; + row_count integer; +BEGIN + SELECT * INTO existing FROM alarm a WHERE a.originator_id = a_o_id AND a.type = a_type ORDER BY a.start_ts DESC FOR UPDATE; + IF existing.id IS NULL OR existing.cleared IS TRUE THEN + IF a_creation_enabled = FALSE THEN + RETURN json_build_object('success', false)::text; + END IF; + IF c_id = null_id THEN + c_id = NULL; + end if; + INSERT INTO alarm + (tenant_id, customer_id, id, created_time, + originator_id, originator_type, type, + severity, start_ts, end_ts, + additional_info, + propagate, propagate_to_owner, propagate_to_tenant, propagate_relation_types, + acknowledged, ack_ts, + cleared, clear_ts, + assignee_id, assign_ts) + VALUES + (t_id, c_id, a_id, a_created_ts, + a_o_id, a_o_type, a_type, + a_severity, a_start_ts, a_end_ts, + a_details, + a_propagate, a_propagate_to_owner, a_propagate_to_tenant, a_propagation_types, + false, 0, false, 0, NULL, 0); + SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; + RETURN json_build_object('success', true, 'created', true, 'modified', true, 'alarm', row_to_json(result))::text; + ELSE + UPDATE alarm a + SET severity = a_severity, + start_ts = a_start_ts, + end_ts = a_end_ts, + additional_info = a_details, + propagate = a_propagate, + propagate_to_owner = a_propagate_to_owner, + propagate_to_tenant = a_propagate_to_tenant, + propagate_relation_types = a_propagation_types + WHERE a.id = existing.id + AND a.tenant_id = t_id + AND (severity != a_severity OR start_ts != a_start_ts OR end_ts != a_end_ts OR additional_info != a_details + OR propagate != a_propagate OR propagate_to_owner != a_propagate_to_owner OR + propagate_to_tenant != a_propagate_to_tenant OR propagate_relation_types != a_propagation_types); + GET DIAGNOSTICS row_count = ROW_COUNT; + SELECT * INTO result FROM alarm_info a WHERE a.id = existing.id AND a.tenant_id = t_id; + IF row_count > 0 THEN + RETURN json_build_object('success', true, 'modified', true, 'alarm', row_to_json(result), 'old', row_to_json(existing))::text; + ELSE + RETURN json_build_object('success', true, 'modified', false, 'alarm', row_to_json(result))::text; + END IF; + END IF; +END +$$; + +DROP FUNCTION IF EXISTS update_alarm; +CREATE OR REPLACE FUNCTION update_alarm(t_id uuid, a_id uuid, a_severity varchar, a_start_ts bigint, a_end_ts bigint, + a_details varchar, + a_propagate boolean, a_propagate_to_owner boolean, + a_propagate_to_tenant boolean, a_propagation_types varchar) + RETURNS varchar + LANGUAGE plpgsql +AS +$$ +DECLARE + existing alarm; + result alarm_info; + row_count integer; +BEGIN + SELECT * INTO existing FROM alarm a WHERE a.id = a_id AND a.tenant_id = t_id FOR UPDATE; + IF existing IS NULL THEN + RETURN json_build_object('success', false)::text; + END IF; + UPDATE alarm a + SET severity = a_severity, + start_ts = a_start_ts, + end_ts = a_end_ts, + additional_info = a_details, + propagate = a_propagate, + propagate_to_owner = a_propagate_to_owner, + propagate_to_tenant = a_propagate_to_tenant, + propagate_relation_types = a_propagation_types + WHERE a.id = a_id + AND a.tenant_id = t_id + AND (severity != a_severity OR start_ts != a_start_ts OR end_ts != a_end_ts OR additional_info != a_details + OR propagate != a_propagate OR propagate_to_owner != a_propagate_to_owner OR + propagate_to_tenant != a_propagate_to_tenant OR propagate_relation_types != a_propagation_types); + GET DIAGNOSTICS row_count = ROW_COUNT; + SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; + IF row_count > 0 THEN + RETURN json_build_object('success', true, 'modified', row_count > 0, 'alarm', row_to_json(result), 'old', row_to_json(existing))::text; + ELSE + RETURN json_build_object('success', true, 'modified', row_count > 0, 'alarm', row_to_json(result))::text; + END IF; +END +$$; + +DROP FUNCTION IF EXISTS acknowledge_alarm; +CREATE OR REPLACE FUNCTION acknowledge_alarm(t_id uuid, a_id uuid, a_ts bigint) + RETURNS varchar + LANGUAGE plpgsql +AS +$$ +DECLARE + existing alarm; + result alarm_info; + modified boolean = FALSE; +BEGIN + SELECT * INTO existing FROM alarm a WHERE a.id = a_id AND a.tenant_id = t_id FOR UPDATE; + IF existing IS NULL THEN + RETURN json_build_object('success', false)::text; + END IF; + + IF NOT (existing.acknowledged) THEN + modified = TRUE; + UPDATE alarm a SET acknowledged = true, ack_ts = a_ts WHERE a.id = a_id AND a.tenant_id = t_id; + END IF; + SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; + RETURN json_build_object('success', true, 'modified', modified, 'alarm', row_to_json(result))::text; +END +$$; + +DROP FUNCTION IF EXISTS clear_alarm; +CREATE OR REPLACE FUNCTION clear_alarm(t_id uuid, a_id uuid, a_ts bigint, a_details varchar) + RETURNS varchar + LANGUAGE plpgsql +AS +$$ +DECLARE + existing alarm; + result alarm_info; + cleared boolean = FALSE; +BEGIN + SELECT * INTO existing FROM alarm a WHERE a.id = a_id AND a.tenant_id = t_id FOR UPDATE; + IF existing IS NULL THEN + RETURN json_build_object('success', false)::text; + END IF; + IF NOT(existing.cleared) THEN + cleared = TRUE; + UPDATE alarm a SET cleared = true, clear_ts = a_ts, additional_info = a_details WHERE a.id = a_id AND a.tenant_id = t_id; + END IF; + SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; + RETURN json_build_object('success', true, 'cleared', cleared, 'alarm', row_to_json(result))::text; +END +$$; + +DROP FUNCTION IF EXISTS assign_alarm; +CREATE OR REPLACE FUNCTION assign_alarm(t_id uuid, a_id uuid, u_id uuid, a_ts bigint) + RETURNS varchar + LANGUAGE plpgsql +AS +$$ +DECLARE + existing alarm; + result alarm_info; + modified boolean = FALSE; +BEGIN + SELECT * INTO existing FROM alarm a WHERE a.id = a_id AND a.tenant_id = t_id FOR UPDATE; + IF existing IS NULL THEN + RETURN json_build_object('success', false)::text; + END IF; + IF existing.assignee_id IS NULL OR existing.assignee_id != u_id THEN + modified = TRUE; + UPDATE alarm a SET assignee_id = u_id, assign_ts = a_ts WHERE a.id = a_id AND a.tenant_id = t_id; + END IF; + SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; + RETURN json_build_object('success', true, 'modified', modified, 'alarm', row_to_json(result))::text; +END +$$; + +DROP FUNCTION IF EXISTS unassign_alarm; +CREATE OR REPLACE FUNCTION unassign_alarm(t_id uuid, a_id uuid, a_ts bigint) + RETURNS varchar + LANGUAGE plpgsql +AS +$$ +DECLARE + existing alarm; + result alarm_info; + modified boolean = FALSE; +BEGIN + SELECT * INTO existing FROM alarm a WHERE a.id = a_id AND a.tenant_id = t_id FOR UPDATE; + IF existing IS NULL THEN + RETURN json_build_object('success', false)::text; + END IF; + IF existing.assignee_id IS NOT NULL THEN + modified = TRUE; + UPDATE alarm a SET assignee_id = NULL, assign_ts = a_ts WHERE a.id = a_id AND a.tenant_id = t_id; + END IF; + SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; + RETURN json_build_object('success', true, 'modified', modified, 'alarm', row_to_json(result))::text; +END +$$; + +-- ALARM FUNCTIONS END \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java index f82f97ebee..d85750f7d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/alarm/BaseAlarmProcessor.java @@ -48,7 +48,7 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { return Futures.immediateFuture(null); } try { - Alarm existentAlarm = alarmService.findLatestByOriginatorAndType(tenantId, originatorId, alarmUpdateMsg.getType()).get(); + Alarm existentAlarm = alarmService.findLatestActiveByOriginatorAndType(tenantId, originatorId, alarmUpdateMsg.getType()); switch (alarmUpdateMsg.getMsgType()) { case ENTITY_CREATED_RPC_MESSAGE: case ENTITY_UPDATED_RPC_MESSAGE: @@ -78,12 +78,12 @@ public abstract class BaseAlarmProcessor extends BaseEdgeProcessor { case ALARM_CLEAR_RPC_MESSAGE: if (existentAlarm != null) { alarmService.clearAlarm(tenantId, existentAlarm.getId(), - JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails()), alarmUpdateMsg.getAckTs()); + alarmUpdateMsg.getAckTs(), JacksonUtil.OBJECT_MAPPER.readTree(alarmUpdateMsg.getDetails())); } break; case ENTITY_DELETED_RPC_MESSAGE: if (existentAlarm != null) { - alarmService.deleteAlarm(tenantId, existentAlarm.getId()); + alarmService.delAlarm(tenantId, existentAlarm.getId()); } break; } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java index cd2c6d4101..012a5b0b8f 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java @@ -15,9 +15,6 @@ */ package org.thingsboard.server.service.entitiy.alarm; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; @@ -28,9 +25,8 @@ import org.thingsboard.server.common.data.alarm.AlarmAssignee; import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmCommentType; import org.thingsboard.server.common.data.alarm.AlarmInfo; -import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; -import org.thingsboard.server.common.data.alarm.CreateOrUpdateActiveAlarmRequest; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -38,7 +34,6 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.dao.alarm.AlarmApiCallResult; -import org.thingsboard.server.dao.alarm.AlarmOperationResult; import org.thingsboard.server.service.entitiy.AbstractTbEntityService; import java.util.List; @@ -52,18 +47,17 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb ActionType actionType = alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = alarm.getTenantId(); try { - Alarm savedAlarm = checkNotNull(alarmSubscriptionService.createOrUpdateAlarm(alarm)); AlarmApiCallResult result; if (alarm.getId() == null) { - result = alarmSubscriptionService.createAlarm(CreateOrUpdateActiveAlarmRequest.fromAlarm(alarm)); + result = alarmSubscriptionService.createAlarm(AlarmCreateOrUpdateActiveRequest.fromAlarm(alarm)); } else { result = alarmSubscriptionService.updateAlarm(AlarmUpdateRequest.fromAlarm(alarm)); } actionType = result.isCreated() ? ActionType.ADDED : ActionType.UPDATED; if (result.isModified()) { - notificationEntityService.notifyCreateOrUpdateAlarm(savedAlarm, actionType, user); + notificationEntityService.notifyCreateOrUpdateAlarm(result.getAlarm(), actionType, user); } - return savedAlarm; + return new Alarm(result.getAlarm()); } catch (Exception e) { notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ALARM), alarm, actionType, user, e); throw e; diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index b761ffa3fe..b55b965330 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -26,6 +26,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rpc.RpcError; @@ -503,15 +504,14 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService { if (TbSubscriptionType.ALARMS.equals(s.getType())) { @@ -303,13 +304,13 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene } }, s -> alarm.getCreatedTime() >= s.getTs() || alarm.getAssignTs() >= s.getTs(), - alarm, assignee, false + alarm, false ); callback.onSuccess(); } @Override - public void onAlarmDeleted(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback) { + public void onAlarmDeleted(TenantId tenantId, EntityId entityId, AlarmInfo alarm, TbCallback callback) { onLocalAlarmSubUpdate(entityId, s -> { if (TbSubscriptionType.ALARMS.equals(s.getType())) { @@ -319,7 +320,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene } }, s -> alarm.getCreatedTime() >= s.getTs(), - alarm, null, true + alarm, true ); callback.onSuccess(); } @@ -413,8 +414,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene private void onLocalAlarmSubUpdate(EntityId entityId, Function castFunction, Predicate filterFunction, - Alarm alarm, AlarmAssigneeUpdate assignee, - boolean deleted) { + AlarmInfo alarm, boolean deleted) { Set entitySubscriptions = subscriptionsByEntityId.get(entityId); if (alarm == null) { log.warn("[{}] empty alarm update!", entityId); @@ -423,11 +423,11 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene if (entitySubscriptions != null) { entitySubscriptions.stream().map(castFunction).filter(Objects::nonNull).filter(filterFunction).forEach(s -> { if (serviceId.equals(s.getServiceId())) { - AlarmSubscriptionUpdate update = new AlarmSubscriptionUpdate(s.getSubscriptionId(), alarm, assignee, deleted); + AlarmSubscriptionUpdate update = new AlarmSubscriptionUpdate(s.getSubscriptionId(), alarm, deleted); localSubscriptionService.onSubscriptionUpdate(s.getSessionId(), update, TbCallback.EMPTY); } else { TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, s.getServiceId()); - toCoreNotificationsProducer.send(tpi, toProto(s, alarm, assignee, deleted), null); + toCoreNotificationsProducer.send(tpi, toProto(s, alarm, deleted), null); } }); } else { @@ -563,15 +563,12 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene return new TbProtoQueueMsg<>(subscription.getEntityId().getId(), toCoreMsg); } - private TbProtoQueueMsg toProto(TbSubscription subscription, Alarm alarm, AlarmAssigneeUpdate assignee, boolean deleted) { + private TbProtoQueueMsg toProto(TbSubscription subscription, AlarmInfo alarm, boolean deleted) { TbAlarmSubscriptionUpdateProto.Builder builder = TbAlarmSubscriptionUpdateProto.newBuilder(); builder.setSessionId(subscription.getSessionId()); builder.setSubscriptionId(subscription.getSubscriptionId()); builder.setAlarm(JacksonUtil.toString(alarm)); - if (assignee != null) { - builder.setAssignee(JacksonUtil.toString(assignee)); - } builder.setDeleted(deleted); ToCoreNotificationMsg toCoreMsg = ToCoreNotificationMsg.newBuilder().setToLocalSubscriptionServiceMsg( diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java index 2c3423565a..410b2a4a1c 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/SubscriptionManagerService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.subscription; import org.springframework.context.ApplicationListener; import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; @@ -43,9 +44,9 @@ public interface SubscriptionManagerService extends ApplicationListener keys, TbCallback callback); - void onAlarmUpdate(TenantId tenantId, EntityId entityId, Alarm alarm, AlarmAssigneeUpdate assignee, TbCallback callback); + void onAlarmUpdate(TenantId tenantId, EntityId entityId, AlarmInfo alarm, TbCallback callback); - void onAlarmDeleted(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback); + void onAlarmDeleted(TenantId tenantId, EntityId entityId, AlarmInfo alarm, TbCallback callback); } diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java index 70b889bfe6..c90302998a 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbAlarmDataSubCtx.java @@ -225,8 +225,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx { boolean matchesFilter = filter(alarm); if (onCurrentPage) { if (matchesFilter) { - AlarmData updated = current.update(alarm, subscriptionUpdate.getAssignee()); - updated.getLatest().putAll(current.getLatest()); + AlarmData updated = new AlarmData(subscriptionUpdate.getAlarm(), current); alarmsMap.put(alarmId, updated); sendWsMsg(new AlarmDataUpdate(cmdId, null, Collections.singletonList(updated), maxEntitiesPerAlarmSubscription, data.getTotalElements())); } else { diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java index a8b16003cb..c30ac9f0d5 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/TbSubscriptionUtils.java @@ -191,9 +191,8 @@ public class TbSubscriptionUtils { if (proto.getErrorCode() > 0) { return new AlarmSubscriptionUpdate(proto.getSubscriptionId(), SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg()); } else { - Alarm alarm = JacksonUtil.fromString(proto.getAlarm(), Alarm.class); - AlarmAssigneeUpdate assigneeUpdate = JacksonUtil.fromString(proto.getAlarm(), AlarmAssigneeUpdate.class); - return new AlarmSubscriptionUpdate(proto.getSubscriptionId(), alarm, assigneeUpdate, proto.getDeleted()); + AlarmInfo alarm = JacksonUtil.fromString(proto.getAlarm(), AlarmInfo.class); + return new AlarmSubscriptionUpdate(proto.getSubscriptionId(), alarm, proto.getDeleted()); } } @@ -319,7 +318,7 @@ public class TbSubscriptionUtils { return entry; } - public static ToCoreMsg toAlarmUpdateProto(TenantId tenantId, EntityId entityId, AlarmAssigneeUpdate assigneeUpdate, Alarm alarm) { + public static ToCoreMsg toAlarmUpdateProto(TenantId tenantId, EntityId entityId, AlarmInfo alarm) { TbAlarmUpdateProto.Builder builder = TbAlarmUpdateProto.newBuilder(); builder.setEntityType(entityId.getEntityType().name()); builder.setEntityIdMSB(entityId.getId().getMostSignificantBits()); @@ -327,13 +326,12 @@ public class TbSubscriptionUtils { builder.setTenantIdMSB(tenantId.getId().getMostSignificantBits()); builder.setTenantIdLSB(tenantId.getId().getLeastSignificantBits()); builder.setAlarm(JacksonUtil.toString(alarm)); - builder.setAssignee(JacksonUtil.toString(assigneeUpdate)); SubscriptionMgrMsgProto.Builder msgBuilder = SubscriptionMgrMsgProto.newBuilder(); msgBuilder.setAlarmUpdate(builder); return ToCoreMsg.newBuilder().setToSubscriptionMgrMsg(msgBuilder.build()).build(); } - public static ToCoreMsg toAlarmDeletedProto(TenantId tenantId, EntityId entityId, Alarm alarm) { + public static ToCoreMsg toAlarmDeletedProto(TenantId tenantId, EntityId entityId, AlarmInfo alarm) { TbAlarmDeleteProto.Builder builder = TbAlarmDeleteProto.newBuilder(); builder.setEntityType(entityId.getEntityType().name()); builder.setEntityIdMSB(entityId.getId().getMostSignificantBits()); diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java index e9c0f5247c..0f8a9af900 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java @@ -34,7 +34,7 @@ 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.AlarmUpdateRequest; -import org.thingsboard.server.common.data.alarm.CreateOrUpdateActiveAlarmRequest; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -97,7 +97,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService } @Override - public AlarmApiCallResult createAlarm(CreateOrUpdateActiveAlarmRequest request) { + public AlarmApiCallResult createAlarm(AlarmCreateOrUpdateActiveRequest request) { boolean creationEnabled = apiUsageStateService.getApiUsageState(request.getTenantId()).isAlarmCreationEnabled(); return withWsCallback(alarmService.createAlarm(request, creationEnabled)); } @@ -150,7 +150,7 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService @Override public Boolean deleteAlarm(TenantId tenantId, AlarmId alarmId) { - AlarmOperationResult result = alarmService.deleteAlarm(tenantId, alarmId); + AlarmApiCallResult result = alarmService.delAlarm(tenantId, alarmId); onAlarmDeleted(result); return result.isSuccessful(); } @@ -210,6 +210,11 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService return alarmService.findAlarmDataByQueryForEntities(tenantId, query, orderedEntityIds); } + @Override + public Alarm findLatestActiveByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { + return alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, type); + } + @Override public ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { return alarmService.findLatestByOriginatorAndType(tenantId, originator, type); @@ -224,12 +229,12 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); if (currentPartitions.contains(tpi)) { if (subscriptionManagerService.isPresent()) { - subscriptionManagerService.get().onAlarmUpdate(tenantId, entityId, alarm, null, TbCallback.EMPTY); + subscriptionManagerService.get().onAlarmUpdate(tenantId, entityId, new AlarmInfo(alarm), TbCallback.EMPTY); } else { log.warn("Possible misconfiguration because subscriptionManagerService is null!"); } } else { - TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, null, alarm); + TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, new AlarmInfo(alarm)); clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null); } } @@ -238,27 +243,27 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService private void onAlarmUpdated(AlarmApiCallResult result) { wsCallBackExecutor.submit(() -> { - Alarm alarm = result.getAlarm(); + AlarmInfo alarm = result.getAlarm(); TenantId tenantId = alarm.getTenantId(); for (EntityId entityId : result.getPropagatedEntitiesList()) { TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); if (currentPartitions.contains(tpi)) { if (subscriptionManagerService.isPresent()) { - subscriptionManagerService.get().onAlarmUpdate(tenantId, entityId, alarm, result.getAssigneeUpdate(), TbCallback.EMPTY); + subscriptionManagerService.get().onAlarmUpdate(tenantId, entityId, alarm, TbCallback.EMPTY); } else { log.warn("Possible misconfiguration because subscriptionManagerService is null!"); } } else { - TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, result.getAssigneeUpdate(), alarm); + TransportProtos.ToCoreMsg toCoreMsg = TbSubscriptionUtils.toAlarmUpdateProto(tenantId, entityId, alarm); clusterService.pushMsgToCore(tpi, entityId.getId(), toCoreMsg, null); } } }); } - private void onAlarmDeleted(AlarmOperationResult result) { + private void onAlarmDeleted(AlarmApiCallResult result) { wsCallBackExecutor.submit(() -> { - Alarm alarm = result.getAlarm(); + AlarmInfo alarm = result.getAlarm(); TenantId tenantId = alarm.getTenantId(); for (EntityId entityId : result.getPropagatedEntitiesList()) { TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, tenantId, entityId); @@ -291,6 +296,16 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService private AlarmApiCallResult withWsCallback(AlarmApiCallResult result) { if (result.isSuccessful() && result.isModified()) { Futures.addCallback(Futures.immediateFuture(result), new AlarmUpdateCallback(), wsCallBackExecutor); + if (result.isSeverityChanged()) { + AlarmInfo alarm = result.getAlarm(); + AlarmComment alarmComment = AlarmComment.builder() + .alarmId(alarm.getId()) + .type(AlarmCommentType.SYSTEM) + .comment(JacksonUtil.newObjectNode().put("text", + String.format("Alarm severity was updated from %s to %s", result.getOldSeverity(), alarm.getSeverity()))) + .build(); + alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment); + } } return result; } diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java index 201eaba9b2..4961f901c5 100644 --- a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java +++ b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.telemetry.sub; import lombok.Getter; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmAssigneeUpdate; +import org.thingsboard.server.common.data.alarm.AlarmInfo; public class AlarmSubscriptionUpdate { @@ -28,21 +29,18 @@ public class AlarmSubscriptionUpdate { @Getter private String errorMsg; @Getter - private Alarm alarm; - @Getter - private AlarmAssigneeUpdate assignee; + private AlarmInfo alarm; @Getter private boolean alarmDeleted; - public AlarmSubscriptionUpdate(int subscriptionId, Alarm alarm) { - this(subscriptionId, alarm, null, false); + public AlarmSubscriptionUpdate(int subscriptionId, AlarmInfo alarm) { + this(subscriptionId, alarm, false); } - public AlarmSubscriptionUpdate(int subscriptionId, Alarm alarm, AlarmAssigneeUpdate assignee, boolean alarmDeleted) { + public AlarmSubscriptionUpdate(int subscriptionId, AlarmInfo alarm, boolean alarmDeleted) { super(); this.subscriptionId = subscriptionId; this.alarm = alarm; - this.assignee = assignee; this.alarmDeleted = alarmDeleted; } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java index c82f160474..b7234f1008 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java @@ -125,7 +125,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Assert.assertNotNull(updatedAlarm); Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity()); - testNotifyEntityAllOneTime(updatedAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(), + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); + testNotifyEntityAllOneTime(foundAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.UPDATED); } @@ -141,7 +142,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Assert.assertNotNull(updatedAlarm); Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity()); - testNotifyEntityAllOneTime(updatedAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(), + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class); + testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.UPDATED); } @@ -188,7 +190,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(), + testNotifyEntityOneTimeMsgToEdgeServiceNever(new Alarm(alarm), alarm.getId(), alarm.getOriginator(), tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED); } @@ -201,7 +203,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk()); - testNotifyEntityOneTimeMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(), + testNotifyEntityOneTimeMsgToEdgeServiceNever(new Alarm(alarm), alarm.getId(), alarm.getOriginator(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED); } @@ -246,7 +248,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk()); - Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus()); @@ -262,7 +264,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { Mockito.reset(tbClusterService, auditLogService); doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk()); - Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus()); @@ -279,7 +281,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isOk()); - Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(AlarmStatus.ACTIVE_ACK, foundAlarm.getStatus()); @@ -357,7 +359,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { long beforeAssignmentTs = System.currentTimeMillis(); doPost("/api/alarm/" + alarm.getId() + "/assign/" + tenantAdminUserId.getId()).andExpect(status().isOk()); - Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(tenantAdminUserId, foundAlarm.getAssigneeId()); Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis()); @@ -387,7 +389,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { doPost("/api/alarm/" + alarm.getId() + "/assign/" + tenantAdminUserId.getId()).andExpect(status().isOk()); - Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(tenantAdminUserId, foundAlarm.getAssigneeId()); Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis()); @@ -403,7 +405,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { doPost("/api/alarm/" + alarm.getId() + "/assign/" + customerUserId.getId()).andExpect(status().isOk()); - foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(customerUserId, foundAlarm.getAssigneeId()); Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis()); @@ -420,7 +422,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { long beforeAssignmentTs = System.currentTimeMillis(); doPost("/api/alarm/" + alarm.getId() + "/assign/" + tenantAdminUserId.getId()).andExpect(status().isOk()); - Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(tenantAdminUserId, foundAlarm.getAssigneeId()); Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis()); @@ -431,7 +433,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { beforeAssignmentTs = System.currentTimeMillis(); doDelete("/api/alarm/" + alarm.getId() + "/assign").andExpect(status().isOk()); - foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(foundAlarm); Assert.assertNull(foundAlarm.getAssigneeId()); Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis()); @@ -448,7 +450,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { long beforeAssignmentTs = System.currentTimeMillis(); doPost("/api/alarm/" + alarm.getId() + "/assign/" + tenantAdminUserId.getId()).andExpect(status().isOk()); - Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(foundAlarm); Assert.assertEquals(tenantAdminUserId, foundAlarm.getAssigneeId()); Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis()); @@ -463,7 +465,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { beforeAssignmentTs = System.currentTimeMillis(); doDelete("/api/alarm/" + alarm.getId() + "/assign").andExpect(status().isOk()); - foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class); + foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); Assert.assertNotNull(foundAlarm); Assert.assertNull(foundAlarm.getAssigneeId()); Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis()); @@ -580,7 +582,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { testEntityDaoWithRelationsTransactionalException(alarmDao, customerDevice.getId(), alarmId, "/api/alarm/" + alarmId); } - private Alarm createAlarm(String type) throws Exception { + private AlarmInfo createAlarm(String type) throws Exception { Alarm alarm = Alarm.builder() .tenantId(tenantId) .customerId(customerId) @@ -592,6 +594,10 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest { alarm = doPost("/api/alarm", alarm, Alarm.class); Assert.assertNotNull(alarm); - return alarm; + AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class); + Assert.assertNotNull(foundAlarm); + Assert.assertEquals(alarm, new Alarm(foundAlarm)); + + return foundAlarm; } } diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java index c81950c8d1..3d3615a3c4 100644 --- a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java @@ -28,8 +28,6 @@ import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; -import org.thingsboard.server.common.data.alarm.AlarmStatus; -import org.thingsboard.server.common.data.alarm.CreateOrUpdateActiveAlarmRequest; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.dao.alarm.AlarmApiCallResult; diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index 75dad2bda3..d8c55c5917 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -562,7 +562,6 @@ message TbAlarmSubscriptionUpdateProto { string errorMsg = 4; string alarm = 5; bool deleted = 6; - string assignee = 7; } message TbAttributeUpdateProto { @@ -582,7 +581,6 @@ message TbAlarmUpdateProto { int64 tenantIdMSB = 4; int64 tenantIdLSB = 5; string alarm = 6; - string assignee = 7; } message TbAlarmDeleteProto { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmApiCallResult.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmApiCallResult.java index 075216565b..a8aebf16e7 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmApiCallResult.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmApiCallResult.java @@ -18,7 +18,6 @@ package org.thingsboard.server.dao.alarm; import lombok.Builder; import lombok.Data; import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmAssigneeUpdate; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.id.EntityId; @@ -58,7 +57,7 @@ public class AlarmApiCallResult { this.propagatedEntitiesList = propagatedEntitiesList; } - boolean hasSeverityChange() { + public boolean isSeverityChanged() { if (alarm == null || old == null) { return false; } else { @@ -67,10 +66,13 @@ public class AlarmApiCallResult { } public AlarmSeverity getOldSeverity() { - return hasSeverityChange() ? old.getSeverity() : null; + return isSeverityChanged() ? old.getSeverity() : null; } public boolean isPropagationChanged() { + if (created) { + return true; + } if (alarm == null || old == null) { return false; } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index aa973a18ae..ad2aa1814f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -24,7 +24,7 @@ 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.AlarmUpdateRequest; -import org.thingsboard.server.common.data.alarm.CreateOrUpdateActiveAlarmRequest; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -48,13 +48,13 @@ public interface AlarmService extends EntityDaoService { * Designed for atomic operations over active alarms. * Only one active alarm may exist for the pair {originatorId, alarmType} */ - AlarmApiCallResult createAlarm(CreateOrUpdateActiveAlarmRequest request); + AlarmApiCallResult createAlarm(AlarmCreateOrUpdateActiveRequest request); /** * Designed for atomic operations over active alarms. * Only one active alarm may exist for the pair {originatorId, alarmType} */ - AlarmApiCallResult createAlarm(CreateOrUpdateActiveAlarmRequest request, boolean alarmCreationEnabled); + AlarmApiCallResult createAlarm(AlarmCreateOrUpdateActiveRequest request, boolean alarmCreationEnabled); /** * Designed to update existing alarm. Accepts only part of the alarm fields. @@ -89,6 +89,9 @@ public interface AlarmService extends EntityDaoService { @Deprecated(since = "3.5.0", forRemoval = true) AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId); + @Deprecated(since = "3.5.0", forRemoval = true) + ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); + // Other API Alarm findAlarmById(TenantId tenantId, AlarmId alarmId); @@ -103,7 +106,7 @@ public interface AlarmService extends EntityDaoService { AlarmSeverity findHighestAlarmSeverity(TenantId tenantId, EntityId entityId, AlarmSearchStatus alarmSearchStatus, AlarmStatus alarmStatus, String assigneeId); - ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); + Alarm findLatestActiveByOriginatorAndType(TenantId tenantId, EntityId originator, String type); PageData findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection orderedEntityIds); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/CreateOrUpdateActiveAlarmRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java similarity index 93% rename from common/data/src/main/java/org/thingsboard/server/common/data/alarm/CreateOrUpdateActiveAlarmRequest.java rename to common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java index e882b377f1..d599f9cb0d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/CreateOrUpdateActiveAlarmRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCreateOrUpdateActiveRequest.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModelProperty; import lombok.Builder; import lombok.Data; -import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -31,7 +30,7 @@ import javax.validation.constraints.NotNull; @Data @Builder -public class CreateOrUpdateActiveAlarmRequest implements AlarmModificationRequest { +public class AlarmCreateOrUpdateActiveRequest implements AlarmModificationRequest { @NotNull @ApiModelProperty(position = 1, value = "JSON object with Tenant Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @@ -59,8 +58,8 @@ public class CreateOrUpdateActiveAlarmRequest implements AlarmModificationReques @ApiModelProperty(position = 9, value = "JSON object with propagation details") private AlarmPropagationInfo propagation; - public static CreateOrUpdateActiveAlarmRequest fromAlarm(Alarm a) { - return CreateOrUpdateActiveAlarmRequest.builder() + public static AlarmCreateOrUpdateActiveRequest fromAlarm(Alarm a) { + return AlarmCreateOrUpdateActiveRequest.builder() .tenantId(a.getTenantId()) .customerId(a.getCustomerId()) .type(a.getType()) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java index 356097842b..f284bb16d6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java @@ -56,6 +56,7 @@ public class AlarmUpdateRequest implements AlarmModificationRequest { public static AlarmUpdateRequest fromAlarm(Alarm a) { return AlarmUpdateRequest.builder() .tenantId(a.getTenantId()) + .alarmId(a.getId()) .severity((a.getSeverity())) .startTs(a.getStartTs()) .endTs(a.getEndTs()) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmData.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmData.java index 534887ced5..1ae434f28c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/query/AlarmData.java @@ -36,31 +36,11 @@ public class AlarmData extends AlarmInfo { @Getter private final Map> latest; - public AlarmData update(Alarm alarm, AlarmAssigneeUpdate assigneeUpdate) { - this.setEndTs(alarm.getEndTs()); - this.setSeverity(alarm.getSeverity()); - this.setAcknowledged(alarm.isAcknowledged()); - this.setCleared(alarm.isCleared()); - this.setDetails(alarm.getDetails()); - this.setPropagate(alarm.isPropagate()); - this.setPropagateToOwner(alarm.isPropagateToOwner()); - this.setPropagateToTenant(alarm.isPropagateToTenant()); - this.setPropagateRelationTypes(alarm.getPropagateRelationTypes()); - // This should be changed via separate message? - this.setAckTs(alarm.getAckTs()); - this.setClearTs(alarm.getClearTs()); - - if (assigneeUpdate != null) { - if (assigneeUpdate.isDeleted()) { - this.setAssigneeId(null); - this.setAssignee(null); - } else { - AlarmAssignee assignee = assigneeUpdate.getAssignee(); - this.setAssigneeId(assignee.getId()); - this.setAssignee(assignee); - } - } - return this; + public AlarmData(AlarmInfo main, AlarmData prototype) { + super(main); + this.entityId = prototype.entityId; + this.latest = new HashMap<>(); + this.latest.putAll(prototype.getLatest()); } public AlarmData(Alarm alarm, EntityId entityId) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index e8c169897c..3d2e7078a1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; -import org.thingsboard.server.common.data.alarm.CreateOrUpdateActiveAlarmRequest; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.EntityAlarm; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; @@ -48,6 +48,8 @@ public interface AlarmDao extends Dao { Alarm findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); + Alarm findLatestActiveByOriginatorAndType(TenantId tenantId, EntityId originator, String type); + ListenableFuture findLatestByOriginatorAndTypeAsync(TenantId tenantId, EntityId originator, String type); Alarm findAlarmById(TenantId tenantId, UUID key); @@ -74,7 +76,7 @@ public interface AlarmDao extends Dao { void deleteEntityAlarmRecords(TenantId tenantId, EntityId entityId); - AlarmApiCallResult createOrUpdateActiveAlarm(CreateOrUpdateActiveAlarmRequest request, boolean alarmCreationEnabled); + AlarmApiCallResult createOrUpdateActiveAlarm(AlarmCreateOrUpdateActiveRequest request, boolean alarmCreationEnabled); AlarmApiCallResult updateAlarm(AlarmUpdateRequest request); diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index 3245a81ec5..0dd2eebf87 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -22,13 +22,11 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmAssigneeUpdate; import org.thingsboard.server.common.data.alarm.AlarmModificationRequest; import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; @@ -37,7 +35,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery; 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.CreateOrUpdateActiveAlarmRequest; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.EntityAlarm; import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException; import org.thingsboard.server.common.data.id.AlarmId; @@ -60,7 +58,6 @@ import org.thingsboard.server.dao.service.ConstraintValidator; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; -import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -94,12 +91,12 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } @Override - public AlarmApiCallResult createAlarm(CreateOrUpdateActiveAlarmRequest request) { + public AlarmApiCallResult createAlarm(AlarmCreateOrUpdateActiveRequest request) { return createAlarm(request, true); } @Override - public AlarmApiCallResult createAlarm(CreateOrUpdateActiveAlarmRequest request, boolean alarmCreationEnabled) { + public AlarmApiCallResult createAlarm(AlarmCreateOrUpdateActiveRequest request, boolean alarmCreationEnabled) { validateAlarmRequest(request); CustomerId customerId = entityService.fetchEntityCustomerId(request.getTenantId(), request.getOriginator()).orElse(null); if (customerId == null && request.getCustomerId() != null) { @@ -160,6 +157,12 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } } + @Override + public Alarm findLatestActiveByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { + return alarmDao.findLatestActiveByOriginatorAndType(tenantId, originator, type); + } + + @Override public ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { return alarmDao.findLatestByOriginatorAndTypeAsync(tenantId, originator, type); } @@ -452,7 +455,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ private void validateAlarmRequest(AlarmModificationRequest request) { ConstraintValidator.validateFields(request); - if (request.getStartTs() > request.getEndTs()) { + if (request.getEndTs() > 0 && request.getStartTs() > request.getEndTs()) { throw new DataValidationException("Alarm start ts can't be greater then alarm end ts!"); } if (!tenantService.tenantExists(request.getTenantId())) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index 04dc13c53d..49e0257daf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -40,6 +40,11 @@ public interface AlarmRepository extends JpaRepository { @Param("alarmType") String alarmType, Pageable pageable); + @Query("SELECT a FROM AlarmEntity a WHERE a.originatorId = :originatorId AND a.type = :alarmType AND a.cleared = FALSE ORDER BY a.createdTime DESC") + List findLatestActiveByOriginatorAndType(@Param("originatorId") UUID originatorId, + @Param("alarmType") String alarmType, + Pageable pageable); + @Query(value = "SELECT a " + "FROM AlarmInfoEntity a " + "LEFT JOIN EntityAlarmEntity ea ON a.id = ea.alarmId " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index d6bc52b3bb..f1abd07d95 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -35,7 +35,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; -import org.thingsboard.server.common.data.alarm.CreateOrUpdateActiveAlarmRequest; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.EntityAlarm; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; @@ -102,6 +102,15 @@ public class JpaAlarmDao extends JpaAbstractDao implements A return latest.isEmpty() ? null : DaoUtil.getData(latest.get(0)); } + @Override + public Alarm findLatestActiveByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { + List latest = alarmRepository.findLatestActiveByOriginatorAndType( + originator.getId(), + type, + PageRequest.of(0, 1)); + return latest.isEmpty() ? null : DaoUtil.getData(latest.get(0)); + } + @Override public ListenableFuture findLatestByOriginatorAndTypeAsync(TenantId tenantId, EntityId originator, String type) { return service.submit(() -> findLatestByOriginatorAndType(tenantId, originator, type)); @@ -231,7 +240,7 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } @Override - public AlarmApiCallResult createOrUpdateActiveAlarm(CreateOrUpdateActiveAlarmRequest request, boolean alarmCreationEnabled) { + public AlarmApiCallResult createOrUpdateActiveAlarm(AlarmCreateOrUpdateActiveRequest request, boolean alarmCreationEnabled) { AlarmPropagationInfo ap = getSafePropagationInfo(request.getPropagation()); return toAlarmApiResult(alarmRepository.createOrUpdateActiveAlarm( request.getTenantId().getId(), diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java index 692e69032b..a07887fb57 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/AlarmDataAdapter.java @@ -125,7 +125,9 @@ public class AlarmDataAdapter { AlarmData alarmData = new AlarmData(alarm, entityId); alarmData.setOriginatorName(originatorName); alarmData.setOriginatorLabel(originatorLabel); - alarmData.setAssignee(new AlarmAssignee(alarm.getAssigneeId(), assigneeFirstName, assigneeLastName, assigneeEmail)); + if (alarm.getAssigneeId() != null) { + alarmData.setAssignee(new AlarmAssignee(alarm.getAssigneeId(), assigneeFirstName, assigneeLastName, assigneeEmail)); + } return alarmData; } diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 1eab033b41..90c11fbd2c 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -851,8 +851,8 @@ DECLARE result alarm_info; row_count integer; BEGIN - SELECT * INTO existing FROM alarm a WHERE a.originator_id = a_o_id AND a.type = a_type ORDER BY a.start_ts DESC FOR UPDATE; - IF existing.id IS NULL OR existing.cleared IS TRUE THEN + SELECT * INTO existing FROM alarm a WHERE a.originator_id = a_o_id AND a.type = a_type AND a.cleared = false ORDER BY a.start_ts DESC FOR UPDATE; + IF existing.id IS NULL THEN IF a_creation_enabled = FALSE THEN RETURN json_build_object('success', false)::text; END IF; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java index 897f77a0ce..b7f5135533 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java @@ -26,11 +26,14 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.alarm.AlarmPropagationInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; 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.AlarmUpdateRequest; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; @@ -91,14 +94,12 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertTrue(relationService.saveRelationAsync(tenantId, relation).get()); long ts = System.currentTimeMillis(); - Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId) + AlarmApiCallResult result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) + .originator(childId) .type(TEST_ALARM) .severity(AlarmSeverity.CRITICAL) - .acknowledged(false) - .cleared(false) - .startTs(ts).build(); - - AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm); + .startTs(ts).build()); Alarm created = result.getAlarm(); Assert.assertNotNull(created); @@ -117,7 +118,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertEquals(0L, created.getAckTs()); Assert.assertEquals(0L, created.getClearTs()); - Alarm fetched = alarmService.findAlarmByIdAsync(tenantId, created.getId()).get(); + Alarm fetched = alarmService.findAlarmInfoById(tenantId, created.getId()); Assert.assertEquals(created, fetched); } @@ -131,16 +132,13 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertTrue(relationService.saveRelationAsync(tenantId, relation).get()); long ts = System.currentTimeMillis(); - Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId) + AlarmApiCallResult result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) + .originator(childId) .type(TEST_ALARM) - .propagate(false) .severity(AlarmSeverity.CRITICAL) - .acknowledged(false) - .cleared(false) - .startTs(ts).build(); - - AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm); - Alarm created = result.getAlarm(); + .startTs(ts).build()); + AlarmInfo created = result.getAlarm(); // Check child relation PageData alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() @@ -151,7 +149,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); // Check parent relation alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() @@ -164,7 +162,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertEquals(0, alarms.getData().size()); created.setPropagate(true); - result = alarmService.createOrUpdateAlarm(created); + result = alarmService.updateAlarm(AlarmUpdateRequest.fromAlarm(created)); created = result.getAlarm(); // Check child relation @@ -176,7 +174,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); // Check parent relation alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() @@ -187,10 +185,10 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); alarmService.acknowledgeAlarm(tenantId, created.getId(), System.currentTimeMillis()); - created = alarmService.findAlarmByIdAsync(tenantId, created.getId()).get(); + created = alarmService.findAlarmInfoById(tenantId, created.getId()); alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() .affectedEntityId(childId) @@ -200,7 +198,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); // Check not existing relation alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() @@ -212,8 +210,8 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertNotNull(alarms.getData()); Assert.assertEquals(0, alarms.getData().size()); - alarmService.clearAlarm(tenantId, created.getId(), null, System.currentTimeMillis()).get(); - created = alarmService.findAlarmByIdAsync(tenantId, created.getId()).get(); + alarmService.clearAlarm(tenantId, created.getId(), System.currentTimeMillis(), null); + created = alarmService.findAlarmInfoById(tenantId, created.getId()); alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() .affectedEntityId(childId) @@ -223,7 +221,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); } @Test @@ -234,19 +232,17 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { EntityRelation relation = new EntityRelation(parentId, childId, EntityRelation.CONTAINS_TYPE); - Assert.assertTrue(relationService.saveRelationAsync(tenantId, relation).get()); + Assert.assertTrue(relationService.saveRelation(tenantId, relation)); long ts = System.currentTimeMillis(); - Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId) + AlarmApiCallResult result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) + .originator(childId) .type(TEST_ALARM) - .propagate(false) .severity(AlarmSeverity.CRITICAL) - .acknowledged(false) - .cleared(false) - .startTs(ts).build(); + .startTs(ts).build()); - AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm); - Alarm created = result.getAlarm(); + AlarmInfo created = result.getAlarm(); User tenantUser = new User(); tenantUser.setTenantId(tenantId); @@ -269,7 +265,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, alarms.getData().get(0)); AlarmDataPageLink pageLink = new AlarmDataPageLink(); pageLink.setPage(0); @@ -279,7 +275,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { PageData assignedAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(created.getOriginator())); Assert.assertNotNull(assignedAlarms.getData()); Assert.assertEquals(1, assignedAlarms.getData().size()); - Assert.assertEquals(created, new Alarm(assignedAlarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(assignedAlarms.getData().get(0))); User tenantUser2 = new User(); tenantUser2.setTenantId(tenantId); @@ -319,23 +315,23 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { customerDevice = deviceService.saveDevice(customerDevice); long ts = System.currentTimeMillis(); - Alarm tenantAlarm = Alarm.builder().tenantId(tenantId) + AlarmApiCallResult result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) .originator(tenantDevice.getId()) .type(TEST_ALARM) - .propagate(true) .severity(AlarmSeverity.CRITICAL) - .startTs(ts).build(); - AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm); - tenantAlarm = result.getAlarm(); + .propagation(AlarmPropagationInfo.builder().propagate(true).build()) + .startTs(ts).build()); + AlarmInfo tenantAlarm = result.getAlarm(); - Alarm deviceAlarm = Alarm.builder().tenantId(tenantId) + result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) .originator(customerDevice.getId()) .type(TEST_ALARM) - .propagate(true) .severity(AlarmSeverity.CRITICAL) - .startTs(ts).build(); - result = alarmService.createOrUpdateAlarm(deviceAlarm); - deviceAlarm = result.getAlarm(); + .propagation(AlarmPropagationInfo.builder().propagate(true).build()) + .startTs(ts).build()); + AlarmInfo deviceAlarm = result.getAlarm(); AlarmDataPageLink pageLink = new AlarmDataPageLink(); pageLink.setPage(0); @@ -353,7 +349,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { PageData customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(customerDevice.getId())); Assert.assertEquals(1, customerAlarms.getData().size()); - Assert.assertEquals(deviceAlarm, new Alarm(customerAlarms.getData().get(0))); + Assert.assertEquals(deviceAlarm, new AlarmInfo(customerAlarms.getData().get(0))); PageData alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() .affectedEntityId(tenantDevice.getId()) @@ -363,7 +359,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(tenantAlarm, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(tenantAlarm, new AlarmInfo(alarms.getData().get(0))); } @Test @@ -395,23 +391,21 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { relationService.saveRelation(tenantId, relation); long ts = System.currentTimeMillis(); - Alarm tenantAlarm = Alarm.builder().tenantId(tenantId) + alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) .originator(tenantDevice.getId()) .type("Not Propagated") - .propagate(false) .severity(AlarmSeverity.CRITICAL) - .startTs(ts).build(); - AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm); - tenantAlarm = result.getAlarm(); + .startTs(ts).build()); - Alarm customerAlarm = Alarm.builder().tenantId(tenantId) + AlarmApiCallResult result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) .originator(tenantDevice.getId()) .type("Propagated") - .propagate(true) .severity(AlarmSeverity.CRITICAL) - .startTs(ts).build(); - result = alarmService.createOrUpdateAlarm(customerAlarm); - customerAlarm = result.getAlarm(); + .propagation(AlarmPropagationInfo.builder().propagate(true).build()) + .startTs(ts).build()); + AlarmInfo customerAlarm = result.getAlarm(); AlarmDataPageLink pageLink = new AlarmDataPageLink(); pageLink.setPage(0); @@ -427,7 +421,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { //TEST that propagated alarms are visible on the asset level. PageData customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(customerAsset.getId())); Assert.assertEquals(1, customerAlarms.getData().size()); - Assert.assertEquals(customerAlarm, new Alarm(customerAlarms.getData().get(0))); + Assert.assertEquals(customerAlarm, new AlarmInfo(customerAlarms.getData().get(0))); } @Test @@ -445,24 +439,24 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { device = deviceService.saveDevice(device); long ts = System.currentTimeMillis(); - Alarm tenantAlarm = Alarm.builder().tenantId(tenantId) + + AlarmApiCallResult result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) .originator(device.getId()) .type("Propagated To Tenant") - .propagateToTenant(true) .severity(AlarmSeverity.CRITICAL) - .startTs(ts).build(); - AlarmOperationResult result = alarmService.createOrUpdateAlarm(tenantAlarm); - tenantAlarm = result.getAlarm(); + .propagation(AlarmPropagationInfo.builder().propagateToTenant(true).build()) + .startTs(ts).build()); + AlarmInfo tenantAlarm = result.getAlarm(); - Alarm customerAlarm = Alarm.builder().tenantId(tenantId) + result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) .originator(device.getId()) .type("Propagated to Customer") - .propagate(false) - .propagateToOwner(true) .severity(AlarmSeverity.CRITICAL) - .startTs(ts).build(); - result = alarmService.createOrUpdateAlarm(customerAlarm); - customerAlarm = result.getAlarm(); + .propagation(AlarmPropagationInfo.builder().propagateToOwner(true).build()) + .startTs(ts).build()); + AlarmInfo customerAlarm = result.getAlarm(); AlarmDataPageLink pageLink = new AlarmDataPageLink(); pageLink.setPage(0); @@ -473,17 +467,17 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { pageLink.setEndTs(System.currentTimeMillis()); pageLink.setSearchPropagatedAlarms(true); pageLink.setSeverityList(Arrays.asList(AlarmSeverity.CRITICAL, AlarmSeverity.WARNING)); - pageLink.setStatusList(Arrays.asList(AlarmSearchStatus.ACTIVE)); + pageLink.setStatusList(Collections.singletonList(AlarmSearchStatus.ACTIVE)); //TEST that propagated alarms are visible on the asset level. PageData tenantAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(tenantId)); Assert.assertEquals(1, tenantAlarms.getData().size()); - Assert.assertEquals(tenantAlarm, new Alarm(tenantAlarms.getData().get(0))); + Assert.assertEquals(tenantAlarm, new AlarmInfo(tenantAlarms.getData().get(0))); //TEST that propagated alarms are visible on the asset level. PageData customerAlarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(customer.getId())); Assert.assertEquals(1, customerAlarms.getData().size()); - Assert.assertEquals(customerAlarm, new Alarm(customerAlarms.getData().get(0))); + Assert.assertEquals(customerAlarm, new AlarmInfo(customerAlarms.getData().get(0))); } private AlarmDataQuery toQuery(AlarmDataPageLink pageLink) { @@ -511,40 +505,33 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { // no one alarms was created Assert.assertNull(alarmService.findHighestAlarmSeverity(tenantId, customerDevice.getId(), null, null, null)); - Alarm alarm1 = Alarm.builder() + AlarmApiCallResult result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() .tenantId(tenantId) .originator(customerDevice.getId()) .type(TEST_ALARM) .severity(AlarmSeverity.MAJOR) - .acknowledged(false) - .cleared(false) - .startTs(System.currentTimeMillis()) - .build(); - alarm1 = alarmService.createOrUpdateAlarm(alarm1).getAlarm(); - alarmService.clearAlarm(tenantId, alarm1.getId(), null, System.currentTimeMillis()).get(); - - Alarm alarm2 = Alarm.builder() + .startTs(System.currentTimeMillis()).build()); + AlarmInfo alarm1 = result.getAlarm(); + alarmService.clearAlarm(tenantId, alarm1.getId(), System.currentTimeMillis(), null); + + result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() .tenantId(tenantId) .originator(customerDevice.getId()) .type(TEST_ALARM) .severity(AlarmSeverity.MINOR) - .acknowledged(true) - .cleared(false) - .startTs(System.currentTimeMillis()) - .build(); - alarm2 = alarmService.createOrUpdateAlarm(alarm2).getAlarm(); - alarmService.clearAlarm(tenantId, alarm2.getId(), null, System.currentTimeMillis()).get(); - - Alarm alarm3 = Alarm.builder() + .startTs(System.currentTimeMillis()).build()); + AlarmInfo alarm2 = result.getAlarm(); + alarmService.acknowledgeAlarm(tenantId, alarm2.getId(), System.currentTimeMillis()); + alarmService.clearAlarm(tenantId, alarm2.getId(), System.currentTimeMillis(), null); + + result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() .tenantId(tenantId) .originator(customerDevice.getId()) .type(TEST_ALARM) .severity(AlarmSeverity.CRITICAL) - .acknowledged(true) - .cleared(false) - .startTs(System.currentTimeMillis()) - .build(); - alarm3 = alarmService.createOrUpdateAlarm(alarm3).getAlarm(); + .startTs(System.currentTimeMillis()).build()); + AlarmInfo alarm3 = result.getAlarm(); + alarmService.acknowledgeAlarm(tenantId, alarm3.getId(), System.currentTimeMillis()); Assert.assertEquals(AlarmSeverity.MAJOR, alarmService.findHighestAlarmSeverity(tenantId, customerDevice.getId(), AlarmSearchStatus.UNACK, null, null)); Assert.assertEquals(AlarmSeverity.CRITICAL, alarmService.findHighestAlarmSeverity(tenantId, customerDevice.getId(), null, null, null)); @@ -566,14 +553,13 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertTrue(relationService.saveRelationAsync(tenantId, relation2).get()); long ts = System.currentTimeMillis(); - Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId) + AlarmApiCallResult result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) + .originator(childId) .type(TEST_ALARM) - .propagate(false) .severity(AlarmSeverity.CRITICAL) - .startTs(ts).build(); - - AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm); - Alarm created = result.getAlarm(); + .startTs(ts).build()); + AlarmInfo created = result.getAlarm(); AlarmDataPageLink pageLink = new AlarmDataPageLink(); pageLink.setPage(0); @@ -590,7 +576,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); pageLink.setPage(0); pageLink.setPageSize(10); @@ -605,17 +591,17 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId)); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); pageLink.setSearchPropagatedAlarms(true); alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId)); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); // Check child relation created.setPropagate(true); - result = alarmService.createOrUpdateAlarm(created); + result = alarmService.updateAlarm(AlarmUpdateRequest.fromAlarm(created)); created = result.getAlarm(); // Check child relation @@ -632,7 +618,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId)); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); // Check parent relation pageLink.setPage(0); @@ -648,7 +634,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(parentId)); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); PageData alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder() .affectedEntityId(childId) @@ -659,7 +645,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarmsInfoData.getData()); Assert.assertEquals(1, alarmsInfoData.getData().size()); - Assert.assertEquals(created, new Alarm(alarmsInfoData.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarmsInfoData.getData().get(0))); alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder() .affectedEntityId(parentId) @@ -670,7 +656,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarmsInfoData.getData()); Assert.assertEquals(1, alarmsInfoData.getData().size()); - Assert.assertEquals(created, new Alarm(alarmsInfoData.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarmsInfoData.getData().get(0))); alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder() .affectedEntityId(parentId2) @@ -681,7 +667,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarmsInfoData.getData()); Assert.assertEquals(1, alarmsInfoData.getData().size()); - Assert.assertEquals(created, new Alarm(alarmsInfoData.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarmsInfoData.getData().get(0))); pageLink.setPage(0); pageLink.setPageSize(10); @@ -696,9 +682,9 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(parentId)); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); - created = new Alarm(alarmService.acknowledgeAlarm(tenantId, created.getId(), System.currentTimeMillis()).getAlarm()); + created = alarmService.acknowledgeAlarm(tenantId, created.getId(), System.currentTimeMillis()).getAlarm(); pageLink.setPage(0); pageLink.setPageSize(10); @@ -713,7 +699,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, toQuery(pageLink), Collections.singletonList(childId)); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); } @Test @@ -723,17 +709,17 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { EntityRelation relation = new EntityRelation(parentId, childId, EntityRelation.CONTAINS_TYPE); - Assert.assertTrue(relationService.saveRelationAsync(tenantId, relation).get()); + Assert.assertTrue(relationService.saveRelation(tenantId, relation)); long ts = System.currentTimeMillis(); - Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId) + AlarmApiCallResult result = alarmService.createAlarm(AlarmCreateOrUpdateActiveRequest.builder() + .tenantId(tenantId) + .originator(childId) .type(TEST_ALARM) - .propagate(true) .severity(AlarmSeverity.CRITICAL) - .startTs(ts).build(); - - AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm); - Alarm created = result.getAlarm(); + .propagation(AlarmPropagationInfo.builder().propagate(true).build()) + .startTs(ts).build()); + AlarmInfo created = result.getAlarm(); PageData alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() .affectedEntityId(childId) @@ -743,7 +729,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); // Check parent relation alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() @@ -754,7 +740,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { ).build()).get(); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + Assert.assertEquals(created, new AlarmInfo(alarms.getData().get(0))); Assert.assertTrue("Alarm was not deleted when expected", alarmService.deleteAlarm(tenantId, created.getId()).isSuccessful()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java index 389510d7d2..5fbc2fd627 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDaoTest.java @@ -17,15 +17,13 @@ package org.thingsboard.server.dao.sql.alarm; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; -import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSeverity; -import org.thingsboard.server.common.data.alarm.CreateOrUpdateActiveAlarmRequest; -import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; @@ -93,7 +91,7 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { TenantId tenantId = TenantId.fromUUID(UUID.randomUUID()); DeviceId deviceId = new DeviceId(UUID.randomUUID()); - CreateOrUpdateActiveAlarmRequest request = CreateOrUpdateActiveAlarmRequest.builder() + AlarmCreateOrUpdateActiveRequest request = AlarmCreateOrUpdateActiveRequest.builder() .tenantId(tenantId) .originator(deviceId) .type("ALARM_TYPE") @@ -109,7 +107,7 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { AlarmInfo afterSave = alarmDao.findAlarmInfoById(tenantId, newAlarmId); assertEquals(afterSave, result.getAlarm()); - request = CreateOrUpdateActiveAlarmRequest.builder() + request = AlarmCreateOrUpdateActiveRequest.builder() .tenantId(tenantId) .originator(deviceId) .type("ALARM_TYPE") @@ -127,7 +125,7 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { alarmDao.clearAlarm(tenantId, result.getAlarm().getId(), System.currentTimeMillis(), result.getAlarm().getDetails()); - request = CreateOrUpdateActiveAlarmRequest.builder() + request = AlarmCreateOrUpdateActiveRequest.builder() .tenantId(tenantId) .originator(deviceId) .type("ALARM_TYPE") @@ -145,7 +143,7 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { alarmDao.clearAlarm(tenantId, result.getAlarm().getId(), System.currentTimeMillis(), result.getAlarm().getDetails()); - request = CreateOrUpdateActiveAlarmRequest.builder() + request = AlarmCreateOrUpdateActiveRequest.builder() .tenantId(tenantId) .originator(deviceId) .type("ALARM_TYPE2") @@ -165,7 +163,7 @@ public class JpaAlarmDaoTest extends AbstractJpaDaoTest { TenantId tenantId = TenantId.fromUUID(UUID.randomUUID()); DeviceId deviceId = new DeviceId(UUID.randomUUID()); - CreateOrUpdateActiveAlarmRequest request = CreateOrUpdateActiveAlarmRequest.builder() + AlarmCreateOrUpdateActiveRequest request = AlarmCreateOrUpdateActiveRequest.builder() .tenantId(tenantId) .originator(deviceId) .type("ALARM_TYPE") diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java index 7927843900..e88411d73a 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java @@ -25,7 +25,7 @@ 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.AlarmUpdateRequest; -import org.thingsboard.server.common.data.alarm.CreateOrUpdateActiveAlarmRequest; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -52,7 +52,7 @@ public interface RuleEngineAlarmService { * Designed for atomic operations over active alarms. * Only one active alarm may exist for the pair {originatorId, alarmType} */ - AlarmApiCallResult createAlarm(CreateOrUpdateActiveAlarmRequest request); + AlarmApiCallResult createAlarm(AlarmCreateOrUpdateActiveRequest request); /** * Designed to update existing alarm. Accepts only part of the alarm fields. */ @@ -88,6 +88,8 @@ public interface RuleEngineAlarmService { Alarm findAlarmById(TenantId tenantId, AlarmId alarmId); + Alarm findLatestActiveByOriginatorAndType(TenantId tenantId, EntityId originator, String type); + ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAlarmResult.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAlarmResult.java index b5658a3451..3641d78b7c 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAlarmResult.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAlarmResult.java @@ -17,7 +17,9 @@ package org.thingsboard.rule.engine.action; import lombok.AllArgsConstructor; import lombok.Data; +import org.jetbrains.annotations.NotNull; import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.dao.alarm.AlarmApiCallResult; @Data @AllArgsConstructor @@ -34,4 +36,8 @@ public class TbAlarmResult { this.isCleared = isCleared; this.alarm = alarm; } + + public static TbAlarmResult fromAlarmResult(AlarmApiCallResult result) { + return new TbAlarmResult(result.isCreated(), result.isModified(), result.isSeverityChanged(), result.isCleared(), result.getAlarm()); + } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java index 0cd7b77fd6..ee32ac1533 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java @@ -26,7 +26,6 @@ import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -58,18 +57,16 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode processAlarm(TbContext ctx, TbMsg msg) { String alarmType = TbNodeUtils.processPattern(this.config.getAlarmType(), msg); - ListenableFuture alarmFuture; + Alarm alarm; if (msg.getOriginator().getEntityType().equals(EntityType.ALARM)) { - alarmFuture = ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), new AlarmId(msg.getOriginator().getId())); + alarm = ctx.getAlarmService().findAlarmById(ctx.getTenantId(), new AlarmId(msg.getOriginator().getId())); } else { - alarmFuture = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType); + alarm = ctx.getAlarmService().findLatestActiveByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType); } - return Futures.transformAsync(alarmFuture, a -> { - if (a != null && !a.getStatus().isCleared()) { - return clearAlarm(ctx, msg, a); - } - return Futures.immediateFuture(new TbAlarmResult(false, false, false, null)); - }, ctx.getDbCallbackExecutor()); + if (alarm != null && !alarm.getStatus().isCleared()) { + return clearAlarm(ctx, msg, alarm); + } + return Futures.immediateFuture(new TbAlarmResult(false, false, false, null)); } private ListenableFuture clearAlarm(TbContext ctx, TbMsg msg, Alarm alarm) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java index 3e7bb48d65..333926864d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java @@ -17,13 +17,11 @@ package org.thingsboard.rule.engine.action; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.base.Function; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.EnumUtils; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; @@ -31,10 +29,12 @@ import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; 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.alarm.AlarmUpdateRequest; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.dao.alarm.AlarmApiCallResult; import java.io.IOException; import java.util.List; @@ -64,9 +64,9 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType); - return Futures.transformAsync(latest, existingAlarm -> { - if (existingAlarm == null || existingAlarm.getStatus().isCleared()) { - return createNewAlarm(ctx, msg, msgAlarm); - } else { - return updateAlarm(ctx, msg, existingAlarm, msgAlarm); - } - }, ctx.getDbCallbackExecutor()); - + Alarm existingAlarm = ctx.getAlarmService().findLatestActiveByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType); + if (existingAlarm == null || existingAlarm.getStatus().isCleared()) { + return createNewAlarm(ctx, msg, msgAlarm); + } else { + return updateAlarm(ctx, msg, existingAlarm, msgAlarm); + } } private Alarm getAlarmFromMessage(TbContext ctx, TbMsg msg) throws IOException { @@ -128,7 +125,7 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode asyncAlarm = Futures.transform(asyncDetails, details -> { + ListenableFuture asyncAlarm = Futures.transform(asyncDetails, details -> { if (buildDetails) { ctx.logJsEvalResponse(); } @@ -143,9 +140,9 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode asyncCreated = Futures.transform(asyncAlarm, - alarm -> ctx.getAlarmService().createOrUpdateAlarm(alarm), ctx.getDbCallbackExecutor()); - return Futures.transform(asyncCreated, alarm -> new TbAlarmResult(true, false, false, alarm), MoreExecutors.directExecutor()); + ListenableFuture asyncCreated = Futures.transform(asyncAlarm, + alarm -> ctx.getAlarmService().createAlarm(AlarmCreateOrUpdateActiveRequest.fromAlarm(alarm)), ctx.getDbCallbackExecutor()); + return Futures.transform(asyncCreated, TbAlarmResult::fromAlarmResult, MoreExecutors.directExecutor()); } private ListenableFuture updateAlarm(TbContext ctx, TbMsg msg, Alarm existingAlarm, Alarm msgAlarm) { @@ -157,7 +154,7 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode asyncUpdated = Futures.transform(asyncDetails, (Function) details -> { + ListenableFuture asyncUpdated = Futures.transform(asyncDetails, details -> { if (buildDetails) { ctx.logJsEvalResponse(); } @@ -181,10 +178,9 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode new TbAlarmResult(false, true, false, a), MoreExecutors.directExecutor()); + return Futures.transform(asyncUpdated, TbAlarmResult::fromAlarmResult, MoreExecutors.directExecutor()); } private Alarm buildAlarm(TbMsg msg, JsonNode details, TenantId tenantId) { @@ -208,7 +204,7 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class)); - long ts = msg.getTs(); + when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(null); + Alarm expectedAlarm = Alarm.builder() + .startTs(ts) + .endTs(ts) + .tenantId(tenantId) + .originator(originator) + .severity(CRITICAL) + .propagate(true) + .type("SomeType") + .details(null) + .build(); + when(alarmService.createAlarm(any(AlarmCreateOrUpdateActiveRequest.class))).thenReturn( + AlarmApiCallResult.builder() + .created(true) + .alarm(new AlarmInfo(expectedAlarm)) + .build()); + node.onMsg(ctx, msg); verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture()); @@ -153,17 +167,6 @@ public class TbAlarmNodeTest { assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); - Alarm expectedAlarm = Alarm.builder() - .startTs(ts) - .endTs(ts) - .tenantId(tenantId) - .originator(originator) - .severity(CRITICAL) - .propagate(true) - .type("SomeType") - .details(null) - .build(); - assertEquals(expectedAlarm, actualAlarm); } @@ -174,7 +177,7 @@ public class TbAlarmNodeTest { TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFailedFuture(new NotImplementedException("message"))); - when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null)); + when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(null); node.onMsg(ctx, msg); @@ -185,7 +188,7 @@ public class TbAlarmNodeTest { verify(ctx, times(3)).getDbCallbackExecutor(); verify(ctx).logJsEvalRequest(); verify(ctx).getTenantId(); - verify(alarmService).findLatestByOriginatorAndType(tenantId, originator, "SomeType"); + verify(alarmService).findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType"); verifyNoMoreInteractions(ctx, alarmService); } @@ -199,9 +202,24 @@ public class TbAlarmNodeTest { 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)); + when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(clearedAlarm); - doAnswer((Answer) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class)); + Alarm expectedAlarm = Alarm.builder() + .startTs(ts) + .endTs(ts) + .tenantId(tenantId) + .originator(originator) + .severity(CRITICAL) + .propagate(true) + .type("SomeType") + .details(null) + .build(); + when(alarmService.createAlarm(any(AlarmCreateOrUpdateActiveRequest.class))).thenReturn( + AlarmApiCallResult.builder() + .successful(true) + .created(true) + .alarm(new AlarmInfo(expectedAlarm)) + .build()); node.onMsg(ctx, msg); @@ -224,22 +242,11 @@ public class TbAlarmNodeTest { Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); - Alarm expectedAlarm = Alarm.builder() - .startTs(ts) - .endTs(ts) - .tenantId(tenantId) - .originator(originator) - .severity(CRITICAL) - .propagate(true) - .type("SomeType") - .details(null) - .build(); - assertEquals(expectedAlarm, actualAlarm); } @Test - public void alarmCanBeUpdated() throws ScriptException, IOException { + public void alarmCanBeUpdated() throws IOException { initWithCreateAlarmScript(); metaData.putValue("key", "value"); TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); @@ -248,10 +255,23 @@ public class TbAlarmNodeTest { 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)); - - doAnswer((Answer) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(activeAlarm); + when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(activeAlarm); + Alarm expectedAlarm = Alarm.builder() + .tenantId(tenantId) + .originator(originator) + .severity(CRITICAL) + .propagate(true) + .type("SomeType") + .details(null) + .endTs(activeAlarm.getEndTs()) + .build(); + when(alarmService.updateAlarm(any(AlarmUpdateRequest.class))).thenReturn( + AlarmApiCallResult.builder() + .successful(true) + .modified(true) + .alarm(new AlarmInfo(expectedAlarm)) + .build()); node.onMsg(ctx, msg); verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture()); @@ -272,17 +292,7 @@ public class TbAlarmNodeTest { assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); - assertTrue(activeAlarm.getEndTs() > oldEndDate); - Alarm expectedAlarm = Alarm.builder() - .tenantId(tenantId) - .originator(originator) - .severity(CRITICAL) - .propagate(true) - .type("SomeType") - .details(null) - .endTs(activeAlarm.getEndTs()) - .build(); - + assertTrue(activeAlarm.getEndTs() >= oldEndDate); assertEquals(expectedAlarm, actualAlarm); } @@ -290,17 +300,30 @@ public class TbAlarmNodeTest { public void alarmCanBeCleared() throws ScriptException, IOException { initWithClearAlarmScript(); 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 oldEndDate = System.currentTimeMillis(); Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(WARNING).endTs(oldEndDate).build(); + Alarm expectedAlarm = Alarm.builder() + .tenantId(tenantId) + .originator(originator) + .cleared(true) + .severity(WARNING) + .propagate(false) + .type("SomeType") + .details(null) + .endTs(oldEndDate) + .build(); + when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); - when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(activeAlarm)); - when(alarmService.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), nullable(JsonNode.class), anyLong())) - .thenReturn(Futures.immediateFuture( false)); - when(alarmService.findAlarmByIdAsync(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()))).thenReturn(Futures.immediateFuture(activeAlarm)); -// doAnswer((Answer) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(activeAlarm); + when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(activeAlarm); + when(alarmService.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), anyLong(), nullable(JsonNode.class))) + .thenReturn(AlarmApiCallResult.builder() + .successful(true) + .cleared(true) + .alarm(new AlarmInfo(expectedAlarm)) + .build()); node.onMsg(ctx, msg); @@ -322,17 +345,6 @@ public class TbAlarmNodeTest { assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); - Alarm expectedAlarm = Alarm.builder() - .tenantId(tenantId) - .originator(originator) - .cleared(true) - .severity(WARNING) - .propagate(false) - .type("SomeType") - .details(null) - .endTs(oldEndDate) - .build(); - assertEquals(expectedAlarm, actualAlarm); } @@ -340,17 +352,33 @@ public class TbAlarmNodeTest { public void alarmCanBeClearedWithAlarmOriginator() throws ScriptException, IOException { initWithClearAlarmScript(); metaData.putValue("key", "value"); - TbMsg msg = TbMsg.newMsg( "USER", alarmOriginator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg("USER", alarmOriginator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long oldEndDate = System.currentTimeMillis(); AlarmId id = new AlarmId(alarmOriginator.getId()); Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(WARNING).endTs(oldEndDate).build(); activeAlarm.setId(id); + Alarm expectedAlarm = Alarm.builder() + .tenantId(tenantId) + .originator(originator) + .cleared(true) + .severity(WARNING) + .propagate(false) + .type("SomeType") + .details(null) + .endTs(oldEndDate) + .build(); + expectedAlarm.setId(id); + when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); when(alarmService.findAlarmByIdAsync(tenantId, id)).thenReturn(Futures.immediateFuture(activeAlarm)); - when(alarmService.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), nullable(JsonNode.class), anyLong())).thenReturn(Futures.immediateFuture(true)); -// doAnswer((Answer) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(activeAlarm); + when(alarmService.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), anyLong(), nullable(JsonNode.class))) + .thenReturn(AlarmApiCallResult.builder() + .successful(true) + .cleared(true) + .alarm(new AlarmInfo(expectedAlarm)) + .build()); node.onMsg(ctx, msg); @@ -372,18 +400,6 @@ public class TbAlarmNodeTest { assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); - Alarm expectedAlarm = Alarm.builder() - .tenantId(tenantId) - .originator(originator) - .cleared(true) - .severity(WARNING) - .propagate(false) - .type("SomeType") - .details(null) - .endTs(oldEndDate) - .build(); - expectedAlarm.setId(id); - assertEquals(expectedAlarm, actualAlarm); } @@ -412,9 +428,25 @@ public class TbAlarmNodeTest { metaData.putValue("key", "value"); TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long ts = msg.getTs(); + Alarm expectedAlarm = Alarm.builder() + .startTs(ts) + .endTs(ts) + .tenantId(tenantId) + .originator(originator) + .severity(WARNING) + .propagate(true) + .type("SomeType") + .details(null) + .build(); + when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); - when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null)); - doAnswer((Answer) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class)); + when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(null); + when(alarmService.createAlarm(any(AlarmCreateOrUpdateActiveRequest.class))).thenReturn( + AlarmApiCallResult.builder() + .successful(true) + .created(true) + .alarm(new AlarmInfo(expectedAlarm)) + .build()); node.onMsg(ctx, msg); @@ -436,17 +468,6 @@ public class TbAlarmNodeTest { assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); - Alarm expectedAlarm = Alarm.builder() - .startTs(ts) - .endTs(ts) - .tenantId(tenantId) - .originator(originator) - .severity(WARNING) - .propagate(true) - .type("SomeType") - .details(null) - .build(); - assertEquals(expectedAlarm, actualAlarm); } @@ -474,10 +495,25 @@ public class TbAlarmNodeTest { metaData.putValue("alarmSeverity", "WARNING"); TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); long ts = msg.getTs(); + Alarm expectedAlarm = Alarm.builder() + .startTs(ts) + .endTs(ts) + .tenantId(tenantId) + .originator(originator) + .severity(WARNING) + .propagate(true) + .type("SomeType") + .details(null) + .build(); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); - when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null)); - doAnswer((Answer) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class)); + when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(null); + when(alarmService.createAlarm(any(AlarmCreateOrUpdateActiveRequest.class))).thenReturn( + AlarmApiCallResult.builder() + .successful(true) + .created(true) + .alarm(new AlarmInfo(expectedAlarm)) + .build()); node.onMsg(ctx, msg); @@ -498,22 +534,11 @@ public class TbAlarmNodeTest { assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); - Alarm expectedAlarm = Alarm.builder() - .startTs(ts) - .endTs(ts) - .tenantId(tenantId) - .originator(originator) - .severity(WARNING) - .propagate(true) - .type("SomeType") - .details(null) - .build(); - assertEquals(expectedAlarm, actualAlarm); } @Test - public void testCreateAlarmsWithPropagationToTenantWithDynamicTypes() throws Exception{ + public void testCreateAlarmsWithPropagationToTenantWithDynamicTypes() throws Exception { for (int i = 0; i < 10; i++) { var config = new TbCreateAlarmNodeConfiguration(); config.setPropagateToTenant(true); @@ -536,11 +561,26 @@ public class TbAlarmNodeTest { metaData.putValue("key", "value"); TbMsg msg = TbMsg.newMsg("USER", originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId); + long ts = msg.getTs(); + Alarm expectedAlarm = Alarm.builder() + .startTs(ts) + .endTs(ts) + .tenantId(tenantId) + .originator(originator) + .severity(CRITICAL) + .propagateToTenant(true) + .type("SomeType" + i) + .details(null) + .build(); when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null)); - when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType" + i)).thenReturn(Futures.immediateFuture(null)); - doAnswer((Answer) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class)); - long ts = msg.getTs(); + when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType" + i)).thenReturn(null); + when(alarmService.createAlarm(any(AlarmCreateOrUpdateActiveRequest.class))).thenReturn( + AlarmApiCallResult.builder() + .successful(true) + .created(true) + .alarm(new AlarmInfo(expectedAlarm)) + .build()); node.onMsg(ctx, msg); verify(ctx, atMost(10)).enqueue(any(), successCaptor.capture(), failureCaptor.capture()); @@ -561,17 +601,6 @@ public class TbAlarmNodeTest { assertNotSame(metaData, metadataCaptor.getValue()); Alarm actualAlarm = new ObjectMapper().readValue(dataCaptor.getValue().getBytes(), Alarm.class); - Alarm expectedAlarm = Alarm.builder() - .startTs(ts) - .endTs(ts) - .tenantId(tenantId) - .originator(originator) - .severity(CRITICAL) - .propagateToTenant(true) - .type("SomeType" + i) - .details(null) - .build(); - assertEquals(expectedAlarm, actualAlarm); } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java index fd10e1212e..f64661bc5b 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java @@ -25,6 +25,8 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest; +import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.device.profile.AlarmCondition; import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter; @@ -42,6 +44,7 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.dao.alarm.AlarmApiCallResult; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.device.DeviceService; @@ -77,11 +80,15 @@ public class DeviceStateTest { when(ctx.getAttributesService()).thenReturn(attributesService); RuleEngineAlarmService alarmService = mock(RuleEngineAlarmService.class); - when(alarmService.findLatestByOriginatorAndType(any(), any(), any())).thenReturn(Futures.immediateFuture(null)); - when(alarmService.createOrUpdateAlarm(any())).thenAnswer(invocationOnMock -> { - Alarm alarm = invocationOnMock.getArgument(0); - alarm.setId(new AlarmId(UUID.randomUUID())); - return alarm; + when(alarmService.findLatestActiveByOriginatorAndType(any(), any(), any())).thenReturn(null); + when(alarmService.createAlarm(any())).thenAnswer(invocationOnMock -> { + AlarmCreateOrUpdateActiveRequest request = invocationOnMock.getArgument(0); + return AlarmApiCallResult.builder() + .successful(true) + .created(true) + .modified(true) + .alarm(new AlarmInfo(new Alarm(new AlarmId(UUID.randomUUID())))) + .build(); }); when(ctx.getAlarmService()).thenReturn(alarmService); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java index 5d9abc130e..fb3085f3f7 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java @@ -21,7 +21,6 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.AdditionalAnswers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; @@ -34,6 +33,8 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.device.profile.AlarmCondition; import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter; @@ -46,6 +47,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.device.profile.DurationAlarmConditionSpec; import org.thingsboard.server.common.data.device.profile.RepeatingAlarmConditionSpec; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -62,6 +64,7 @@ import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.session.SessionMsgType; +import org.thingsboard.server.dao.alarm.AlarmApiCallResult; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; @@ -79,8 +82,10 @@ import java.util.TreeMap; import java.util.UUID; import java.util.concurrent.TimeUnit; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class TbDeviceProfileNodeTest { @@ -187,8 +192,8 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")).thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")).thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())).thenReturn(theMsg); @@ -205,6 +210,8 @@ public class TbDeviceProfileNodeTest { TbMsg theMsg2 = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), "2"); Mockito.when(ctx.newMsg(Mockito.any(), Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyString())).thenReturn(theMsg2); + registerCreateAlarmMock(alarmService.updateAlarm(any()), false); + TbMsg msg2 = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, new TbMsgMetaData(), TbMsgDataType.JSON, mapper.writeValueAsString(data), null, null); @@ -274,9 +281,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "alarmEnabledAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "alarmEnabledAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(attrListListenableFuture); @@ -357,9 +364,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "alarmEnabledAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "alarmEnabledAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(Futures.immediateFuture(Collections.emptyList())); @@ -430,9 +437,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFutureWithLess); @@ -524,9 +531,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFuture); @@ -642,9 +649,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.anyString(), Mockito.anyString())) .thenReturn(optionalDurationAttribute); @@ -757,9 +764,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFuture); @@ -867,9 +874,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), Mockito.anyString(), Mockito.anyString())) .thenReturn(optionalDurationAttribute); @@ -969,9 +976,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFuture); @@ -1067,9 +1074,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFuture); @@ -1149,9 +1156,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFutureActiveSchedule); @@ -1246,8 +1253,8 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "highTemperatureAlarm")) + .thenReturn(null); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFutureInactiveSchedule); @@ -1319,9 +1326,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "lessTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "lessTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFutureWithLess); @@ -1394,10 +1401,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "lessTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())) - .thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "lessTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(attributesService.find(eq(tenantId), eq(deviceId), Mockito.anyString(), Mockito.anySet())) .thenReturn(listListenableFutureWithLess); @@ -1474,10 +1480,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "lessTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())) - .thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "lessTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId)) .thenReturn(device); @@ -1485,7 +1490,7 @@ public class TbDeviceProfileNodeTest { .thenReturn(listListenableFutureWithLess); Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.anyString(), Mockito.anyString())) .thenReturn(emptyOptionalFuture); - Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) + Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); @@ -1560,10 +1565,9 @@ public class TbDeviceProfileNodeTest { Mockito.when(cache.get(tenantId, deviceId)).thenReturn(deviceProfile); Mockito.when(timeseriesService.findLatest(tenantId, deviceId, Collections.singleton("temperature"))) .thenReturn(Futures.immediateFuture(Collections.emptyList())); - Mockito.when(alarmService.findLatestByOriginatorAndType(tenantId, deviceId, "greaterTemperatureAlarm")) - .thenReturn(Futures.immediateFuture(null)); - Mockito.when(alarmService.createOrUpdateAlarm(Mockito.any())) - .thenAnswer(AdditionalAnswers.returnsFirstArg()); + Mockito.when(alarmService.findLatestActiveByOriginatorAndType(tenantId, deviceId, "greaterTemperatureAlarm")) + .thenReturn(null); + registerCreateAlarmMock(alarmService.createAlarm(any()), true); Mockito.when(ctx.getAttributesService()).thenReturn(attributesService); Mockito.when(ctx.getDeviceService().findDeviceById(tenantId, deviceId)) .thenReturn(device); @@ -1571,7 +1575,7 @@ public class TbDeviceProfileNodeTest { .thenReturn(listListenableFutureWithLess); Mockito.when(attributesService.find(eq(tenantId), eq(customerId), Mockito.anyString(), Mockito.anyString())) .thenReturn(emptyOptionalFuture); - Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) + Mockito.when(attributesService.find(eq(tenantId), eq(tenantId), eq(DataConstants.SERVER_SCOPE), Mockito.anyString())) .thenReturn(optionalListenableFutureWithLess); TbMsg theMsg = TbMsg.newMsg("ALARM", deviceId, new TbMsgMetaData(), ""); @@ -1601,4 +1605,18 @@ public class TbDeviceProfileNodeTest { node.init(ctx, nodeConfiguration); } + private void registerCreateAlarmMock(AlarmApiCallResult a, boolean created) { + when(a).thenAnswer(invocationOnMock -> { +// AlarmCreateOrUpdateActiveRequest request = invocationOnMock.getArgument(0); + AlarmInfo alarm = new AlarmInfo(new Alarm(new AlarmId(UUID.randomUUID()))); + alarm.setSeverity(AlarmSeverity.CRITICAL); + return AlarmApiCallResult.builder() + .successful(true) + .created(created) + .modified(true) + .alarm(alarm) + .build(); + }); + } + }