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.thingsboard.server.actors.service.ActorService;
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.id.*;
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.widget.WidgetType;
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.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
@ -83,6 +86,9 @@ public abstract class BaseController {
@Autowired
protected AssetService assetService;
@Autowired
protected AlarmService alarmService;
@Autowired
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 {
try {
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;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId;
@ -26,9 +27,9 @@ import org.thingsboard.server.common.data.page.TimePageLink;
*/
@Data
@Builder
@AllArgsConstructor
public class AlarmQuery {
private TenantId tenantId;
private EntityId affectedEntityId;
private TimePageLink pageLink;
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);
ListenableFuture<Boolean> updateAlarm(Alarm alarm);
ListenableFuture<Boolean> ackAlarm(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
public Alarm createOrUpdateAlarm(Alarm alarm) {
alarmDataValidator.validate(alarm);
@ -93,53 +92,61 @@ public class BaseAlarmService extends BaseEntityService implements AlarmService
if (alarm.getEndTs() == 0L) {
alarm.setEndTs(alarm.getStartTs());
}
Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get();
if (existing == null || existing.getStatus().isCleared()) {
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()));
if (alarm.getId() == null) {
Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get();
if (existing == null || existing.getStatus().isCleared()) {
return createAlarm(alarm);
} else {
return updateAlarm(existing, alarm);
}
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 {
log.debug("Alarm before merge: {}", alarm);
alarm = merge(existing, alarm);
log.debug("Alarm after merge: {}", alarm);
return getData(alarmDao.save(new AlarmEntity(alarm)));
return updateAlarm(alarm).get();
}
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public ListenableFuture<Boolean> updateAlarm(Alarm update) {
private Alarm createAlarm(Alarm alarm) throws InterruptedException, ExecutionException {
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);
return getAndUpdate(update.getId(), new Function<Alarm, Boolean>() {
return getAndUpdate(update.getId(), new Function<Alarm, Alarm>() {
@Nullable
@Override
public Boolean apply(@Nullable Alarm alarm) {
public Alarm apply(@Nullable Alarm alarm) {
if (alarm == null) {
return false;
return null;
} else {
AlarmStatus oldStatus = alarm.getStatus();
AlarmStatus newStatus = update.getStatus();
alarmDao.save(new AlarmEntity(merge(alarm, update)));
if (oldStatus != newStatus) {
updateRelations(alarm, oldStatus, newStatus);
}
return true;
return updateAlarm(alarm, update);
}
}
});
}
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
public ListenableFuture<Boolean> ackAlarm(AlarmId alarmId, long ackTime) {
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!");
ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId());
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
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)
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
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);
// Check child relation
TimePageData<Alarm> alarms = alarmService.findAlarms(AlarmQuery.builder().tenantId(tenantId)
TimePageData<Alarm> alarms = alarmService.findAlarms(AlarmQuery.builder()
.affectedEntityId(childId)
.status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), true)
new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get();
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0));
// Check parent relation
alarms = alarmService.findAlarms(AlarmQuery.builder().tenantId(tenantId)
alarms = alarmService.findAlarms(AlarmQuery.builder()
.affectedEntityId(parentId)
.status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), true)
new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get();
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
@ -139,20 +139,20 @@ public class AlarmServiceTest extends AbstractServiceTest {
alarmService.ackAlarm(created.getId(), System.currentTimeMillis()).get();
created = alarmService.findAlarmById(created.getId()).get();
alarms = alarmService.findAlarms(AlarmQuery.builder().tenantId(tenantId)
alarms = alarmService.findAlarms(AlarmQuery.builder()
.affectedEntityId(childId)
.status(AlarmStatus.ACTIVE_ACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), true)
new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get();
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0));
// Check not existing relation
alarms = alarmService.findAlarms(AlarmQuery.builder().tenantId(tenantId)
alarms = alarmService.findAlarms(AlarmQuery.builder()
.affectedEntityId(childId)
.status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), true)
new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get();
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(0, alarms.getData().size());
@ -160,10 +160,10 @@ public class AlarmServiceTest extends AbstractServiceTest {
alarmService.clearAlarm(created.getId(), System.currentTimeMillis()).get();
created = alarmService.findAlarmById(created.getId()).get();
alarms = alarmService.findAlarms(AlarmQuery.builder().tenantId(tenantId)
alarms = alarmService.findAlarms(AlarmQuery.builder()
.affectedEntityId(childId)
.status(AlarmStatus.CLEARED_ACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), true)
new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get();
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());

Loading…
Cancel
Save