diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index c6a3ac6105..8505b710d5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -724,7 +724,7 @@ public abstract class BaseController { AlarmInfo checkAlarmInfoId(AlarmId alarmId, Operation operation) throws ThingsboardException { try { validateId(alarmId, "Incorrect alarmId " + alarmId); - AlarmInfo alarmInfo = alarmService.findAlarmInfoByIdAsync(getCurrentUser().getTenantId(), alarmId).get(); + AlarmInfo alarmInfo = alarmService.findAlarmInfoById(getCurrentUser().getTenantId(), alarmId); checkNotNull(alarmInfo, "Alarm with id [" + alarmId + "] is not found"); accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, operation, alarmId, alarmInfo); return 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 8c6037576f..71d58b48a3 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 @@ -174,8 +174,8 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService } @Override - public ListenableFuture findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId) { - return alarmService.findAlarmInfoByIdAsync(tenantId, alarmId); + public AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId) { + return alarmService.findAlarmInfoById(tenantId, alarmId); } @Override 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 2e5660e1c8..e160053ff9 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 @@ -23,6 +23,8 @@ 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.alarm.CreateOrUpdateActiveAlarmRequest; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -35,30 +37,51 @@ import org.thingsboard.server.dao.entity.EntityDaoService; import java.util.Collection; -/** - * Created by ashvayka on 11.05.17. - */ + public interface AlarmService extends EntityDaoService { + // New API, since 3.5. + /** + * Designed for atomic operations over active alarms. + * Only one active alarm may exist for the pair {originatorId, alarmType} + */ + AlarmOperationResult createAlarm(CreateOrUpdateActiveAlarmRequest request); + + /** + * Designed for atomic operations over active alarms. + * Only one active alarm may exist for the pair {originatorId, alarmType} + */ + AlarmOperationResult createAlarm(CreateOrUpdateActiveAlarmRequest request, boolean alarmCreationEnabled); + + /** + * Designed to update existing alarm. Accepts only part of the alarm fields. + * + */ + AlarmOperationResult updateAlarm(AlarmUpdateRequest request); + + // Legacy API, before 3.5 + AlarmOperationResult createOrUpdateAlarm(Alarm alarm); AlarmOperationResult createOrUpdateAlarm(Alarm alarm, boolean alarmCreationEnabled); + // Other API + AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId); ListenableFuture ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs); ListenableFuture clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTs); - AlarmOperationResult assignAlarm(TenantId tenantId, AlarmId alarmId, UserId assigneeId, long assignTs); + AlarmOperationResult assignAlarm(TenantId tenantId, AlarmId alarmId, UserId assigneeId, long ts); - AlarmOperationResult unassignAlarm(TenantId tenantId, AlarmId alarmId, long assignTs); + AlarmOperationResult unassignAlarm(TenantId tenantId, AlarmId alarmId, long ts); Alarm findAlarmById(TenantId tenantId, AlarmId alarmId); ListenableFuture findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId); - ListenableFuture findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId); + AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId); ListenableFuture> findAlarms(TenantId tenantId, AlarmQuery query); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmPropagationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmPropagationInfo.java new file mode 100644 index 0000000000..78d62de523 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmPropagationInfo.java @@ -0,0 +1,37 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.alarm; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +@Data +public class AlarmPropagationInfo { + + @ApiModelProperty(position = 1, value = "Propagation flag to specify if alarm should be propagated to parent entities of alarm originator", example = "true") + private boolean propagate; + @ApiModelProperty(position = 2, value = "Propagation flag to specify if alarm should be propagated to the owner (tenant or customer) of alarm originator", example = "true") + private boolean propagateToOwner; + @ApiModelProperty(position = 3, value = "Propagation flag to specify if alarm should be propagated to the tenant entity", example = "true") + private boolean propagateToTenant; + @ApiModelProperty(position = 4, value = "JSON array of relation types that should be used for propagation. " + + "By default, 'propagateRelationTypes' array is empty which means that the alarm will be propagated based on any relation type to parent entities. " + + "This parameter should be used only in case when 'propagate' parameter is set to true, otherwise, 'propagateRelationTypes' array will be ignored.") + private List propagateRelationTypes; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java index d6af82f413..e4d0fb32df 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java @@ -35,6 +35,7 @@ public class AlarmQuery { private AlarmSearchStatus searchStatus; private AlarmStatus status; private UserId assigneeId; + @Deprecated private Boolean fetchOriginator; } 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 new file mode 100644 index 0000000000..a5d88edb41 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.alarm; + +import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.TenantId; + +@Data +public class AlarmUpdateRequest { + + @ApiModelProperty(position = 1, value = "JSON object with Tenant Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private TenantId tenantId; + @ApiModelProperty(position = 2, value = "JSON object with the alarm Id. " + + "Specify this field to update the alarm. " + + "Referencing non-existing alarm Id will cause error. " + + "Omit this field to create new alarm.") + private AlarmId alarmId; + @ApiModelProperty(position = 3, required = true, value = "Alarm severity", example = "CRITICAL") + private AlarmSeverity severity; + @ApiModelProperty(position = 4, value = "Timestamp of the alarm start time, in milliseconds", example = "1634058704565") + private long startTs; + @ApiModelProperty(position = 5, value = "Timestamp of the alarm end time(last time update), in milliseconds", example = "1634111163522") + private long endTs; + @ApiModelProperty(position = 6, value = "JSON object with alarm details") + private JsonNode details; + @ApiModelProperty(position = 7, value = "JSON object with propagation details") + private AlarmPropagationInfo propagation; + +} 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/CreateOrUpdateActiveAlarmRequest.java new file mode 100644 index 0000000000..afd4a2e537 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/CreateOrUpdateActiveAlarmRequest.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.alarm; + +import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.annotations.ApiModelProperty; +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; +import org.thingsboard.server.common.data.validation.Length; + +@Data +public class CreateOrUpdateActiveAlarmRequest { + + @ApiModelProperty(position = 1, value = "JSON object with Tenant Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private TenantId tenantId; + @ApiModelProperty(position = 2, value = "JSON object with Customer Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private CustomerId customerId; + @ApiModelProperty(position = 3, required = true, value = "representing type of the Alarm", example = "High Temperature Alarm") + @Length(fieldName = "type") + private String type; + @ApiModelProperty(position = 4, required = true, value = "JSON object with alarm originator id") + private EntityId originator; + @ApiModelProperty(position = 5, required = true, value = "Alarm severity", example = "CRITICAL") + private AlarmSeverity severity; + @ApiModelProperty(position = 6, value = "Timestamp of the alarm start time, in milliseconds", example = "1634058704565") + private long startTs; + @ApiModelProperty(position = 7, value = "Timestamp of the alarm end time(last time update), in milliseconds", example = "1634111163522") + private long endTs; + @ApiModelProperty(position = 8, value = "JSON object with alarm details") + private JsonNode details; + @ApiModelProperty(position = 9, value = "JSON object with propagation details") + private AlarmPropagationInfo propagation; + +} 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 fa1a8a2eb9..daa8d2e206 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 @@ -51,6 +51,8 @@ public interface AlarmDao extends Dao { ListenableFuture findAlarmByIdAsync(TenantId tenantId, UUID key); + AlarmInfo findAlarmInfoById(TenantId tenantId, UUID key); + Alarm save(TenantId tenantId, Alarm alarm); PageData findAlarms(TenantId tenantId, AlarmQuery query); 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 218ffd2b52..4ae0954816 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 @@ -26,17 +26,18 @@ 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.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmAssignee; import org.thingsboard.server.common.data.alarm.AlarmAssigneeUpdate; +import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest; import org.thingsboard.server.common.data.alarm.AlarmInfo; 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.EntityAlarm; import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException; import org.thingsboard.server.common.data.id.AlarmId; @@ -59,8 +60,6 @@ import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.user.UserService; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -69,8 +68,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -90,24 +87,23 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @Autowired private EntityService entityService; - @Autowired - private UserService userService; - @Autowired private DataValidator alarmDataValidator; - protected ExecutorService readResultsProcessingExecutor; - @PostConstruct - public void startExecutor() { - readResultsProcessingExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("alarm-service")); + @Override + public AlarmOperationResult updateAlarm(AlarmUpdateRequest request) { + return null; } - @PreDestroy - public void stopExecutor() { - if (readResultsProcessingExecutor != null) { - readResultsProcessingExecutor.shutdownNow(); - } + @Override + public AlarmOperationResult createAlarm(CreateOrUpdateActiveAlarmRequest request) { + return null; + } + + @Override + public AlarmOperationResult createAlarm(CreateOrUpdateActiveAlarmRequest request, boolean alarmCreationEnabled) { + return null; } @Override @@ -235,45 +231,35 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ @Override public ListenableFuture ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTime) { - return getAndUpdateAsync(tenantId, alarmId, new Function<>() { - @Nullable - @Override - public AlarmOperationResult apply(@Nullable Alarm alarm) { - if (alarm == null || alarm.getStatus().isAck()) { - return new AlarmOperationResult(alarm, false); - } else { - AlarmStatus oldStatus = alarm.getStatus(); - AlarmStatus newStatus = oldStatus.isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK; - alarm.setStatus(newStatus); - alarm.setAckTs(ackTime); - alarm = alarmDao.save(alarm.getTenantId(), alarm); - return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); - } - } - }); + Alarm alarm = alarmDao.findAlarmById(tenantId, alarmId.getId()); + if (alarm == null || alarm.getStatus().isAck()) { + return Futures.immediateFuture(new AlarmOperationResult(alarm, false)); + } else { + AlarmStatus oldStatus = alarm.getStatus(); + AlarmStatus newStatus = oldStatus.isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK; + alarm.setStatus(newStatus); + alarm.setAckTs(ackTime); + alarm = alarmDao.save(alarm.getTenantId(), alarm); + return Futures.immediateFuture(new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)))); + } } @Override public ListenableFuture clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTime) { - return getAndUpdateAsync(tenantId, alarmId, new Function() { - @Nullable - @Override - public AlarmOperationResult apply(@Nullable Alarm alarm) { - if (alarm == null || alarm.getStatus().isCleared()) { - return new AlarmOperationResult(alarm, false); - } else { - AlarmStatus oldStatus = alarm.getStatus(); - AlarmStatus newStatus = oldStatus.isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK; - alarm.setStatus(newStatus); - alarm.setClearTs(clearTime); - if (details != null) { - alarm.setDetails(details); - } - alarm = alarmDao.save(alarm.getTenantId(), alarm); - return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); - } + Alarm alarm = alarmDao.findAlarmById(tenantId, alarmId.getId()); + if (alarm == null || alarm.getStatus().isCleared()) { + return Futures.immediateFuture(new AlarmOperationResult(alarm, false)); + } else { + AlarmStatus oldStatus = alarm.getStatus(); + AlarmStatus newStatus = oldStatus.isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK; + alarm.setStatus(newStatus); + alarm.setClearTs(clearTime); + if (details != null) { + alarm.setDetails(details); } - }); + alarm = alarmDao.save(alarm.getTenantId(), alarm); + return Futures.immediateFuture(new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)))); + } } @Override @@ -288,7 +274,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ alarm.setAssigneeId(assigneeId); alarm.setAssignTs(assignTime); alarm = alarmDao.save(alarm.getTenantId(), alarm); - AlarmInfo alarmInfo = getAlarmInfo(tenantId, alarm); + AlarmInfo alarmInfo = alarmDao.findAlarmInfoById(tenantId, alarm.getUuidId()); return new AlarmOperationResult(alarm, new AlarmAssigneeUpdate(false, alarmInfo.getAssignee()), new ArrayList<>(getPropagationEntityIds(alarm))); @@ -331,48 +317,20 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } @Override - public ListenableFuture findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId) { + public AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId) { log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId); validateId(alarmId, "Incorrect alarmId " + alarmId); - return Futures.transform(alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId()), - a -> getAlarmInfo(tenantId, a), MoreExecutors.directExecutor()); + return alarmDao.findAlarmInfoById(tenantId, alarmId.getId()); } @Override public ListenableFuture> findAlarms(TenantId tenantId, AlarmQuery query) { - PageData alarms = alarmDao.findAlarms(tenantId, query); - if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { - return fetchAlarmsOriginators(tenantId, alarms); - } - return Futures.immediateFuture(alarms); + return Futures.immediateFuture(alarmDao.findAlarms(tenantId, query)); } @Override public ListenableFuture> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) { - PageData alarms = alarmDao.findCustomerAlarms(tenantId, customerId, query); - if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) { - return fetchAlarmsOriginators(tenantId, alarms); - } - return Futures.immediateFuture(alarms); - } - - private ListenableFuture> fetchAlarmsOriginators(TenantId tenantId, PageData alarms) { - List> alarmFutures = new ArrayList<>(alarms.getData().size()); - for (AlarmInfo alarmInfo : alarms.getData()) { - Optional detailsOpt = entityService.fetchNameLabelAndCustomerDetails(tenantId, alarmInfo.getOriginator()); - if (detailsOpt.isPresent() && detailsOpt.get().getName() != null) { - NameLabelAndCustomerDetails details = detailsOpt.get(); - alarmInfo.setOriginatorName(details.getName()); - alarmInfo.setOriginatorLabel(details.getLabel()); - } else { - alarmInfo.setOriginatorName("Deleted"); - alarmInfo.setOriginatorLabel("Deleted"); - } - alarmFutures.add(Futures.immediateFuture(alarmInfo)); - } - return Futures.transform(Futures.successfulAsList(alarmFutures), - alarmInfos -> new PageData<>(alarmInfos, alarms.getTotalPages(), alarms.getTotalElements(), - alarms.hasNext()), MoreExecutors.directExecutor()); + return Futures.immediateFuture(alarmDao.findCustomerAlarms(tenantId, customerId, query)); } @Override @@ -451,40 +409,12 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ } } - private ListenableFuture getAndUpdateAsync(TenantId tenantId, AlarmId alarmId, Function function) { - validateId(alarmId, "Alarm id should be specified!"); - ListenableFuture entity = alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId()); - return Futures.transform(entity, function, readResultsProcessingExecutor); - } - private T getAndUpdate(TenantId tenantId, AlarmId alarmId, Function function) { validateId(alarmId, "Alarm id should be specified!"); Alarm entity = alarmDao.findAlarmById(tenantId, alarmId.getId()); return function.apply(entity); } - private AlarmInfo getAlarmInfo(TenantId tenantId, Alarm alarm) { - String originatorName; - String originatorLabel; - - Optional detailsOpt = entityService.fetchNameLabelAndCustomerDetails(tenantId, alarm.getOriginator()); - if (detailsOpt.isPresent() && detailsOpt.get().getName() != null) { - NameLabelAndCustomerDetails details = detailsOpt.get(); - originatorName = details.getName(); - originatorLabel = details.getLabel(); - } else { - originatorName = "Deleted"; - originatorLabel = "Deleted"; - } - - AlarmAssignee assignee = null; - if (alarm.getAssigneeId() != null) { - User assignedUser = userService.findUserById(tenantId, alarm.getAssigneeId()); - assignee = new AlarmAssignee(assignedUser.getId(), assignedUser.getFirstName(), assignedUser.getLastName(), assignedUser.getEmail()); - } - return new AlarmInfo(alarm, originatorName, originatorLabel, assignee); - } - @Override public Optional> findEntity(TenantId tenantId, EntityId entityId) { return Optional.ofNullable(findAlarmById(tenantId, new AlarmId(entityId.getId()))); 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 33df276a46..a4eb8fb3ae 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 @@ -287,6 +287,7 @@ public class ModelConstants { */ public static final String ENTITY_ALARM_COLUMN_FAMILY_NAME = "entity_alarm"; public static final String ALARM_COLUMN_FAMILY_NAME = "alarm"; + public static final String ALARM_VIEW_NAME = "alarm_info"; public static final String ALARM_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String ALARM_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; public static final String ALARM_TYPE_PROPERTY = "type"; 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 0587cdb4ab..43483a0165 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 @@ -21,23 +21,44 @@ import org.thingsboard.server.common.data.alarm.AlarmAssignee; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.id.UserId; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; + +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_EMAIL_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_FIRST_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ASSIGNEE_LAST_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_LABEL_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_NAME_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_VIEW_NAME; + @Data @EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = ALARM_VIEW_NAME) public class AlarmInfoEntity extends AbstractAlarmEntity { + @Column(name = ALARM_ORIGINATOR_NAME_PROPERTY) private String originatorName; + @Column(name = ALARM_ORIGINATOR_LABEL_PROPERTY) private String originatorLabel; - + @Column(name = ALARM_ASSIGNEE_FIRST_NAME_PROPERTY) private String assigneeFirstName; + @Column(name = ALARM_ASSIGNEE_LAST_NAME_PROPERTY) private String assigneeLastName; + @Column(name = ALARM_ASSIGNEE_EMAIL_PROPERTY) private String assigneeEmail; public AlarmInfoEntity() { super(); } - public AlarmInfoEntity(AlarmEntity alarmEntity, String assigneeFirstName, - String assigneeLastName, String assigneeEmail) { + public AlarmInfoEntity(AlarmEntity alarmEntity, + String assigneeFirstName, + String assigneeLastName, + String assigneeEmail) { super(alarmEntity); this.assigneeFirstName = assigneeFirstName; this.assigneeLastName = assigneeLastName; 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 ba61e89a6d..751d876296 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 @@ -39,10 +39,9 @@ public interface AlarmRepository extends JpaRepository { @Param("alarmType") String alarmType, Pageable pageable); - @Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a, tbu.firstName, tbu.lastName, tbu.email) " + - "FROM AlarmEntity a " + + @Query(value = "SELECT a " + + "FROM AlarmInfoEntity a " + "LEFT JOIN EntityAlarmEntity ea ON a.id = ea.alarmId " + - "LEFT JOIN UserEntity tbu ON tbu.id = a.assigneeId " + "WHERE a.tenantId = :tenantId " + "AND ea.tenantId = :tenantId " + "AND ea.entityId = :affectedEntityId " + @@ -57,7 +56,7 @@ public interface AlarmRepository extends JpaRepository { , countQuery = "" + "SELECT count(a) " + //alarms with relations only - "FROM AlarmEntity a " + + "FROM AlarmInfoEntity a " + "LEFT JOIN EntityAlarmEntity ea ON a.id = ea.alarmId " + "WHERE a.tenantId = :tenantId " + "AND ea.tenantId = :tenantId " + @@ -80,9 +79,8 @@ public interface AlarmRepository extends JpaRepository { @Param("searchText") String searchText, Pageable pageable); - @Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a, tbu.firstName, tbu.lastName, tbu.email) " + - "FROM AlarmEntity a " + - "LEFT JOIN UserEntity tbu ON tbu.id = a.assigneeId " + + @Query(value = "SELECT a " + + "FROM AlarmInfoEntity a " + "WHERE a.tenantId = :tenantId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + @@ -110,9 +108,8 @@ public interface AlarmRepository extends JpaRepository { @Param("searchText") String searchText, Pageable pageable); - @Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a, tbu.firstName, tbu.lastName, tbu.email) " + - "FROM AlarmEntity a " + - "LEFT JOIN UserEntity tbu ON tbu.id = a.assigneeId " + + @Query(value = "SELECT a " + + "FROM AlarmInfoEntity a " + "WHERE a.tenantId = :tenantId AND a.customerId = :customerId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + @@ -124,7 +121,7 @@ public interface AlarmRepository extends JpaRepository { , countQuery = "" + "SELECT count(a) " + - "FROM AlarmEntity a " + + "FROM AlarmInfoEntity a " + "WHERE a.tenantId = :tenantId AND a.customerId = :customerId " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + @@ -159,4 +156,6 @@ public interface AlarmRepository extends JpaRepository { @Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.createdTime < :time AND a.endTs < :time") Page findAlarmsIdsByEndTsBeforeAndTenantId(@Param("time") Long time, @Param("tenantId") UUID tenantId, Pageable pageable); + @Query(value = "SELECT a FROM AlarmInfoEntity a WHERE a.tenantId = :tenantId AND a.id = :alarmId") + AlarmInfoEntity findAlarmInfoById(@Param("tenantId") UUID tenantId, @Param("alarmId") UUID 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 9c5bfb6a55..21d4990bd0 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 @@ -100,6 +100,11 @@ public class JpaAlarmDao extends JpaAbstractDao implements A return findById(tenantId, key); } + @Override + public AlarmInfo findAlarmInfoById(TenantId tenantId, UUID key) { + return DaoUtil.getData(alarmRepository.findAlarmInfoById(tenantId.getId(), key)); + } + @Override public ListenableFuture findAlarmByIdAsync(TenantId tenantId, UUID key) { return findByIdAsync(tenantId, key); 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 59c01c5ae1..a56273c785 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 @@ -83,7 +83,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { alarmFieldColumnMap.put(ASSIGNEE_EMAIL_KEY, ModelConstants.ALARM_ASSIGNEE_EMAIL_PROPERTY); } - private static final String SELECT_ORIGINATOR_NAME = " COALESCE(CASE" + + public static final String SELECT_ORIGINATOR_NAME = " COALESCE(CASE" + " WHEN a.originator_type = " + EntityType.TENANT.ordinal() + " THEN (select title from tenant where id = a.originator_id)" + " WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() + @@ -106,7 +106,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { " THEN (select name from edge where id = a.originator_id)" + " END, 'Deleted') as " + ModelConstants.ALARM_ORIGINATOR_NAME_PROPERTY; - private static final String SELECT_ORIGINATOR_LABEL = " COALESCE(CASE" + + public static final String SELECT_ORIGINATOR_LABEL = " COALESCE(CASE" + " WHEN a.originator_type = " + EntityType.TENANT.ordinal() + " THEN (select title from tenant where id = a.originator_id)" + " WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() + diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 3671b4ed34..79edc1e86b 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -28,6 +28,9 @@ CREATE INDEX IF NOT EXISTS idx_alarm_tenant_assignee_created_time ON alarm(tenan CREATE INDEX IF NOT EXISTS idx_entity_alarm_created_time ON entity_alarm(tenant_id, entity_id, created_time DESC); +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); + CREATE INDEX IF NOT EXISTS idx_entity_alarm_alarm_id ON entity_alarm(alarm_id); CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index f592ff6446..0120902e88 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -797,4 +797,33 @@ CREATE TABLE IF NOT EXISTS user_settings ( user_id uuid NOT NULL CONSTRAINT user_settings_pkey PRIMARY KEY, settings varchar(10000), CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE -); \ No newline at end of file +); + +DROP VIEW IF EXISTS alarm_info; +CREATE VIEW alarm_info AS +SELECT a.*, +COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id) + WHEN a.originator_type = 1 THEN (select title from customer where id = a.originator_id) + WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id) + WHEN a.originator_type = 3 THEN (select title from dashboard where id = a.originator_id) + WHEN a.originator_type = 4 THEN (select name from asset where id = a.originator_id) + WHEN a.originator_type = 5 THEN (select name from device where id = a.originator_id) + WHEN a.originator_type = 9 THEN (select name from entity_view where id = a.originator_id) + WHEN a.originator_type = 13 THEN (select name from device_profile where id = a.originator_id) + WHEN a.originator_type = 14 THEN (select name from asset_profile where id = a.originator_id) + WHEN a.originator_type = 18 THEN (select name from edge where id = a.originator_id) END + , 'Deleted') originator_name, +COALESCE(CASE WHEN a.originator_type = 0 THEN (select title from tenant where id = a.originator_id) + WHEN a.originator_type = 1 THEN (select COALESCE(title, email) from customer where id = a.originator_id) + WHEN a.originator_type = 2 THEN (select email from tb_user where id = a.originator_id) + WHEN a.originator_type = 3 THEN (select title from dashboard where id = a.originator_id) + WHEN a.originator_type = 4 THEN (select COALESCE(label, name) from asset where id = a.originator_id) + WHEN a.originator_type = 5 THEN (select COALESCE(label, name) from device where id = a.originator_id) + WHEN a.originator_type = 9 THEN (select name from entity_view where id = a.originator_id) + WHEN a.originator_type = 13 THEN (select name from device_profile where id = a.originator_id) + WHEN a.originator_type = 14 THEN (select name from asset_profile where id = a.originator_id) + WHEN a.originator_type = 18 THEN (select COALESCE(label, name) from edge where id = a.originator_id) END + , 'Deleted') as originator_label, +u.first_name as assignee_first_name, u.last_name as assignee_last_name, u.email as assignee_email +FROM alarm a +LEFT JOIN tb_user u ON u.id = a.assignee_id; \ No newline at end of file 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 cf35729de7..c111a498c3 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 @@ -16,6 +16,7 @@ package org.thingsboard.rule.engine.api; import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; @@ -60,7 +61,11 @@ public interface RuleEngineAlarmService { ListenableFuture findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); - ListenableFuture findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId); + AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId); + + default ListenableFuture findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId){ + return Futures.immediateFuture(findAlarmInfoById(tenantId, alarmId)); + } ListenableFuture> findAlarms(TenantId tenantId, AlarmQuery query);