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 931acf908c..c2acd77953 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; @@ -502,7 +503,8 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService { if (TbSubscriptionType.ALARMS.equals(s.getType())) { @@ -303,6 +304,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene }, s -> alarm.getCreatedTime() >= s.getTs(), s -> alarm, + alarmInfo, false ); callback.onSuccess(); @@ -320,6 +322,7 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene }, s -> alarm.getCreatedTime() >= s.getTs(), s -> alarm, + null, true ); callback.onSuccess(); @@ -414,18 +417,19 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene private void onLocalAlarmSubUpdate(EntityId entityId, Function castFunction, Predicate filterFunction, - Function processFunction, boolean deleted) { + Function processFunction, AlarmInfo alarmInfo, + boolean deleted) { Set entitySubscriptions = subscriptionsByEntityId.get(entityId); if (entitySubscriptions != null) { entitySubscriptions.stream().map(castFunction).filter(Objects::nonNull).filter(filterFunction).forEach(s -> { Alarm alarm = processFunction.apply(s); if (alarm != null) { if (serviceId.equals(s.getServiceId())) { - AlarmSubscriptionUpdate update = new AlarmSubscriptionUpdate(s.getSubscriptionId(), alarm, deleted); + AlarmSubscriptionUpdate update = new AlarmSubscriptionUpdate(s.getSubscriptionId(), alarm, alarmInfo, deleted); localSubscriptionService.onSubscriptionUpdate(s.getSessionId(), update, TbCallback.EMPTY); } else { TopicPartitionInfo tpi = notificationsTopicService.getNotificationsTopic(ServiceType.TB_CORE, s.getServiceId()); - toCoreNotificationsProducer.send(tpi, toProto(s, alarm, deleted), null); + toCoreNotificationsProducer.send(tpi, toProto(s, alarm, alarmInfo, deleted), null); } } }); @@ -562,12 +566,13 @@ public class DefaultSubscriptionManagerService extends TbApplicationEventListene return new TbProtoQueueMsg<>(subscription.getEntityId().getId(), toCoreMsg); } - private TbProtoQueueMsg toProto(TbSubscription subscription, Alarm alarm, boolean deleted) { + private TbProtoQueueMsg toProto(TbSubscription subscription, Alarm alarm, AlarmInfo alarmInfo, boolean deleted) { TbAlarmSubscriptionUpdateProto.Builder builder = TbAlarmSubscriptionUpdateProto.newBuilder(); builder.setSessionId(subscription.getSessionId()); builder.setSubscriptionId(subscription.getSubscriptionId()); builder.setAlarm(JacksonUtil.toString(alarm)); + builder.setAlarmInfo(JacksonUtil.toString(alarmInfo)); 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 15aab86327..747a73ebe1 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; @@ -42,7 +43,7 @@ public interface SubscriptionManagerService extends ApplicationListener keys, TbCallback callback); - void onAlarmUpdate(TenantId tenantId, EntityId entityId, Alarm alarm, TbCallback callback); + void onAlarmUpdate(TenantId tenantId, EntityId entityId, Alarm alarm, AlarmInfo alarmInfo, TbCallback callback); void onAlarmDeleted(TenantId tenantId, EntityId entityId, Alarm 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 5804002c77..7afc82c03b 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 @@ -20,6 +20,7 @@ import lombok.Setter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.EntityId; @@ -219,7 +220,7 @@ public class TbAlarmDataSubCtx extends TbAbstractDataSubCtx { boolean matchesFilter = filter(alarm); if (onCurrentPage) { if (matchesFilter) { - AlarmData updated = new AlarmData(alarm, current.getOriginatorName(), current.getEntityId()); + AlarmData updated = new AlarmData(alarm, subscriptionUpdate.getAlarmInfo(), current.getEntityId()); updated.getLatest().putAll(current.getLatest()); alarmsMap.put(alarmId, updated); sendWsMsg(new AlarmDataUpdate(cmdId, null, Collections.singletonList(updated), maxEntitiesPerAlarmSubscription, data.getTotalElements())); 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 f8359a21ee..91583e1194 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 @@ -17,6 +17,7 @@ package org.thingsboard.server.service.subscription; 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.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; @@ -190,7 +191,8 @@ public class TbSubscriptionUtils { return new AlarmSubscriptionUpdate(proto.getSubscriptionId(), SubscriptionErrorCode.forCode(proto.getErrorCode()), proto.getErrorMsg()); } else { Alarm alarm = JacksonUtil.fromString(proto.getAlarm(), Alarm.class); - return new AlarmSubscriptionUpdate(proto.getSubscriptionId(), alarm); + AlarmInfo alarmInfo = JacksonUtil.fromString(proto.getAlarmInfo(), AlarmInfo.class); + return new AlarmSubscriptionUpdate(proto.getSubscriptionId(), alarm, alarmInfo); } } 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 f7d121d9ff..634c90880a 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 @@ -187,7 +187,13 @@ 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, TbCallback.EMPTY); + AlarmInfo alarmInfo = new AlarmInfo(alarm); + alarmInfo.setOriginatorName(result.getAlarmAdditionalInfo().getOriginatorName()); + alarmInfo.setOriginatorLabel(result.getAlarmAdditionalInfo().getOriginatorName()); + alarmInfo.setAssigneeFirstName(result.getAlarmAdditionalInfo().getFirstName()); + alarmInfo.setAssigneeLastName(result.getAlarmAdditionalInfo().getLastName()); + alarmInfo.setAssigneeEmail(result.getAlarmAdditionalInfo().getEmail()); + subscriptionManagerService.get().onAlarmUpdate(tenantId, entityId, alarm, alarmInfo, TbCallback.EMPTY); } else { log.warn("Possible misconfiguration because subscriptionManagerService is null!"); } 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 3f4bc9ce1e..10b750c5c7 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 @@ -17,6 +17,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.AlarmInfo; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.query.AlarmData; @@ -38,16 +39,19 @@ public class AlarmSubscriptionUpdate { @Getter private Alarm alarm; @Getter + private AlarmInfo alarmInfo; + @Getter private boolean alarmDeleted; - public AlarmSubscriptionUpdate(int subscriptionId, Alarm alarm) { - this(subscriptionId, alarm, false); + public AlarmSubscriptionUpdate(int subscriptionId, Alarm alarm, AlarmInfo alarmInfo) { + this(subscriptionId, alarm, alarmInfo, false); } - public AlarmSubscriptionUpdate(int subscriptionId, Alarm alarm, boolean alarmDeleted) { + public AlarmSubscriptionUpdate(int subscriptionId, Alarm alarm, AlarmInfo alarmInfo, boolean alarmDeleted) { super(); this.subscriptionId = subscriptionId; this.alarm = alarm; + this.alarmInfo = alarmInfo; this.alarmDeleted = alarmDeleted; } @@ -65,6 +69,6 @@ public class AlarmSubscriptionUpdate { @Override public String toString() { return "AlarmUpdate [subscriptionId=" + subscriptionId + ", errorCode=" + errorCode + ", errorMsg=" + errorMsg + ", alarm=" - + alarm + "]"; + + alarm + ", alarmInfo=" + alarmInfo + "]"; } } diff --git a/common/cluster-api/src/main/proto/queue.proto b/common/cluster-api/src/main/proto/queue.proto index 5a28d42546..4ef017715d 100644 --- a/common/cluster-api/src/main/proto/queue.proto +++ b/common/cluster-api/src/main/proto/queue.proto @@ -562,6 +562,7 @@ message TbAlarmSubscriptionUpdateProto { string errorMsg = 4; string alarm = 5; bool deleted = 6; + string alarmInfo = 7; } message TbAttributeUpdateProto { @@ -581,6 +582,7 @@ message TbAlarmUpdateProto { int64 tenantIdMSB = 4; int64 tenantIdLSB = 5; string alarm = 6; + string alarmInfo = 7; } message TbAlarmDeleteProto { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmAdditionalInfo.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmAdditionalInfo.java new file mode 100644 index 0000000000..a726ca599a --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmAdditionalInfo.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2022 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.alarm; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +@AllArgsConstructor +public class AlarmAdditionalInfo { + private final String originatorName; + private final String originatorLabel; + + private final String firstName; + private final String lastName; + private final String email; +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmOperationResult.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmOperationResult.java index 4940c74191..c8d2c0aad9 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmOperationResult.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmOperationResult.java @@ -28,19 +28,25 @@ public class AlarmOperationResult { private final boolean successful; private final boolean created; private final List propagatedEntitiesList; + private final AlarmAdditionalInfo alarmAdditionalInfo; public AlarmOperationResult(Alarm alarm, boolean successful) { - this(alarm, successful, Collections.emptyList()); + this(alarm, successful, Collections.emptyList(), new AlarmAdditionalInfo(null, null, null, null, null)); } - public AlarmOperationResult(Alarm alarm, boolean successful, List propagatedEntitiesList) { - this(alarm, successful, false, propagatedEntitiesList); + public AlarmOperationResult(Alarm alarm, boolean successful, AlarmAdditionalInfo alarmAdditionalInfo) { + this(alarm, successful, Collections.emptyList(), alarmAdditionalInfo); } - public AlarmOperationResult(Alarm alarm, boolean successful, boolean created, List propagatedEntitiesList) { + public AlarmOperationResult(Alarm alarm, boolean successful, List propagatedEntitiesList, AlarmAdditionalInfo alarmAdditionalInfo) { + this(alarm, successful, false, propagatedEntitiesList, alarmAdditionalInfo); + } + + public AlarmOperationResult(Alarm alarm, boolean successful, boolean created, List propagatedEntitiesList, AlarmAdditionalInfo alarmAdditionalInfo) { this.alarm = alarm; this.successful = successful; this.created = created; this.propagatedEntitiesList = propagatedEntitiesList; + this.alarmAdditionalInfo = alarmAdditionalInfo; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java index 54537276bd..9b55324800 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java @@ -17,15 +17,42 @@ package org.thingsboard.server.common.data.alarm; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import org.thingsboard.server.common.data.User; + +import java.util.Objects; @ApiModel public class AlarmInfo extends Alarm { private static final long serialVersionUID = 2807343093519543363L; + @Getter + @Setter @ApiModelProperty(position = 19, value = "Alarm originator name", example = "Thermostat") private String originatorName; + @Getter + @Setter + @ApiModelProperty(position = 20, value = "Alarm originator label", example = "Thermostat label") + private String originatorLabel; + + @Getter + @Setter + @ApiModelProperty(position = 21, value = "Alarm assignee first name") + private String assigneeFirstName; + + @Getter + @Setter + @ApiModelProperty(position = 22, value = "Alarm assignee last name") + private String assigneeLastName; + + @Getter + @Setter + @ApiModelProperty(position = 23, value = "Alarm assignee email") + private String assigneeEmail; + public AlarmInfo() { super(); } @@ -34,17 +61,13 @@ public class AlarmInfo extends Alarm { super(alarm); } - public AlarmInfo(Alarm alarm, String originatorName) { + public AlarmInfo(Alarm alarm, AlarmInfo alarmInfo) { super(alarm); - this.originatorName = originatorName; - } - - public String getOriginatorName() { - return originatorName; - } - - public void setOriginatorName(String originatorName) { - this.originatorName = originatorName; + originatorName = alarmInfo.originatorName; + originatorLabel = alarmInfo.originatorLabel; + assigneeFirstName = alarmInfo.assigneeFirstName; + assigneeLastName = alarmInfo.assigneeLastName; + assigneeEmail = alarmInfo.assigneeEmail; } @Override @@ -55,8 +78,11 @@ public class AlarmInfo extends Alarm { AlarmInfo alarmInfo = (AlarmInfo) o; - return originatorName != null ? originatorName.equals(alarmInfo.originatorName) : alarmInfo.originatorName == null; - + return (Objects.equals(originatorName, alarmInfo.originatorName)) && + (Objects.equals(originatorLabel, alarmInfo.originatorLabel)) && + (Objects.equals(assigneeFirstName, alarmInfo.assigneeFirstName)) && + (Objects.equals(assigneeLastName, alarmInfo.assigneeLastName)) && + (Objects.equals(assigneeEmail, alarmInfo.assigneeEmail)); } @Override 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 c288fec13e..cae512af18 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 @@ -31,8 +31,14 @@ public class AlarmData extends AlarmInfo { @Getter private final Map> latest; - public AlarmData(Alarm alarm, String originatorName, EntityId entityId) { - super(alarm, originatorName); + public AlarmData(Alarm alarm, AlarmInfo alarmInfo, EntityId entityId) { + super(alarm, alarmInfo); + this.entityId = entityId; + this.latest = new HashMap<>(); + } + + public AlarmData(Alarm alarm, EntityId entityId) { + super(alarm); this.entityId = entityId; this.latest = new HashMap<>(); } 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 3abefb780b..1ccac7a19b 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 @@ -27,6 +27,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.thingsboard.common.util.ThingsBoardThreadFactory; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityView; +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.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; @@ -34,10 +40,15 @@ 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.EntityAlarm; +import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException; import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; @@ -47,9 +58,16 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.EntitySearchDirection; import org.thingsboard.server.common.data.relation.RelationsSearchParameters; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.EntityService; +import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.dao.user.UserService; import javax.annotation.Nullable; import javax.annotation.PostConstruct; @@ -81,6 +99,27 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @Autowired private EntityService entityService; + @Autowired + private UserService userService; + + @Autowired + private TenantService tenantService; + + @Autowired + private CustomerService customerService; + + @Autowired + private DashboardService dashboardService; + + @Autowired + private AssetService assetService; + + @Autowired + private DeviceService deviceService; + + @Autowired + private EntityViewService entityViewService; + @Autowired private DataValidator alarmDataValidator; @@ -151,7 +190,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ if (alarm == null) { return new AlarmOperationResult(alarm, false); } - AlarmOperationResult result = new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); + AlarmAdditionalInfo alarmAdditionalInfo = getAlarmAdditionalInfo(tenantId, alarm); + AlarmOperationResult result = new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)), alarmAdditionalInfo); deleteEntityRelations(tenantId, alarm.getId()); alarmDao.removeById(tenantId, alarm.getUuidId()); return result; @@ -161,7 +201,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ log.debug("New Alarm : {}", alarm); Alarm saved = alarmDao.save(alarm.getTenantId(), alarm); List propagatedEntitiesList = createEntityAlarmRecords(saved); - return new AlarmOperationResult(saved, true, true, propagatedEntitiesList); + AlarmAdditionalInfo alarmAdditionalInfo = getAlarmAdditionalInfo(alarm.getTenantId(), alarm); + return new AlarmOperationResult(saved, true, true, propagatedEntitiesList, alarmAdditionalInfo); } private List createEntityAlarmRecords(Alarm alarm) throws InterruptedException, ExecutionException { @@ -216,7 +257,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } else { propagatedEntitiesList = new ArrayList<>(getPropagationEntityIds(result)); } - return new AlarmOperationResult(result, true, propagatedEntitiesList); + AlarmAdditionalInfo alarmAdditionalInfo = getAlarmAdditionalInfo(newAlarm.getTenantId(), newAlarm); + return new AlarmOperationResult(result, true, propagatedEntitiesList, alarmAdditionalInfo); } @Override @@ -233,7 +275,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ alarm.setStatus(newStatus); alarm.setAckTs(ackTime); alarm = alarmDao.save(alarm.getTenantId(), alarm); - return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); + AlarmAdditionalInfo alarmAdditionalInfo = getAlarmAdditionalInfo(tenantId, alarm); + return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)), alarmAdditionalInfo); } } }); @@ -256,7 +299,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ alarm.setDetails(details); } alarm = alarmDao.save(alarm.getTenantId(), alarm); - return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); + AlarmAdditionalInfo alarmAdditionalInfo = getAlarmAdditionalInfo(tenantId, alarm); + return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)), alarmAdditionalInfo); } } }); @@ -274,7 +318,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ alarm.setAssigneeId(assigneeId); alarm.setAssignTs(assignTime); alarm = alarmDao.save(alarm.getTenantId(), alarm); - return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); + AlarmAdditionalInfo alarmAdditionalInfo = getAlarmAdditionalInfo(tenantId, alarm); + return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)), alarmAdditionalInfo); } } }); @@ -292,7 +337,8 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ alarm.setAssigneeId(null); alarm.setAssignTs(assignTime); alarm = alarmDao.save(alarm.getTenantId(), alarm); - return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); + AlarmAdditionalInfo alarmAdditionalInfo = getAlarmAdditionalInfo(tenantId, alarm); + return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)), alarmAdditionalInfo); } } }); @@ -396,10 +442,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ if (alarm.getAckTs() > existing.getAckTs()) { existing.setAckTs(alarm.getAckTs()); } + if (alarm.getAssignTs() > existing.getAssignTs()) { + existing.setAssignTs(alarm.getAssignTs()); + } existing.setStatus(alarm.getStatus()); existing.setSeverity(alarm.getSeverity()); existing.setDetails(alarm.getDetails()); existing.setCustomerId(alarm.getCustomerId()); + existing.setAssigneeId(alarm.getAssigneeId()); existing.setPropagate(existing.isPropagate() || alarm.isPropagate()); existing.setPropagateToOwner(existing.isPropagateToOwner() || alarm.isPropagateToOwner()); existing.setPropagateToTenant(existing.isPropagateToTenant() || alarm.isPropagateToTenant()); @@ -447,4 +497,63 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ Alarm entity = alarmDao.findAlarmById(tenantId, alarmId.getId()); return function.apply(entity); } + + private AlarmAdditionalInfo getAlarmAdditionalInfo(TenantId tenantId, Alarm alarm) { + AlarmAdditionalInfo.AlarmAdditionalInfoBuilder builder = AlarmAdditionalInfo.builder(); + + addAlarmOriginatorNameAndLabel(tenantId, alarm.getOriginator(), builder); + + if (alarm.getAssigneeId() != null) { + User assignedUser = userService.findUserById(tenantId, alarm.getAssigneeId()); + builder.firstName(assignedUser.getFirstName()); + builder.lastName(assignedUser.getLastName()); + builder.email(assignedUser.getEmail()); + } + return builder.build(); + } + + private void addAlarmOriginatorNameAndLabel(TenantId tenantId, EntityId originatorId, AlarmAdditionalInfo.AlarmAdditionalInfoBuilder builder) { + String originatorName = "Unknown"; + String originatorLabel = "Unknown"; + switch(originatorId.getEntityType()) { + case TENANT: + Tenant tenantOriginator = tenantService.findTenantById((TenantId) originatorId); + originatorName = tenantOriginator.getTitle(); + originatorLabel = tenantOriginator.getEmail(); + break; + case CUSTOMER: + Customer customerOriginator = customerService.findCustomerById(tenantId, (CustomerId) originatorId); + originatorName = customerOriginator.getTitle(); + originatorLabel = customerOriginator.getEmail(); + break; + case USER: + User userOriginator = userService.findUserById(tenantId, (UserId) originatorId); + originatorName = userOriginator.getEmail(); + originatorLabel = userOriginator.getName(); + break; + case DASHBOARD: + Dashboard dashboardOriginator = dashboardService.findDashboardById(tenantId, (DashboardId) originatorId); + originatorName = dashboardOriginator.getTitle(); + originatorLabel = dashboardOriginator.getName(); + break; + case ASSET: + Asset assetOriginator = assetService.findAssetById(tenantId, (AssetId) originatorId); + originatorName = assetOriginator.getName(); + originatorLabel = assetOriginator.getLabel(); + break; + case DEVICE: + Device deviceOriginator = deviceService.findDeviceById(tenantId, (DeviceId) originatorId); + originatorName = deviceOriginator.getName(); + originatorLabel = deviceOriginator.getLabel(); + break; + case ENTITY_VIEW: + EntityView entityViewOriginator = entityViewService.findEntityViewById(tenantId, (EntityViewId) originatorId); + originatorName = entityViewOriginator.getName(); + originatorLabel = entityViewOriginator.getType(); // TODO Should we use something else? + break; + } + + builder.originatorName(originatorName); + builder.originatorLabel(originatorLabel); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index c64dea0c40..da2ffc7c89 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -284,10 +284,14 @@ public class ModelConstants { public static final String ALARM_DETAILS_PROPERTY = "details"; public static final String ALARM_ORIGINATOR_ID_PROPERTY = "originator_id"; public static final String ALARM_ORIGINATOR_NAME_PROPERTY = "originator_name"; + public static final String ALARM_ORIGINATOR_LABEL_PROPERTY = "originator_label"; public static final String ALARM_ORIGINATOR_TYPE_PROPERTY = "originator_type"; public static final String ALARM_SEVERITY_PROPERTY = "severity"; public static final String ALARM_STATUS_PROPERTY = "status"; public static final String ALARM_ASSIGNEE_ID_PROPERTY = "assignee_id"; + public static final String ALARM_ASSIGNEE_FIRST_NAME_PROPERTY = "assignee_first_name"; + public static final String ALARM_ASSIGNEE_LAST_NAME_PROPERTY = "assignee_last_name"; + public static final String ALARM_ASSIGNEE_EMAIL_PROPERTY = "assignee_email"; public static final String ALARM_START_TS_PROPERTY = "start_ts"; public static final String ALARM_END_TS_PROPERTY = "end_ts"; public static final String ALARM_ACK_TS_PROPERTY = "ack_ts"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java index cef7c47a73..aada0c7c17 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java @@ -24,6 +24,11 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; public class AlarmInfoEntity extends AbstractAlarmEntity { private String originatorName; + private String originatorLabel; + + private String assigneeFirstName; + private String assigneeLastName; + private String assigneeEmail; public AlarmInfoEntity() { super(); @@ -35,6 +40,13 @@ public class AlarmInfoEntity extends AbstractAlarmEntity { @Override public AlarmInfo toData() { - return new AlarmInfo(super.toAlarm(), this.originatorName); + AlarmInfo alarmInfo = new AlarmInfo(super.toAlarm()); + alarmInfo.setOriginatorName(originatorName); + alarmInfo.setOriginatorLabel(originatorLabel); + + alarmInfo.setAssigneeFirstName(assigneeFirstName); + alarmInfo.setAssigneeLastName(assigneeLastName); + alarmInfo.setAssigneeEmail(assigneeEmail); + return alarmInfo; } } 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 ff156eb51c..e7ea6a1b66 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 @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.EntityDataPageLink; @@ -68,6 +69,7 @@ public class AlarmDataAdapter { alarm.setCreatedTime((long) row.get(ModelConstants.CREATED_TIME_PROPERTY)); alarm.setAckTs((long) row.get(ModelConstants.ALARM_ACK_TS_PROPERTY)); alarm.setClearTs((long) row.get(ModelConstants.ALARM_CLEAR_TS_PROPERTY)); + alarm.setAssignTs((long) row.get(ModelConstants.ALARM_ASSIGN_TS_PROPERTY)); alarm.setStartTs((long) row.get(ModelConstants.ALARM_START_TS_PROPERTY)); alarm.setEndTs((long) row.get(ModelConstants.ALARM_END_TS_PROPERTY)); Object additionalInfo = row.get(ModelConstants.ADDITIONAL_INFO_PROPERTY); @@ -81,6 +83,16 @@ public class AlarmDataAdapter { EntityType originatorType = EntityType.values()[(int) row.get(ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY)]; UUID originatorId = (UUID) row.get(ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY); alarm.setOriginator(EntityIdFactory.getByTypeAndUuid(originatorType, originatorId)); + Object assigneeIdObj = row.get(ModelConstants.ASSIGNEE_ID_PROPERTY); + String assigneeFirstName = null; + String assigneeLastName = null; + String assigneeEmail = null; + if (assigneeIdObj != null) { + alarm.setAssigneeId(new UserId((UUID) row.get(ModelConstants.ALARM_ASSIGNEE_ID_PROPERTY))); + assigneeFirstName = row.get(ModelConstants.ALARM_ASSIGNEE_FIRST_NAME_PROPERTY).toString(); + assigneeLastName = row.get(ModelConstants.ALARM_ASSIGNEE_LAST_NAME_PROPERTY).toString(); + assigneeEmail = row.get(ModelConstants.ALARM_ASSIGNEE_EMAIL_PROPERTY).toString(); + } alarm.setPropagate((boolean) row.get(ModelConstants.ALARM_PROPAGATE_PROPERTY)); alarm.setPropagateToOwner((boolean) row.get(ModelConstants.ALARM_PROPAGATE_TO_OWNER_PROPERTY)); alarm.setPropagateToTenant((boolean) row.get(ModelConstants.ALARM_PROPAGATE_TO_TENANT_PROPERTY)); @@ -105,7 +117,17 @@ public class AlarmDataAdapter { EntityId entityId = entityIdMap.get(entityUuid); Object originatorNameObj = row.get(ModelConstants.ALARM_ORIGINATOR_NAME_PROPERTY); String originatorName = originatorNameObj != null ? originatorNameObj.toString() : null; - return new AlarmData(alarm, originatorName, entityId); + Object originatorLabelObj = row.get(ModelConstants.ALARM_ORIGINATOR_LABEL_PROPERTY); + String originatorLabel = originatorLabelObj != null ? originatorLabelObj.toString() : null; + + AlarmData alarmData = new AlarmData(alarm, entityId); + alarmData.setOriginatorName(originatorName); + alarmData.setOriginatorLabel(originatorLabel); + alarmData.setAssigneeFirstName(assigneeFirstName); + alarmData.setAssigneeLastName(assigneeLastName); + alarmData.setAssigneeEmail(assigneeEmail); + + return alarmData; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java index d91a8c5fc4..d3b8137f8b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java @@ -57,6 +57,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { alarmFieldColumnMap.put("ackTime", ModelConstants.ALARM_ACK_TS_PROPERTY); alarmFieldColumnMap.put("clearTs", ModelConstants.ALARM_CLEAR_TS_PROPERTY); alarmFieldColumnMap.put("clearTime", ModelConstants.ALARM_CLEAR_TS_PROPERTY); + alarmFieldColumnMap.put("assignTime", ModelConstants.ALARM_ASSIGN_TS_PROPERTY); alarmFieldColumnMap.put("details", ModelConstants.ADDITIONAL_INFO_PROPERTY); alarmFieldColumnMap.put("endTs", ModelConstants.ALARM_END_TS_PROPERTY); alarmFieldColumnMap.put("endTime", ModelConstants.ALARM_END_TS_PROPERTY); @@ -67,7 +68,12 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { alarmFieldColumnMap.put("severity", ModelConstants.ALARM_SEVERITY_PROPERTY); alarmFieldColumnMap.put("originatorId", ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY); alarmFieldColumnMap.put("originatorType", ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY); + alarmFieldColumnMap.put("assigneeId", ModelConstants.ALARM_ASSIGNEE_ID_PROPERTY); alarmFieldColumnMap.put("originator", "originator_name"); + alarmFieldColumnMap.put("originatorLabel", ModelConstants.ALARM_ORIGINATOR_LABEL_PROPERTY); + alarmFieldColumnMap.put("assigneeFirstName", ModelConstants.ALARM_ASSIGNEE_FIRST_NAME_PROPERTY); + alarmFieldColumnMap.put("assigneeLastName", ModelConstants.ALARM_ASSIGNEE_LAST_NAME_PROPERTY); + alarmFieldColumnMap.put("assigneeEmail", ModelConstants.ALARM_ASSIGNEE_EMAIL_PROPERTY); } private static final String SELECT_ORIGINATOR_NAME = " COALESCE(CASE" + @@ -87,10 +93,15 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { " THEN (select name from entity_view where id = a.originator_id)" + " END, 'Deleted') as originator_name"; + private static final String SELECT_ASSIGNEE_INFO = " tbu.first_name as assignee_first_name," + + " tbu.last_name as assignee_last_name," + + " tbu.email as assignee_email"; + private static final String FIELDS_SELECTION = "select a.id as id," + " a.created_time as created_time," + " a.ack_ts as ack_ts," + " a.clear_ts as clear_ts," + + " a.assign_ts as assign_ts," + " a.additional_info as additional_info," + " a.end_ts as end_ts," + " a.originator_id as originator_id," + @@ -104,9 +115,11 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { " a.tenant_id as tenant_id, " + " a.customer_id as customer_id, " + " a.propagate_relation_types as propagate_relation_types, " + - " a.type as type," + SELECT_ORIGINATOR_NAME + ", "; + " a.type as type," + SELECT_ORIGINATOR_NAME + ", " + + SELECT_ASSIGNEE_INFO + ", "; private static final String JOIN_ENTITY_ALARMS = "inner join entity_alarm ea on a.id = ea.alarm_id"; + private static final String LEFT_JOIN_TB_USERS = "left join tb_user tbu on a.assignee_id = tbu.id"; protected final NamedParameterJdbcTemplate jdbcTemplate; private final TransactionTemplate transactionTemplate; @@ -139,6 +152,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { } else { selectPart.append(" a.originator_id as entity_id "); } + fromPart.append(LEFT_JOIN_TB_USERS); EntityDataSortOrder sortOrder = pageLink.getSortOrder(); String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch()); if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) { diff --git a/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts b/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts index be3aff8c50..f8c257e641 100644 --- a/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/alarm/alarm-details-dialog.component.ts @@ -84,10 +84,15 @@ export class AlarmDetailsDialogComponent extends DialogComponent { export interface AlarmInfo extends Alarm { originatorName: string; + assigneeFirstName: string; + assigneeLastName: string; + assigneeEmail: string; } export interface AlarmDataInfo extends AlarmInfo { @@ -126,6 +129,9 @@ export const simulatedAlarm: AlarmInfo = { clearTs: 0, assignTs: 0, originatorName: 'Simulated', + assigneeFirstName: "", + assigneeLastName: "", + assigneeEmail: "test@example.com", originator: { entityType: EntityType.DEVICE, id: '1' @@ -207,6 +213,26 @@ export const alarmFields: {[fieldName: string]: AlarmField} = { keyName: 'status', value: 'status', name: 'alarm.status' + }, + assigneeId: { + keyName: 'assigneeId', + value: 'assigneeId.id', + name: 'alarm.assignee-id' + }, + assigneeFirstName: { + keyName: 'assigneeFirstName', + value: 'assigneeFirstName', + name: 'alarm.assignee-first-name' + }, + assigneeLastName: { + keyName: 'assigneeLastName', + value: 'assigneeLastName', + name: 'alarm.assignee-last-name' + }, + assigneeEmail: { + keyName: 'assigneeEmail', + value: 'assigneeEmail', + name: 'alarm.assignee-email' } }; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 269e2e0f67..d191442427 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -433,6 +433,10 @@ "severity": "Severity", "originator": "Originator", "originator-type": "Originator type", + "assignee-id": "Assignee id", + "assignee-first-name": "Assignee first name", + "assignee-last-name": "Assignee last name", + "assignee-email": "Assignee email", "details": "Details", "status": "Status", "alarm-details": "Alarm details",