thingsboard-memoizeit

Alarm REST API

5/24/2017 7:58:49 AM

Details

diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
new file mode 100644
index 0000000..7d29d57
--- /dev/null
+++ b/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);
+        }
+    }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index d4adebe..ec93d29 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/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;
@@ -84,6 +87,9 @@ public abstract class BaseController {
     protected AssetService assetService;
 
     @Autowired
+    protected AlarmService alarmService;
+
+    @Autowired
     protected DeviceCredentialsService deviceCredentialsService;
 
     @Autowired
@@ -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);
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
index 9c4f921..00ca6c3 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
@@ -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;
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
index 3d35723..3c4da18 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
+++ b/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);
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
index 2675fa2..a5bf27c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
@@ -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);
diff --git a/dao/src/main/resources/schema.cql b/dao/src/main/resources/schema.cql
index 44afed9..e0ef1e2 100644
--- a/dao/src/main/resources/schema.cql
+++ b/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 *
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java
index 4264cbd..4bf43f9 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java
+++ b/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());