Browse Source

Alarm query updates

pull/8102/head
Andrii Shvaika 4 years ago
parent
commit
c16dfd4407
  1. 2
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  2. 4
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
  3. 35
      common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  4. 37
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmPropagationInfo.java
  5. 1
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
  6. 45
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmUpdateRequest.java
  7. 50
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/CreateOrUpdateActiveAlarmRequest.java
  8. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
  9. 154
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  10. 1
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  11. 27
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmInfoEntity.java
  12. 21
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java
  13. 5
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  14. 4
      dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java
  15. 3
      dao/src/main/resources/sql/schema-entities-idx.sql
  16. 31
      dao/src/main/resources/sql/schema-entities.sql
  17. 7
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java

2
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 { AlarmInfo checkAlarmInfoId(AlarmId alarmId, Operation operation) throws ThingsboardException {
try { try {
validateId(alarmId, "Incorrect alarmId " + alarmId); 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"); checkNotNull(alarmInfo, "Alarm with id [" + alarmId + "] is not found");
accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, operation, alarmId, alarmInfo); accessControlService.checkPermission(getCurrentUser(), Resource.ALARM, operation, alarmId, alarmInfo);
return alarmInfo; return alarmInfo;

4
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java

@ -174,8 +174,8 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
} }
@Override @Override
public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId) { public AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId) {
return alarmService.findAlarmInfoByIdAsync(tenantId, alarmId); return alarmService.findAlarmInfoById(tenantId, alarmId);
} }
@Override @Override

35
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.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
import org.thingsboard.server.common.data.alarm.CreateOrUpdateActiveAlarmRequest;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -35,30 +37,51 @@ import org.thingsboard.server.dao.entity.EntityDaoService;
import java.util.Collection; import java.util.Collection;
/**
* Created by ashvayka on 11.05.17.
*/
public interface AlarmService extends EntityDaoService { 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);
AlarmOperationResult createOrUpdateAlarm(Alarm alarm, boolean alarmCreationEnabled); AlarmOperationResult createOrUpdateAlarm(Alarm alarm, boolean alarmCreationEnabled);
// Other API
AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId); AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId);
ListenableFuture<AlarmOperationResult> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs); ListenableFuture<AlarmOperationResult> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs);
ListenableFuture<AlarmOperationResult> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTs); ListenableFuture<AlarmOperationResult> 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); Alarm findAlarmById(TenantId tenantId, AlarmId alarmId);
ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId); ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, AlarmId alarmId);
ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId); AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId);
ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query); ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query);

37
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<String> propagateRelationTypes;
}

1
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java

@ -35,6 +35,7 @@ public class AlarmQuery {
private AlarmSearchStatus searchStatus; private AlarmSearchStatus searchStatus;
private AlarmStatus status; private AlarmStatus status;
private UserId assigneeId; private UserId assigneeId;
@Deprecated
private Boolean fetchOriginator; private Boolean fetchOriginator;
} }

45
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;
}

50
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;
}

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

@ -51,6 +51,8 @@ public interface AlarmDao extends Dao<Alarm> {
ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key); ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key);
AlarmInfo findAlarmInfoById(TenantId tenantId, UUID key);
Alarm save(TenantId tenantId, Alarm alarm); Alarm save(TenantId tenantId, Alarm alarm);
PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query); PageData<AlarmInfo> findAlarms(TenantId tenantId, AlarmQuery query);

154
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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmAssignee; import org.thingsboard.server.common.data.alarm.AlarmAssignee;
import org.thingsboard.server.common.data.alarm.AlarmAssigneeUpdate; import org.thingsboard.server.common.data.alarm.AlarmAssigneeUpdate;
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.alarm.CreateOrUpdateActiveAlarmRequest;
import org.thingsboard.server.common.data.alarm.EntityAlarm; import org.thingsboard.server.common.data.alarm.EntityAlarm;
import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException; import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException;
import org.thingsboard.server.common.data.id.AlarmId; 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 org.thingsboard.server.dao.user.UserService;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -69,8 +68,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -90,24 +87,23 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
@Autowired @Autowired
private EntityService entityService; private EntityService entityService;
@Autowired
private UserService userService;
@Autowired @Autowired
private DataValidator<Alarm> alarmDataValidator; private DataValidator<Alarm> alarmDataValidator;
protected ExecutorService readResultsProcessingExecutor;
@PostConstruct @Override
public void startExecutor() { public AlarmOperationResult updateAlarm(AlarmUpdateRequest request) {
readResultsProcessingExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("alarm-service")); return null;
} }
@PreDestroy @Override
public void stopExecutor() { public AlarmOperationResult createAlarm(CreateOrUpdateActiveAlarmRequest request) {
if (readResultsProcessingExecutor != null) { return null;
readResultsProcessingExecutor.shutdownNow(); }
}
@Override
public AlarmOperationResult createAlarm(CreateOrUpdateActiveAlarmRequest request, boolean alarmCreationEnabled) {
return null;
} }
@Override @Override
@ -235,45 +231,35 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
@Override @Override
public ListenableFuture<AlarmOperationResult> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTime) { public ListenableFuture<AlarmOperationResult> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTime) {
return getAndUpdateAsync(tenantId, alarmId, new Function<>() { Alarm alarm = alarmDao.findAlarmById(tenantId, alarmId.getId());
@Nullable if (alarm == null || alarm.getStatus().isAck()) {
@Override return Futures.immediateFuture(new AlarmOperationResult(alarm, false));
public AlarmOperationResult apply(@Nullable Alarm alarm) { } else {
if (alarm == null || alarm.getStatus().isAck()) { AlarmStatus oldStatus = alarm.getStatus();
return new AlarmOperationResult(alarm, false); AlarmStatus newStatus = oldStatus.isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK;
} else { alarm.setStatus(newStatus);
AlarmStatus oldStatus = alarm.getStatus(); alarm.setAckTs(ackTime);
AlarmStatus newStatus = oldStatus.isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK; alarm = alarmDao.save(alarm.getTenantId(), alarm);
alarm.setStatus(newStatus); return Futures.immediateFuture(new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))));
alarm.setAckTs(ackTime); }
alarm = alarmDao.save(alarm.getTenantId(), alarm);
return new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)));
}
}
});
} }
@Override @Override
public ListenableFuture<AlarmOperationResult> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTime) { public ListenableFuture<AlarmOperationResult> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long clearTime) {
return getAndUpdateAsync(tenantId, alarmId, new Function<Alarm, AlarmOperationResult>() { Alarm alarm = alarmDao.findAlarmById(tenantId, alarmId.getId());
@Nullable if (alarm == null || alarm.getStatus().isCleared()) {
@Override return Futures.immediateFuture(new AlarmOperationResult(alarm, false));
public AlarmOperationResult apply(@Nullable Alarm alarm) { } else {
if (alarm == null || alarm.getStatus().isCleared()) { AlarmStatus oldStatus = alarm.getStatus();
return new AlarmOperationResult(alarm, false); AlarmStatus newStatus = oldStatus.isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK;
} else { alarm.setStatus(newStatus);
AlarmStatus oldStatus = alarm.getStatus(); alarm.setClearTs(clearTime);
AlarmStatus newStatus = oldStatus.isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK; if (details != null) {
alarm.setStatus(newStatus); alarm.setDetails(details);
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 = alarmDao.save(alarm.getTenantId(), alarm);
return Futures.immediateFuture(new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))));
}
} }
@Override @Override
@ -288,7 +274,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
alarm.setAssigneeId(assigneeId); alarm.setAssigneeId(assigneeId);
alarm.setAssignTs(assignTime); alarm.setAssignTs(assignTime);
alarm = alarmDao.save(alarm.getTenantId(), alarm); alarm = alarmDao.save(alarm.getTenantId(), alarm);
AlarmInfo alarmInfo = getAlarmInfo(tenantId, alarm); AlarmInfo alarmInfo = alarmDao.findAlarmInfoById(tenantId, alarm.getUuidId());
return new AlarmOperationResult(alarm, return new AlarmOperationResult(alarm,
new AlarmAssigneeUpdate(false, alarmInfo.getAssignee()), new AlarmAssigneeUpdate(false, alarmInfo.getAssignee()),
new ArrayList<>(getPropagationEntityIds(alarm))); new ArrayList<>(getPropagationEntityIds(alarm)));
@ -331,48 +317,20 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} }
@Override @Override
public ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId) { public AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId) {
log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId); log.trace("Executing findAlarmInfoByIdAsync [{}]", alarmId);
validateId(alarmId, "Incorrect alarmId " + alarmId); validateId(alarmId, "Incorrect alarmId " + alarmId);
return Futures.transform(alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId()), return alarmDao.findAlarmInfoById(tenantId, alarmId.getId());
a -> getAlarmInfo(tenantId, a), MoreExecutors.directExecutor());
} }
@Override @Override
public ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) { public ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query) {
PageData<AlarmInfo> alarms = alarmDao.findAlarms(tenantId, query); return Futures.immediateFuture(alarmDao.findAlarms(tenantId, query));
if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
return fetchAlarmsOriginators(tenantId, alarms);
}
return Futures.immediateFuture(alarms);
} }
@Override @Override
public ListenableFuture<PageData<AlarmInfo>> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) { public ListenableFuture<PageData<AlarmInfo>> findCustomerAlarms(TenantId tenantId, CustomerId customerId, AlarmQuery query) {
PageData<AlarmInfo> alarms = alarmDao.findCustomerAlarms(tenantId, customerId, query); return Futures.immediateFuture(alarmDao.findCustomerAlarms(tenantId, customerId, query));
if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
return fetchAlarmsOriginators(tenantId, alarms);
}
return Futures.immediateFuture(alarms);
}
private ListenableFuture<PageData<AlarmInfo>> fetchAlarmsOriginators(TenantId tenantId, PageData<AlarmInfo> alarms) {
List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(alarms.getData().size());
for (AlarmInfo alarmInfo : alarms.getData()) {
Optional<NameLabelAndCustomerDetails> 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());
} }
@Override @Override
@ -451,40 +409,12 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} }
} }
private <T> ListenableFuture<T> getAndUpdateAsync(TenantId tenantId, AlarmId alarmId, Function<Alarm, T> function) {
validateId(alarmId, "Alarm id should be specified!");
ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId());
return Futures.transform(entity, function, readResultsProcessingExecutor);
}
private <T> T getAndUpdate(TenantId tenantId, AlarmId alarmId, Function<Alarm, T> function) { private <T> T getAndUpdate(TenantId tenantId, AlarmId alarmId, Function<Alarm, T> function) {
validateId(alarmId, "Alarm id should be specified!"); validateId(alarmId, "Alarm id should be specified!");
Alarm entity = alarmDao.findAlarmById(tenantId, alarmId.getId()); Alarm entity = alarmDao.findAlarmById(tenantId, alarmId.getId());
return function.apply(entity); return function.apply(entity);
} }
private AlarmInfo getAlarmInfo(TenantId tenantId, Alarm alarm) {
String originatorName;
String originatorLabel;
Optional<NameLabelAndCustomerDetails> 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 @Override
public Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) { public Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) {
return Optional.ofNullable(findAlarmById(tenantId, new AlarmId(entityId.getId()))); return Optional.ofNullable(findAlarmById(tenantId, new AlarmId(entityId.getId())));

1
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 ENTITY_ALARM_COLUMN_FAMILY_NAME = "entity_alarm";
public static final String ALARM_COLUMN_FAMILY_NAME = "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_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String ALARM_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; public static final String ALARM_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY;
public static final String ALARM_TYPE_PROPERTY = "type"; public static final String ALARM_TYPE_PROPERTY = "type";

27
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.alarm.AlarmInfo;
import org.thingsboard.server.common.data.id.UserId; 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 @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = ALARM_VIEW_NAME)
public class AlarmInfoEntity extends AbstractAlarmEntity<AlarmInfo> { public class AlarmInfoEntity extends AbstractAlarmEntity<AlarmInfo> {
@Column(name = ALARM_ORIGINATOR_NAME_PROPERTY)
private String originatorName; private String originatorName;
@Column(name = ALARM_ORIGINATOR_LABEL_PROPERTY)
private String originatorLabel; private String originatorLabel;
@Column(name = ALARM_ASSIGNEE_FIRST_NAME_PROPERTY)
private String assigneeFirstName; private String assigneeFirstName;
@Column(name = ALARM_ASSIGNEE_LAST_NAME_PROPERTY)
private String assigneeLastName; private String assigneeLastName;
@Column(name = ALARM_ASSIGNEE_EMAIL_PROPERTY)
private String assigneeEmail; private String assigneeEmail;
public AlarmInfoEntity() { public AlarmInfoEntity() {
super(); super();
} }
public AlarmInfoEntity(AlarmEntity alarmEntity, String assigneeFirstName, public AlarmInfoEntity(AlarmEntity alarmEntity,
String assigneeLastName, String assigneeEmail) { String assigneeFirstName,
String assigneeLastName,
String assigneeEmail) {
super(alarmEntity); super(alarmEntity);
this.assigneeFirstName = assigneeFirstName; this.assigneeFirstName = assigneeFirstName;
this.assigneeLastName = assigneeLastName; this.assigneeLastName = assigneeLastName;

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

@ -39,10 +39,9 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
@Param("alarmType") String alarmType, @Param("alarmType") String alarmType,
Pageable pageable); Pageable pageable);
@Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a, tbu.firstName, tbu.lastName, tbu.email) " + @Query(value = "SELECT a " +
"FROM AlarmEntity a " + "FROM AlarmInfoEntity a " +
"LEFT JOIN EntityAlarmEntity ea ON a.id = ea.alarmId " + "LEFT JOIN EntityAlarmEntity ea ON a.id = ea.alarmId " +
"LEFT JOIN UserEntity tbu ON tbu.id = a.assigneeId " +
"WHERE a.tenantId = :tenantId " + "WHERE a.tenantId = :tenantId " +
"AND ea.tenantId = :tenantId " + "AND ea.tenantId = :tenantId " +
"AND ea.entityId = :affectedEntityId " + "AND ea.entityId = :affectedEntityId " +
@ -57,7 +56,7 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
, ,
countQuery = "" + countQuery = "" +
"SELECT count(a) " + //alarms with relations only "SELECT count(a) " + //alarms with relations only
"FROM AlarmEntity a " + "FROM AlarmInfoEntity a " +
"LEFT JOIN EntityAlarmEntity ea ON a.id = ea.alarmId " + "LEFT JOIN EntityAlarmEntity ea ON a.id = ea.alarmId " +
"WHERE a.tenantId = :tenantId " + "WHERE a.tenantId = :tenantId " +
"AND ea.tenantId = :tenantId " + "AND ea.tenantId = :tenantId " +
@ -80,9 +79,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
@Param("searchText") String searchText, @Param("searchText") String searchText,
Pageable pageable); Pageable pageable);
@Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a, tbu.firstName, tbu.lastName, tbu.email) " + @Query(value = "SELECT a " +
"FROM AlarmEntity a " + "FROM AlarmInfoEntity a " +
"LEFT JOIN UserEntity tbu ON tbu.id = a.assigneeId " +
"WHERE a.tenantId = :tenantId " + "WHERE a.tenantId = :tenantId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
@ -110,9 +108,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
@Param("searchText") String searchText, @Param("searchText") String searchText,
Pageable pageable); Pageable pageable);
@Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a, tbu.firstName, tbu.lastName, tbu.email) " + @Query(value = "SELECT a " +
"FROM AlarmEntity a " + "FROM AlarmInfoEntity a " +
"LEFT JOIN UserEntity tbu ON tbu.id = a.assigneeId " +
"WHERE a.tenantId = :tenantId AND a.customerId = :customerId " + "WHERE a.tenantId = :tenantId AND a.customerId = :customerId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
@ -124,7 +121,7 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
, ,
countQuery = "" + countQuery = "" +
"SELECT count(a) " + "SELECT count(a) " +
"FROM AlarmEntity a " + "FROM AlarmInfoEntity a " +
"WHERE a.tenantId = :tenantId AND a.customerId = :customerId " + "WHERE a.tenantId = :tenantId AND a.customerId = :customerId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
@ -159,4 +156,6 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
@Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.createdTime < :time AND a.endTs < :time") @Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.createdTime < :time AND a.endTs < :time")
Page<UUID> findAlarmsIdsByEndTsBeforeAndTenantId(@Param("time") Long time, @Param("tenantId") UUID tenantId, Pageable pageable); Page<UUID> 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);
} }

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

@ -100,6 +100,11 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
return findById(tenantId, key); return findById(tenantId, key);
} }
@Override
public AlarmInfo findAlarmInfoById(TenantId tenantId, UUID key) {
return DaoUtil.getData(alarmRepository.findAlarmInfoById(tenantId.getId(), key));
}
@Override @Override
public ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key) { public ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key) {
return findByIdAsync(tenantId, key); return findByIdAsync(tenantId, key);

4
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); 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() + " WHEN a.originator_type = " + EntityType.TENANT.ordinal() +
" THEN (select title from tenant where id = a.originator_id)" + " THEN (select title from tenant where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() + " 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)" + " THEN (select name from edge where id = a.originator_id)" +
" END, 'Deleted') as " + ModelConstants.ALARM_ORIGINATOR_NAME_PROPERTY; " 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() + " WHEN a.originator_type = " + EntityType.TENANT.ordinal() +
" THEN (select title from tenant where id = a.originator_id)" + " THEN (select title from tenant where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() + " WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() +

3
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_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_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); CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id);

31
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, user_id uuid NOT NULL CONSTRAINT user_settings_pkey PRIMARY KEY,
settings varchar(10000), settings varchar(10000),
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE
); );
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;

7
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; package org.thingsboard.rule.engine.api;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
@ -60,7 +61,11 @@ public interface RuleEngineAlarmService {
ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);
ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId); AlarmInfo findAlarmInfoById(TenantId tenantId, AlarmId alarmId);
default ListenableFuture<AlarmInfo> findAlarmInfoByIdAsync(TenantId tenantId, AlarmId alarmId){
return Futures.immediateFuture(findAlarmInfoById(tenantId, alarmId));
}
ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query); ListenableFuture<PageData<AlarmInfo>> findAlarms(TenantId tenantId, AlarmQuery query);

Loading…
Cancel
Save