Browse Source

Alarm REST API

pull/151/head
Andrew Shvayka 9 years ago
parent
commit
10d39d2094
  1. 129
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  2. 22
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  3. 3
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
  4. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  5. 69
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  6. 2
      dao/src/main/resources/schema.cql
  7. 20
      dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java

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

@ -0,0 +1,129 @@
/**
* Copyright © 2016-2017 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.controller;
import com.google.common.util.concurrent.ListenableFuture;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.asset.AssetSearchQuery;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardException;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api")
public class AlarmController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.GET)
@ResponseBody
public Alarm getAlarmById(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException {
checkParameter("alarmId", strAlarmId);
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
return checkAlarmId(alarmId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm", method = RequestMethod.POST)
@ResponseBody
public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException {
try {
alarm.setTenantId(getCurrentUser().getTenantId());
return checkNotNull(alarmService.createOrUpdateAlarm(alarm));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void ackAlarm(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException {
checkParameter("alarmId", strAlarmId);
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
checkAlarmId(alarmId);
alarmService.ackAlarm(alarmId, System.currentTimeMillis()).get();
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void clearAlarm(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException {
checkParameter("alarmId", strAlarmId);
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
checkAlarmId(alarmId);
alarmService.clearAlarm(alarmId, System.currentTimeMillis()).get();
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public TimePageData<Alarm> getAlarms(
@PathVariable("entityType") String strEntityType,
@PathVariable("entityId") String strEntityId,
@RequestParam(required = false) String status,
@RequestParam int limit,
@RequestParam(required = false) Long startTime,
@RequestParam(required = false) Long endTime,
@RequestParam(required = false, defaultValue = "false") boolean ascOrder,
@RequestParam(required = false) String offset
) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status);
checkEntityId(entityId);
try {
TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
return checkNotNull(alarmService.findAlarms(new AlarmQuery(entityId, pageLink, alarmStatus)).get());
} catch (Exception e) {
throw handleException(e);
}
}
}

22
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -25,6 +25,8 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.thingsboard.server.actors.service.ActorService; import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.*; import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.common.data.page.TextPageLink;
@ -36,6 +38,7 @@ import org.thingsboard.server.common.data.rule.RuleMetaData;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.dashboard.DashboardService;
@ -83,6 +86,9 @@ public abstract class BaseController {
@Autowired @Autowired
protected AssetService assetService; protected AssetService assetService;
@Autowired
protected AlarmService alarmService;
@Autowired @Autowired
protected DeviceCredentialsService deviceCredentialsService; protected DeviceCredentialsService deviceCredentialsService;
@ -334,6 +340,22 @@ public abstract class BaseController {
} }
} }
Alarm checkAlarmId(AlarmId alarmId) throws ThingsboardException {
try {
validateId(alarmId, "Incorrect alarmId " + alarmId);
Alarm alarm = alarmService.findAlarmById(alarmId).get();
checkAlarm(alarm);
return alarm;
} catch (Exception e) {
throw handleException(e, false);
}
}
protected void checkAlarm(Alarm alarm) throws ThingsboardException {
checkNotNull(alarm);
checkTenantId(alarm.getTenantId());
}
WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException { WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, boolean modify) throws ThingsboardException {
try { try {
validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId);

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

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.common.data.alarm; package org.thingsboard.server.common.data.alarm;
import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
@ -26,9 +27,9 @@ import org.thingsboard.server.common.data.page.TimePageLink;
*/ */
@Data @Data
@Builder @Builder
@AllArgsConstructor
public class AlarmQuery { public class AlarmQuery {
private TenantId tenantId;
private EntityId affectedEntityId; private EntityId affectedEntityId;
private TimePageLink pageLink; private TimePageLink pageLink;
private AlarmStatus status; private AlarmStatus status;

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

@ -28,8 +28,6 @@ public interface AlarmService {
Alarm createOrUpdateAlarm(Alarm alarm); Alarm createOrUpdateAlarm(Alarm alarm);
ListenableFuture<Boolean> updateAlarm(Alarm alarm);
ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTs); ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTs);
ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, long ackTs); ListenableFuture<Boolean> clearAlarm(AlarmId alarmId, long ackTs);

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

@ -82,7 +82,6 @@ public class BaseAlarmService extends BaseEntityService implements AlarmService
} }
} }
@Override @Override
public Alarm createOrUpdateAlarm(Alarm alarm) { public Alarm createOrUpdateAlarm(Alarm alarm) {
alarmDataValidator.validate(alarm); alarmDataValidator.validate(alarm);
@ -93,53 +92,61 @@ public class BaseAlarmService extends BaseEntityService implements AlarmService
if (alarm.getEndTs() == 0L) { if (alarm.getEndTs() == 0L) {
alarm.setEndTs(alarm.getStartTs()); alarm.setEndTs(alarm.getStartTs());
} }
Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get(); if (alarm.getId() == null) {
if (existing == null || existing.getStatus().isCleared()) { Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get();
log.debug("New Alarm : {}", alarm); if (existing == null || existing.getStatus().isCleared()) {
Alarm saved = getData(alarmDao.save(new AlarmEntity(alarm))); return createAlarm(alarm);
EntityRelationsQuery query = new EntityRelationsQuery(); } else {
query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE)); return updateAlarm(existing, alarm);
List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
for (EntityId parentId : parentEntities) {
createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION));
createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name()));
} }
createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION));
createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name()));
return saved;
} else { } else {
log.debug("Alarm before merge: {}", alarm); return updateAlarm(alarm).get();
alarm = merge(existing, alarm);
log.debug("Alarm after merge: {}", alarm);
return getData(alarmDao.save(new AlarmEntity(alarm)));
} }
} catch (ExecutionException | InterruptedException e) { } catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override private Alarm createAlarm(Alarm alarm) throws InterruptedException, ExecutionException {
public ListenableFuture<Boolean> updateAlarm(Alarm update) { log.debug("New Alarm : {}", alarm);
Alarm saved = getData(alarmDao.save(new AlarmEntity(alarm)));
EntityRelationsQuery query = new EntityRelationsQuery();
query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
for (EntityId parentId : parentEntities) {
createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION));
createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name()));
}
createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION));
createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name()));
return saved;
}
protected ListenableFuture<Alarm> updateAlarm(Alarm update) {
alarmDataValidator.validate(update); alarmDataValidator.validate(update);
return getAndUpdate(update.getId(), new Function<Alarm, Boolean>() { return getAndUpdate(update.getId(), new Function<Alarm, Alarm>() {
@Nullable @Nullable
@Override @Override
public Boolean apply(@Nullable Alarm alarm) { public Alarm apply(@Nullable Alarm alarm) {
if (alarm == null) { if (alarm == null) {
return false; return null;
} else { } else {
AlarmStatus oldStatus = alarm.getStatus(); return updateAlarm(alarm, update);
AlarmStatus newStatus = update.getStatus();
alarmDao.save(new AlarmEntity(merge(alarm, update)));
if (oldStatus != newStatus) {
updateRelations(alarm, oldStatus, newStatus);
}
return true;
} }
} }
}); });
} }
private Alarm updateAlarm(Alarm oldAlarm, Alarm newAlarm) {
AlarmStatus oldStatus = oldAlarm.getStatus();
AlarmStatus newStatus = newAlarm.getStatus();
AlarmEntity result = alarmDao.save(new AlarmEntity(merge(oldAlarm, newAlarm)));
if (oldStatus != newStatus) {
updateRelations(oldAlarm, oldStatus, newStatus);
}
return result.toData();
}
@Override @Override
public ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTime) { public ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTime) {
return getAndUpdate(alarmId, new Function<Alarm, Boolean>() { return getAndUpdate(alarmId, new Function<Alarm, Boolean>() {
@ -247,7 +254,7 @@ public class BaseAlarmService extends BaseEntityService implements AlarmService
} }
} }
private ListenableFuture<Boolean> getAndUpdate(AlarmId alarmId, Function<Alarm, Boolean> function) { private <T> ListenableFuture<T> getAndUpdate(AlarmId alarmId, Function<Alarm, T> function) {
validateId(alarmId, "Alarm id should be specified!"); validateId(alarmId, "Alarm id should be specified!");
ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId()); ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId());
return Futures.transform(entity, function, readResultsProcessingExecutor); return Futures.transform(entity, function, readResultsProcessingExecutor);

2
dao/src/main/resources/schema.cql

@ -276,7 +276,7 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.relation_by_type_and_child_ty
from thingsboard.relation from thingsboard.relation
WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL
PRIMARY KEY ((from_id, from_type), relation_type, to_type, to_id) PRIMARY KEY ((from_id, from_type), relation_type, to_type, to_id)
WITH CLUSTERING ORDER BY ( relation_type ASC, from_type ASC, from_id ASC); WITH CLUSTERING ORDER BY ( relation_type ASC, to_type ASC, to_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.reverse_relation AS CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.reverse_relation AS
SELECT * SELECT *

20
dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java

@ -117,20 +117,20 @@ public class AlarmServiceTest extends AbstractServiceTest {
Alarm created = alarmService.createOrUpdateAlarm(alarm); Alarm created = alarmService.createOrUpdateAlarm(alarm);
// Check child relation // Check child relation
TimePageData<Alarm> alarms = alarmService.findAlarms(AlarmQuery.builder().tenantId(tenantId) TimePageData<Alarm> alarms = alarmService.findAlarms(AlarmQuery.builder()
.affectedEntityId(childId) .affectedEntityId(childId)
.status(AlarmStatus.ACTIVE_UNACK).pageLink( .status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), true) new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get(); ).build()).get();
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0)); Assert.assertEquals(created, alarms.getData().get(0));
// Check parent relation // Check parent relation
alarms = alarmService.findAlarms(AlarmQuery.builder().tenantId(tenantId) alarms = alarmService.findAlarms(AlarmQuery.builder()
.affectedEntityId(parentId) .affectedEntityId(parentId)
.status(AlarmStatus.ACTIVE_UNACK).pageLink( .status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), true) new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get(); ).build()).get();
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(1, alarms.getData().size());
@ -139,20 +139,20 @@ public class AlarmServiceTest extends AbstractServiceTest {
alarmService.ackAlarm(created.getId(), System.currentTimeMillis()).get(); alarmService.ackAlarm(created.getId(), System.currentTimeMillis()).get();
created = alarmService.findAlarmById(created.getId()).get(); created = alarmService.findAlarmById(created.getId()).get();
alarms = alarmService.findAlarms(AlarmQuery.builder().tenantId(tenantId) alarms = alarmService.findAlarms(AlarmQuery.builder()
.affectedEntityId(childId) .affectedEntityId(childId)
.status(AlarmStatus.ACTIVE_ACK).pageLink( .status(AlarmStatus.ACTIVE_ACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), true) new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get(); ).build()).get();
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0)); Assert.assertEquals(created, alarms.getData().get(0));
// Check not existing relation // Check not existing relation
alarms = alarmService.findAlarms(AlarmQuery.builder().tenantId(tenantId) alarms = alarmService.findAlarms(AlarmQuery.builder()
.affectedEntityId(childId) .affectedEntityId(childId)
.status(AlarmStatus.ACTIVE_UNACK).pageLink( .status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), true) new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get(); ).build()).get();
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
Assert.assertEquals(0, alarms.getData().size()); Assert.assertEquals(0, alarms.getData().size());
@ -160,10 +160,10 @@ public class AlarmServiceTest extends AbstractServiceTest {
alarmService.clearAlarm(created.getId(), System.currentTimeMillis()).get(); alarmService.clearAlarm(created.getId(), System.currentTimeMillis()).get();
created = alarmService.findAlarmById(created.getId()).get(); created = alarmService.findAlarmById(created.getId()).get();
alarms = alarmService.findAlarms(AlarmQuery.builder().tenantId(tenantId) alarms = alarmService.findAlarms(AlarmQuery.builder()
.affectedEntityId(childId) .affectedEntityId(childId)
.status(AlarmStatus.CLEARED_ACK).pageLink( .status(AlarmStatus.CLEARED_ACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), true) new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get(); ).build()).get();
Assert.assertNotNull(alarms.getData()); Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(1, alarms.getData().size());

Loading…
Cancel
Save