Browse Source

added alarm_types table

pull/8825/head
YevhenBondarenko 3 years ago
parent
commit
1c17b00513
  1. 7
      application/src/main/data/upgrade/3.5.1/schema_update.sql
  2. 13
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  3. 6
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
  4. 9
      application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java
  5. 81
      application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java
  6. 4
      application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java
  7. 9
      common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  8. 3
      common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java
  9. 3
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
  10. 5
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java
  11. 5
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java
  12. 43
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  13. 12
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java
  14. 17
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  15. 6
      dao/src/main/resources/sql/schema-entities.sql
  16. 1
      dao/src/main/resources/sql/schema-views-and-functions.sql
  17. 4
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java

7
application/src/main/data/upgrade/3.5.1/schema_update.sql

@ -122,3 +122,10 @@ ALTER TABLE resource
UPDATE resource UPDATE resource
SET etag = encode(sha256(decode(resource.data, 'base64')),'hex') WHERE resource.data is not null; SET etag = encode(sha256(decode(resource.data, 'base64')),'hex') WHERE resource.data is not null;
CREATE TABLE IF NOT EXISTS alarm_types (
tenant_id uuid NOT NULL,
type varchar(255) NOT NULL,
CONSTRAINT tenant_id_type_unq_key UNIQUE (tenant_id, type)
);
INSERT INTO alarm_types (tenant_id, type) SELECT DISTINCT tenant_id, type FROM alarm ON CONFLICT (tenant_id, type) DO NOTHING;

13
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -45,6 +45,7 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.alarm.TbAlarmService; import org.thingsboard.server.service.entitiy.alarm.TbAlarmService;
@ -504,8 +505,16 @@ public class AlarmController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/types", method = RequestMethod.GET) @RequestMapping(value = "/alarm/types", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public List<EntitySubtype> getAlarmTypes() throws ThingsboardException, ExecutionException, InterruptedException { public PageData<EntitySubtype> getAlarmTypes(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
return checkNotNull(alarmService.findAlarmTypesByTenantId(getTenantId())); @RequestParam int pageSize,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
@RequestParam(required = false) String sortOrder) throws ThingsboardException, ExecutionException, InterruptedException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, "type", sortOrder);
return checkNotNull(alarmService.findAlarmTypesByTenantId(getTenantId(), pageLink));
} }
} }

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

@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmData;
import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.common.data.query.AlarmDataQuery;
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
@ -59,7 +60,6 @@ import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils; import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
import java.util.Collection; import java.util.Collection;
import java.util.List;
/** /**
* Created by ashvayka on 27.03.18. * Created by ashvayka on 27.03.18.
@ -238,8 +238,8 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
} }
@Override @Override
public List<EntitySubtype> findAlarmTypesByTenantId(TenantId tenantId) { public PageData<EntitySubtype> findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink) {
return alarmService.findAlarmTypesByTenantId(tenantId); return alarmService.findAlarmTypesByTenantId(tenantId, pageLink);
} }
private void onAlarmUpdated(AlarmApiCallResult result) { private void onAlarmUpdated(AlarmApiCallResult result) {

9
application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java

@ -39,7 +39,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.action.EntityActionService;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@TbCoreComponent @TbCoreComponent
@ -86,20 +88,25 @@ public class AlarmsCleanUpService {
PageLink removalBatchRequest = new PageLink(removalBatchSize, 0); PageLink removalBatchRequest = new PageLink(removalBatchSize, 0);
long totalRemoved = 0; long totalRemoved = 0;
Set<String> typesToRemove = new HashSet<>();
while (true) { while (true) {
PageData<AlarmId> toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(expirationTime, tenantId, removalBatchRequest); PageData<AlarmId> toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(expirationTime, tenantId, removalBatchRequest);
for (AlarmId alarmId : toRemove.getData()) { for (AlarmId alarmId : toRemove.getData()) {
relationService.deleteEntityRelations(tenantId, alarmId); relationService.deleteEntityRelations(tenantId, alarmId);
Alarm alarm = alarmService.delAlarm(tenantId, alarmId).getAlarm(); Alarm alarm = alarmService.delAlarm(tenantId, alarmId, false).getAlarm();
if (alarm != null) { if (alarm != null) {
entityActionService.pushEntityActionToRuleEngine(alarm.getOriginator(), alarm, tenantId, null, ActionType.ALARM_DELETE, null); entityActionService.pushEntityActionToRuleEngine(alarm.getOriginator(), alarm, tenantId, null, ActionType.ALARM_DELETE, null);
totalRemoved++; totalRemoved++;
typesToRemove.add(alarm.getType());
} }
} }
if (!toRemove.hasNext()) { if (!toRemove.hasNext()) {
break; break;
} }
} }
alarmService.delAlarmTypes(tenantId, typesToRemove);
if (totalRemoved > 0) { if (totalRemoved > 0) {
log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime));
} }

81
application/src/test/java/org/thingsboard/server/controller/AlarmControllerTest.java

@ -47,7 +47,6 @@ import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -747,15 +746,15 @@ public class AlarmControllerTest extends AbstractControllerTest {
public void testGetAlarmTypes() throws Exception { public void testGetAlarmTypes() throws Exception {
loginTenantAdmin(); loginTenantAdmin();
Mockito.reset(tbClusterService, auditLogService);
List<String> types = new ArrayList<>(); List<String> types = new ArrayList<>();
for (int i = 1; i < 13; i++) { for (int i = 1; i < 13; i++) {
types.add(createAlarm(TEST_ALARM_TYPE + i).getType()); types.add(createAlarm(TEST_ALARM_TYPE + i).getType());
} }
List<String> foundTypes = doGetTyped("/api/alarm/types", new TypeReference<List<EntitySubtype>>() {}) List<String> foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference<PageData<EntitySubtype>>() {
})
.getData()
.stream() .stream()
.map(EntitySubtype::getType) .map(EntitySubtype::getType)
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -766,4 +765,78 @@ public class AlarmControllerTest extends AbstractControllerTest {
Assert.assertEquals(types, foundTypes); Assert.assertEquals(types, foundTypes);
} }
@Test
public void testDeleteAlarmTypes() throws Exception {
loginTenantAdmin();
List<AlarmInfo> alarms = new ArrayList<>();
for (int i = 1; i < 13; i++) {
alarms.add(createAlarm(TEST_ALARM_TYPE + i));
}
Device device = new Device();
device.setName("Test device 2");
device.setCustomerId(customerId);
customerDevice = doPost("/api/device", device, Device.class);
for (int i = 1; i < 14; i++) {
alarms.add(createAlarm(TEST_ALARM_TYPE + i));
}
List<String> expectedTypes = alarms.stream().map(AlarmInfo::getType).distinct().sorted().collect(Collectors.toList());
List<String> foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference<PageData<EntitySubtype>>() {
})
.getData()
.stream()
.map(EntitySubtype::getType)
.sorted()
.collect(Collectors.toList());
Assert.assertEquals(13, foundTypes.size());
Assert.assertEquals(expectedTypes, foundTypes);
for (int i = 0; i < 12; i++) {
doDelete("/api/alarm/" + alarms.get(i).getId()).andExpect(status().isOk());
}
foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference<PageData<EntitySubtype>>() {
})
.getData()
.stream()
.map(EntitySubtype::getType)
.sorted()
.collect(Collectors.toList());
Assert.assertEquals(13, foundTypes.size());
Assert.assertEquals(expectedTypes, foundTypes);
doDelete("/api/alarm/" + alarms.get(12).getId()).andExpect(status().isOk());
foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference<PageData<EntitySubtype>>() {
})
.getData()
.stream()
.map(EntitySubtype::getType)
.sorted()
.collect(Collectors.toList());
Assert.assertEquals(12, foundTypes.size());
for (int i = 13; i < alarms.size(); i++) {
doDelete("/api/alarm/" + alarms.get(i).getId()).andExpect(status().isOk());
}
foundTypes = doGetTyped("/api/alarm/types?pageSize=1024&page=0", new TypeReference<PageData<EntitySubtype>>() {
})
.getData()
.stream()
.map(EntitySubtype::getType)
.sorted()
.collect(Collectors.toList());
Assert.assertTrue(foundTypes.isEmpty());
}
} }

4
application/src/test/java/org/thingsboard/server/service/ttl/AlarmsCleanUpServiceTest.java

@ -104,10 +104,10 @@ public class AlarmsCleanUpServiceTest extends AbstractControllerTest {
alarmsCleanUpService.cleanUp(); alarmsCleanUpService.cleanUp();
for (AlarmId outdatedAlarm : outdatedAlarms) { for (AlarmId outdatedAlarm : outdatedAlarms) {
verify(alarmService).delAlarm(eq(tenantId), eq(outdatedAlarm)); verify(alarmService).delAlarm(eq(tenantId), eq(outdatedAlarm), eq(false));
} }
for (AlarmId freshAlarm : freshAlarms) { for (AlarmId freshAlarm : freshAlarms) {
verify(alarmService, never()).delAlarm(eq(tenantId), eq(freshAlarm)); verify(alarmService, never()).delAlarm(eq(tenantId), eq(freshAlarm), eq(false));
} }
verify(cleanUpServiceLogger).info(startsWith("Removed {} outdated alarm"), eq((long) count), eq(tenantId), any()); verify(cleanUpServiceLogger).info(startsWith("Removed {} outdated alarm"), eq((long) count), eq(tenantId), any());

9
common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java

@ -34,13 +34,14 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.query.AlarmCountQuery; import org.thingsboard.server.common.data.query.AlarmCountQuery;
import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmData;
import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.common.data.query.AlarmDataQuery;
import org.thingsboard.server.dao.entity.EntityDaoService; import org.thingsboard.server.dao.entity.EntityDaoService;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.Set;
public interface AlarmService extends EntityDaoService { public interface AlarmService extends EntityDaoService {
@ -76,6 +77,10 @@ public interface AlarmService extends EntityDaoService {
AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId); AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId);
AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId, boolean deleteAlarmType);
void delAlarmTypes(TenantId tenantId, Set<String> types);
/* /*
* Legacy API, before 3.5. * Legacy API, before 3.5.
*/ */
@ -124,5 +129,5 @@ public interface AlarmService extends EntityDaoService {
long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query);
List<EntitySubtype> findAlarmTypesByTenantId(TenantId tenantId); PageData<EntitySubtype> findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink);
} }

3
common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java

@ -20,13 +20,14 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ApiModel @ApiModel
public class PageData<T> { public class PageData<T> implements Serializable {
private final List<T> data; private final List<T> data;
private final int totalPages; private final int totalPages;

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

@ -98,6 +98,7 @@ public interface AlarmDao extends Dao<Alarm> {
long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query); long countAlarmsByQuery(TenantId tenantId, CustomerId customerId, AlarmCountQuery query);
List<EntitySubtype> findTenantAlarmTypes(UUID tenantId); PageData<EntitySubtype> findTenantAlarmTypes(UUID tenantId, PageLink pageLink);
boolean removeAlarmTypes(UUID tenantId, Set<String> types);
} }

5
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesCaffeineCache.java

@ -22,12 +22,11 @@ import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import java.util.ArrayList;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("AlarmTypesCache") @Service("AlarmTypesCache")
public class AlarmTypesCaffeineCache extends CaffeineTbTransactionalCache<TenantId, ArrayList<EntitySubtype>> { public class AlarmTypesCaffeineCache extends CaffeineTbTransactionalCache<TenantId, PageData<EntitySubtype>> {
public AlarmTypesCaffeineCache(CacheManager cacheManager) { public AlarmTypesCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.ALARM_TYPES_CACHE); super(cacheManager, CacheConstants.ALARM_TYPES_CACHE);

5
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmTypesRedisCache.java

@ -25,12 +25,11 @@ import org.thingsboard.server.cache.TbFSTRedisSerializer;
import org.thingsboard.server.common.data.CacheConstants; import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import java.util.ArrayList;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("AlarmTypesCache") @Service("AlarmTypesCache")
public class AlarmTypesRedisCache extends RedisTbTransactionalCache<TenantId, ArrayList<EntitySubtype>> { public class AlarmTypesRedisCache extends RedisTbTransactionalCache<TenantId, PageData<EntitySubtype>> {
public AlarmTypesRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) { public AlarmTypesRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.ALARM_TYPES_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); super(CacheConstants.ALARM_TYPES_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>());

43
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -22,12 +22,10 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
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.transaction.event.TransactionalEventListener; import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.thingsboard.server.cache.TbTransactionalCache;
import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
@ -51,6 +49,8 @@ import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.query.AlarmCountQuery; import org.thingsboard.server.common.data.query.AlarmCountQuery;
import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmData;
import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.common.data.query.AlarmDataQuery;
@ -68,7 +68,6 @@ import org.thingsboard.server.dao.tenant.TenantService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -83,24 +82,22 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
@Service("AlarmDaoService") @Service("AlarmDaoService")
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class BaseAlarmService extends AbstractCachedEntityService<TenantId, ArrayList<EntitySubtype>, AlarmTypesCacheEvictEvent> implements AlarmService { public class BaseAlarmService extends AbstractCachedEntityService<TenantId, PageData<EntitySubtype>, AlarmTypesCacheEvictEvent> implements AlarmService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
private static final PageLink DEFAULT_ALARM_TYPES_PAGE_LINK = new PageLink(25, 0, null, new SortOrder("type"));
private final TenantService tenantService; private final TenantService tenantService;
private final AlarmDao alarmDao; private final AlarmDao alarmDao;
private final EntityService entityService; private final EntityService entityService;
private final DataValidator<Alarm> alarmDataValidator; private final DataValidator<Alarm> alarmDataValidator;
@Autowired
protected TbTransactionalCache<TenantId, ArrayList<EntitySubtype>> alarmTypesCache;
@TransactionalEventListener(classes = AlarmTypesCacheEvictEvent.class) @TransactionalEventListener(classes = AlarmTypesCacheEvictEvent.class)
@Override @Override
public void handleEvictEvent(AlarmTypesCacheEvictEvent event) { public void handleEvictEvent(AlarmTypesCacheEvictEvent event) {
TenantId tenantId = event.getTenantId(); TenantId tenantId = event.getTenantId();
cache.evict(tenantId); cache.evict(tenantId);
alarmTypesCache.evict(tenantId);
} }
@Override @Override
@ -198,6 +195,12 @@ public class BaseAlarmService extends AbstractCachedEntityService<TenantId, Arra
@Override @Override
@Transactional @Transactional
public AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId) { public AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId) {
return delAlarm(tenantId, alarmId, true);
}
@Override
@Transactional
public AlarmApiCallResult delAlarm(TenantId tenantId, AlarmId alarmId, boolean deleteAlarmType) {
log.debug("Deleting Alarm Id: {}", alarmId); log.debug("Deleting Alarm Id: {}", alarmId);
AlarmInfo alarm = alarmDao.findAlarmInfoById(tenantId, alarmId.getId()); AlarmInfo alarm = alarmDao.findAlarmInfoById(tenantId, alarmId.getId());
if (alarm == null) { if (alarm == null) {
@ -205,11 +208,20 @@ public class BaseAlarmService extends AbstractCachedEntityService<TenantId, Arra
} else { } else {
deleteEntityRelations(tenantId, alarm.getId()); deleteEntityRelations(tenantId, alarm.getId());
alarmDao.removeById(tenantId, alarm.getUuidId()); alarmDao.removeById(tenantId, alarm.getUuidId());
publishEvictEvent(new AlarmTypesCacheEvictEvent(tenantId)); if (deleteAlarmType) {
delAlarmTypes(tenantId, Collections.singleton(alarm.getType()));
}
return AlarmApiCallResult.builder().alarm(alarm).deleted(true).successful(true).build(); return AlarmApiCallResult.builder().alarm(alarm).deleted(true).successful(true).build();
} }
} }
@Override
public void delAlarmTypes(TenantId tenantId, Set<String> types) {
if (!types.isEmpty() && alarmDao.removeAlarmTypes(tenantId.getId(), types)) {
publishEvictEvent(new AlarmTypesCacheEvictEvent(tenantId));
}
}
@Override @Override
@Transactional @Transactional
public AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId) { public AlarmOperationResult deleteAlarm(TenantId tenantId, AlarmId alarmId) {
@ -221,14 +233,12 @@ public class BaseAlarmService extends AbstractCachedEntityService<TenantId, Arra
AlarmOperationResult result = new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm))); AlarmOperationResult result = new AlarmOperationResult(alarm, true, new ArrayList<>(getPropagationEntityIds(alarm)));
deleteEntityRelations(tenantId, alarm.getId()); deleteEntityRelations(tenantId, alarm.getId());
alarmDao.removeById(tenantId, alarm.getUuidId()); alarmDao.removeById(tenantId, alarm.getUuidId());
publishEvictEvent(new AlarmTypesCacheEvictEvent(tenantId));
return result; return result;
} }
private AlarmOperationResult createAlarm(Alarm alarm) throws InterruptedException, ExecutionException { private AlarmOperationResult createAlarm(Alarm alarm) throws InterruptedException, ExecutionException {
log.debug("New Alarm : {}", alarm); log.debug("New Alarm : {}", alarm);
Alarm saved = alarmDao.save(alarm.getTenantId(), alarm); Alarm saved = alarmDao.save(alarm.getTenantId(), alarm);
publishEvictEvent(new AlarmTypesCacheEvictEvent(alarm.getTenantId()));
List<EntityId> propagatedEntitiesList = createEntityAlarmRecords(saved); List<EntityId> propagatedEntitiesList = createEntityAlarmRecords(saved);
return new AlarmOperationResult(saved, true, true, propagatedEntitiesList); return new AlarmOperationResult(saved, true, true, propagatedEntitiesList);
} }
@ -397,13 +407,14 @@ public class BaseAlarmService extends AbstractCachedEntityService<TenantId, Arra
} }
@Override @Override
public List<EntitySubtype> findAlarmTypesByTenantId(TenantId tenantId) { public PageData<EntitySubtype> findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findAlarmTypesByTenantId, tenantId [{}]", tenantId); log.trace("Executing findAlarmTypesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
return cache.getAndPutInTransaction(tenantId, () -> if (DEFAULT_ALARM_TYPES_PAGE_LINK.equals(pageLink)) {
alarmDao.findTenantAlarmTypes(tenantId.getId()).stream() return cache.getAndPutInTransaction(tenantId, () ->
.sorted(Comparator.comparing(EntitySubtype::getType)) alarmDao.findTenantAlarmTypes(tenantId.getId(), pageLink), false);
.collect(Collectors.toCollection(ArrayList::new)), false); }
return alarmDao.findTenantAlarmTypes(tenantId.getId(), pageLink);
} }
private Alarm merge(Alarm existing, Alarm alarm) { private Alarm merge(Alarm existing, Alarm alarm) {

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

@ -18,8 +18,10 @@ package org.thingsboard.server.dao.sql.alarm;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.dao.model.sql.AlarmEntity; import org.thingsboard.server.dao.model.sql.AlarmEntity;
import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; import org.thingsboard.server.dao.model.sql.AlarmInfoEntity;
@ -346,6 +348,12 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
@Query(value = "SELECT unassign_alarm(:t_id, :a_id, :a_ts)", nativeQuery = true) @Query(value = "SELECT unassign_alarm(:t_id, :a_id, :a_ts)", nativeQuery = true)
String unassignAlarm(@Param("t_id") UUID tenantId, @Param("a_id") UUID alarmId, @Param("a_ts") long unassignTime); String unassignAlarm(@Param("t_id") UUID tenantId, @Param("a_id") UUID alarmId, @Param("a_ts") long unassignTime);
@Query(value = "SELECT DISTINCT a.type FROM alarm a WHERE a.tenant_id = :tenantId LIMIT 256", nativeQuery = true) @Query(value = "SELECT at.type FROM alarm_types AS at WHERE at.tenant_id = :tenantId AND LOWER(at.type) LIKE LOWER(CONCAT('%', :searchText, '%'))", nativeQuery = true)
List<String> findTenantAlarmTypes(@Param("tenantId") UUID tenantId); Page<String> findTenantAlarmTypes(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable);
@Transactional
@Modifying
@Query(value = "DELETE FROM alarm_types AS at WHERE NOT EXISTS (SELECT 1 FROM alarm AS a WHERE a.tenant_id = at.tenant_id AND a.type = at.type) AND at.tenant_id = :tenantId AND at.type IN (:types)", nativeQuery = true)
int deleteTypeIfNoOneAlarmExists(@Param("tenantId") UUID tenantId, @Param("types") Set<String> types);
} }

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

@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -69,6 +70,7 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityTypesToDto; import static org.thingsboard.server.dao.DaoUtil.convertTenantEntityTypesToDto;
import static org.thingsboard.server.dao.DaoUtil.toPageable;
/** /**
* Created by Valerii Sosliuk on 5/19/2017. * Created by Valerii Sosliuk on 5/19/2017.
@ -370,8 +372,19 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
} }
@Override @Override
public List<EntitySubtype> findTenantAlarmTypes(UUID tenantId) { public PageData<EntitySubtype> findTenantAlarmTypes(UUID tenantId, PageLink pageLink) {
return convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, alarmRepository.findTenantAlarmTypes(tenantId)); Page<String> page = alarmRepository.findTenantAlarmTypes(tenantId, Objects.toString(pageLink.getTextSearch(), ""), toPageable(pageLink));
if (page.isEmpty()) {
return new PageData<>();
}
List<EntitySubtype> data = convertTenantEntityTypesToDto(tenantId, EntityType.ALARM, page.getContent());
return new PageData<>(data, page.getTotalPages(), page.getTotalElements(), page.hasNext());
}
@Override
public boolean removeAlarmTypes(UUID tenantId, Set<String> types) {
return alarmRepository.deleteTypeIfNoOneAlarmExists(tenantId, types) > 0;
} }
private static String getPropagationTypes(AlarmPropagationInfo ap) { private static String getPropagationTypes(AlarmPropagationInfo ap) {

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

@ -64,6 +64,12 @@ CREATE TABLE IF NOT EXISTS alarm (
cleared boolean cleared boolean
); );
CREATE TABLE IF NOT EXISTS alarm_types (
tenant_id uuid NOT NULL,
type varchar(255) NOT NULL,
CONSTRAINT tenant_id_type_unq_key UNIQUE (tenant_id, type)
);
CREATE TABLE IF NOT EXISTS alarm_comment ( CREATE TABLE IF NOT EXISTS alarm_comment (
id uuid NOT NULL, id uuid NOT NULL,
created_time bigint NOT NULL, created_time bigint NOT NULL,

1
dao/src/main/resources/sql/schema-views-and-functions.sql

@ -114,6 +114,7 @@ BEGIN
a_details, a_details,
a_propagate, a_propagate_to_owner, a_propagate_to_tenant, a_propagation_types, a_propagate, a_propagate_to_owner, a_propagate_to_tenant, a_propagation_types,
false, 0, false, 0, NULL, 0); false, 0, false, 0, NULL, 0);
INSERT INTO alarm_types (tenant_id, type) VALUES (t_id, a_type) ON CONFLICT (tenant_id, type) DO NOTHING;
SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id; SELECT * INTO result FROM alarm_info a WHERE a.id = a_id AND a.tenant_id = t_id;
RETURN json_build_object('success', true, 'created', true, 'modified', true, 'alarm', row_to_json(result))::text; RETURN json_build_object('success', true, 'created', true, 'modified', true, 'alarm', row_to_json(result))::text;
ELSE ELSE

4
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAlarmService.java

@ -35,12 +35,12 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmData;
import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.common.data.query.AlarmDataQuery;
import org.thingsboard.server.dao.alarm.AlarmOperationResult; import org.thingsboard.server.dao.alarm.AlarmOperationResult;
import java.util.Collection; import java.util.Collection;
import java.util.List;
/** /**
* Created by ashvayka on 02.04.18. * Created by ashvayka on 02.04.18.
@ -114,5 +114,5 @@ public interface RuleEngineAlarmService {
PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds); PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, AlarmDataQuery query, Collection<EntityId> orderedEntityIds);
List<EntitySubtype> findAlarmTypesByTenantId(TenantId tenantId); PageData<EntitySubtype> findAlarmTypesByTenantId(TenantId tenantId, PageLink pageLink);
} }

Loading…
Cancel
Save