thingsboard-memoizeit

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
index 7d29d57..7a2c273 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
@@ -72,7 +72,7 @@ public class AlarmController extends BaseController {
         }
     }
 
-    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+    @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST)
     @ResponseStatus(value = HttpStatus.OK)
     public void ackAlarm(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException {
@@ -86,7 +86,7 @@ public class AlarmController extends BaseController {
         }
     }
 
-    @PreAuthorize("hasAuthority('TENANT_ADMIN')")
+    @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST)
     @ResponseStatus(value = HttpStatus.OK)
     public void clearAlarm(@PathVariable("alarmId") String strAlarmId) throws ThingsboardException {
diff --git a/extensions/extension-kafka/src/main/java/org/thingsboard/server/extensions/kafka/plugin/KafkaMsgHandler.java b/extensions/extension-kafka/src/main/java/org/thingsboard/server/extensions/kafka/plugin/KafkaMsgHandler.java
index bde0a6f..87f890a 100644
--- a/extensions/extension-kafka/src/main/java/org/thingsboard/server/extensions/kafka/plugin/KafkaMsgHandler.java
+++ b/extensions/extension-kafka/src/main/java/org/thingsboard/server/extensions/kafka/plugin/KafkaMsgHandler.java
@@ -16,6 +16,7 @@
 package org.thingsboard.server.extensions.kafka.plugin;
 
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.kafka.clients.producer.Producer;
 import org.apache.kafka.clients.producer.ProducerRecord;
 import org.thingsboard.server.common.data.id.RuleId;
@@ -30,6 +31,7 @@ import org.thingsboard.server.extensions.kafka.action.KafkaActionMsg;
 import org.thingsboard.server.extensions.kafka.action.KafkaActionPayload;
 
 @RequiredArgsConstructor
+@Slf4j
 public class KafkaMsgHandler implements RuleMsgHandler {
 
     private final Producer<?, String> producer;
@@ -40,7 +42,7 @@ public class KafkaMsgHandler implements RuleMsgHandler {
             throw new RuleException("Unsupported message type " + msg.getClass().getName() + "!");
         }
         KafkaActionPayload payload = ((KafkaActionMsg) msg).getPayload();
-
+        log.debug("Processing kafka payload: {}", payload);
         try {
             producer.send(new ProducerRecord<>(payload.getTopic(), payload.getMsgBody()),
                     (metadata, e) -> {
diff --git a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/device/DeviceAttributes.java b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/device/DeviceAttributes.java
index 8628d0c..2cfeefb 100644
--- a/extensions-api/src/main/java/org/thingsboard/server/extensions/api/device/DeviceAttributes.java
+++ b/extensions-api/src/main/java/org/thingsboard/server/extensions/api/device/DeviceAttributes.java
@@ -91,4 +91,13 @@ public class DeviceAttributes {
         }
         return map;
     }
+
+    @Override
+    public String toString() {
+        return "DeviceAttributes{" +
+                "clientSideAttributesMap=" + clientSideAttributesMap +
+                ", serverPrivateAttributesMap=" + serverPrivateAttributesMap +
+                ", serverPublicAttributesMap=" + serverPublicAttributesMap +
+                '}';
+    }
 }
diff --git a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/template/AbstractTemplatePluginAction.java b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/template/AbstractTemplatePluginAction.java
index 9360af1..12958f5 100644
--- a/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/template/AbstractTemplatePluginAction.java
+++ b/extensions-core/src/main/java/org/thingsboard/server/extensions/core/action/template/AbstractTemplatePluginAction.java
@@ -15,6 +15,7 @@
  */
 package org.thingsboard.server.extensions.core.action.template;
 
+import lombok.extern.slf4j.Slf4j;
 import org.apache.velocity.Template;
 import org.apache.velocity.VelocityContext;
 import org.apache.velocity.runtime.parser.ParseException;
@@ -35,6 +36,7 @@ import java.util.Optional;
 /**
  * @author Andrew Shvayka
  */
+@Slf4j
 public abstract class AbstractTemplatePluginAction<T extends TemplateActionConfiguration> extends SimpleRuleLifecycleComponent implements PluginAction<T> {
     protected T configuration;
     protected Template template;
@@ -69,6 +71,7 @@ public abstract class AbstractTemplatePluginAction<T extends TemplateActionConfi
     }
 
     protected String getMsgBody(RuleContext ctx, ToDeviceActorMsg msg) {
+        log.trace("Creating context for: {} and payload {}", ctx.getDeviceAttributes(), msg.getPayload());
         VelocityContext context = VelocityUtils.createContext(ctx.getDeviceAttributes(), msg.getPayload());
         return VelocityUtils.merge(template, context);
     }
diff --git a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
index a78319a..d6a953a 100644
--- a/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
+++ b/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionCtx.java
@@ -78,6 +78,7 @@ public class GatewaySessionCtx {
                 Device newDevice = new Device();
                 newDevice.setTenantId(gateway.getTenantId());
                 newDevice.setName(deviceName);
+                newDevice.setType("default");
                 return deviceService.saveDevice(newDevice);
             });
             GatewayDeviceSessionCtx ctx = new GatewayDeviceSessionCtx(this, device);
diff --git a/ui/src/app/api/alarm.service.js b/ui/src/app/api/alarm.service.js
new file mode 100644
index 0000000..d9ab4ff
--- /dev/null
+++ b/ui/src/app/api/alarm.service.js
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+export default angular.module('thingsboard.api.alarm', [])
+    .factory('alarmService', AlarmService)
+    .name;
+
+/*@ngInject*/
+function AlarmService($http, $q, $interval, $filter) {
+    var service = {
+        getAlarm: getAlarm,
+        saveAlarm: saveAlarm,
+        ackAlarm: ackAlarm,
+        clearAlarm: clearAlarm,
+        getAlarms: getAlarms,
+        pollAlarms: pollAlarms,
+        cancelPollAlarms: cancelPollAlarms
+    }
+
+    return service;
+
+    function getAlarm(alarmId, ignoreErrors, config) {
+        var deferred = $q.defer();
+        var url = '/api/alarm/' + alarmId;
+        if (!config) {
+            config = {};
+        }
+        config = Object.assign(config, { ignoreErrors: ignoreErrors });
+        $http.get(url, config).then(function success(response) {
+            deferred.resolve(response.data);
+        }, function fail() {
+            deferred.reject();
+        });
+        return deferred.promise;
+    }
+
+    function saveAlarm(alarm, ignoreErrors, config) {
+        var deferred = $q.defer();
+        var url = '/api/alarm';
+        if (!config) {
+            config = {};
+        }
+        config = Object.assign(config, { ignoreErrors: ignoreErrors });
+        $http.post(url, alarm, config).then(function success(response) {
+            deferred.resolve(response.data);
+        }, function fail() {
+            deferred.reject();
+        });
+        return deferred.promise;
+    }
+
+    function ackAlarm(alarmId, ignoreErrors, config) {
+        var deferred = $q.defer();
+        var url = '/api/alarm/' + alarmId + '/ack';
+        if (!config) {
+            config = {};
+        }
+        config = Object.assign(config, { ignoreErrors: ignoreErrors });
+        $http.post(url, null, config).then(function success(response) {
+            deferred.resolve(response.data);
+        }, function fail() {
+            deferred.reject();
+        });
+        return deferred.promise;
+    }
+
+    function clearAlarm(alarmId, ignoreErrors, config) {
+        var deferred = $q.defer();
+        var url = '/api/alarm/' + alarmId + '/clear';
+        if (!config) {
+            config = {};
+        }
+        config = Object.assign(config, { ignoreErrors: ignoreErrors });
+        $http.post(url, null, config).then(function success(response) {
+            deferred.resolve(response.data);
+        }, function fail() {
+            deferred.reject();
+        });
+        return deferred.promise;
+    }
+
+    function getAlarms(entityType, entityId, pageLink, alarmStatus, ascOrder, config) {
+        var deferred = $q.defer();
+        var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit;
+
+        if (angular.isDefined(pageLink.startTime)) {
+            url += '&startTime=' + pageLink.startTime;
+        }
+        if (angular.isDefined(pageLink.endTime)) {
+            url += '&endTime=' + pageLink.endTime;
+        }
+        if (angular.isDefined(pageLink.idOffset)) {
+            url += '&offset=' + pageLink.idOffset;
+        }
+        if (alarmStatus) {
+            url += '&status=' + alarmStatus;
+        }
+        if (angular.isDefined(ascOrder) && ascOrder != null) {
+            url += '&ascOrder=' + (ascOrder ? 'true' : 'false');
+        }
+
+        $http.get(url, config).then(function success(response) {
+            deferred.resolve(response.data);
+        }, function fail() {
+            deferred.reject();
+        });
+        return deferred.promise;
+    }
+
+    function fetchAlarms(alarmsQuery, pageLink, deferred, alarmsList) {
+        getAlarms(alarmsQuery.entityType, alarmsQuery.entityId,
+            pageLink, alarmsQuery.alarmStatus, false, {ignoreLoading: true}).then(
+            function success(alarms) {
+                if (!alarmsList) {
+                    alarmsList = [];
+                }
+                alarmsList = alarmsList.concat(alarms.data);
+                if (alarms.hasNext && !alarmsQuery.limit) {
+                    fetchAlarms(alarmsQuery, alarms.nextPageLink, deferred, alarmsList);
+                } else {
+                    alarmsList = $filter('orderBy')(alarmsList, ['-createdTime']);
+                    deferred.resolve(alarmsList);
+                }
+            },
+            function fail() {
+                deferred.reject();
+            }
+        );
+    }
+
+    function getAlarmsByQuery(alarmsQuery) {
+        var deferred = $q.defer();
+        var time = Date.now();
+        var pageLink;
+        if (alarmsQuery.limit) {
+            pageLink = {
+                limit: alarmsQuery.limit
+            };
+        } else {
+            pageLink = {
+                limit: 100,
+                startTime: time - alarmsQuery.interval
+            };
+        }
+        fetchAlarms(alarmsQuery, pageLink, deferred);
+        return deferred.promise;
+    }
+
+    function onPollAlarms(alarmsQuery) {
+        getAlarmsByQuery(alarmsQuery).then(
+            function success(alarms) {
+                alarmsQuery.onAlarms(alarms);
+            },
+            function fail() {}
+        );
+    }
+
+    function pollAlarms(entityType, entityId, alarmStatus, interval, limit, pollingInterval, onAlarms) {
+        var alarmsQuery = {
+            entityType: entityType,
+            entityId: entityId,
+            alarmStatus: alarmStatus,
+            interval: interval,
+            limit: limit,
+            onAlarms: onAlarms
+        };
+        onPollAlarms(alarmsQuery);
+        return $interval(onPollAlarms, pollingInterval, 0, false, alarmsQuery);
+    }
+
+    function cancelPollAlarms(pollPromise) {
+        if (angular.isDefined(pollPromise)) {
+            $interval.cancel(pollPromise);
+        }
+    }
+
+}
diff --git a/ui/src/app/api/datasource.service.js b/ui/src/app/api/datasource.service.js
index 27da468..bfa3184 100644
--- a/ui/src/app/api/datasource.service.js
+++ b/ui/src/app/api/datasource.service.js
@@ -595,8 +595,8 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic
                             }
                             series = [time, value];
                             data.push(series);
-                            update = true;
                         }
+                        update = true;
                     }
                     if (update) {
                         datasourceData[datasourceKey].data = data;
diff --git a/ui/src/app/app.js b/ui/src/app/app.js
index f67be33..9ba34b4 100644
--- a/ui/src/app/app.js
+++ b/ui/src/app/app.js
@@ -67,6 +67,7 @@ import thingsboardApiEntityRelation from './api/entity-relation.service';
 import thingsboardApiAsset from './api/asset.service';
 import thingsboardApiAttribute from './api/attribute.service';
 import thingsboardApiEntity from './api/entity.service';
+import thingsboardApiAlarm from './api/alarm.service';
 
 import 'typeface-roboto';
 import 'font-awesome/css/font-awesome.min.css';
@@ -124,6 +125,7 @@ angular.module('thingsboard', [
     thingsboardApiAsset,
     thingsboardApiAttribute,
     thingsboardApiEntity,
+    thingsboardApiAlarm,
     uiRouter])
     .config(AppConfig)
     .factory('globalInterceptor', GlobalInterceptor)
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index 47ac7a0..44e59a2 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -59,6 +59,12 @@ export default angular.module('thingsboard.types', [])
                     name: "aggregation.none"
                 }
             },
+            alarmStatus: {
+                activeUnack: "ACTIVE_UNACK",
+                activeAck: "ACTIVE_ACK",
+                clearedUnack: "CLEARED_UNACK",
+                clearedAck: "CLEARED_ACK"
+            },
             position: {
                 top: {
                     value: "top",
diff --git a/ui/src/app/dashboard/dashboard.controller.js b/ui/src/app/dashboard/dashboard.controller.js
index ecf072d..ac14f79 100644
--- a/ui/src/app/dashboard/dashboard.controller.js
+++ b/ui/src/app/dashboard/dashboard.controller.js
@@ -138,10 +138,10 @@ export default function DashboardController(types, dashboardUtils, widgetService
     }
 
     vm.mainLayoutHeight = function() {
-        if (vm.isEditingWidget && vm.editingLayoutCtx.id === 'main') {
+        if (!vm.isEditingWidget || vm.editingLayoutCtx.id === 'main') {
             return '100%';
         } else {
-            return 'auto';
+            return '0px';
         }
     }
 
@@ -154,10 +154,10 @@ export default function DashboardController(types, dashboardUtils, widgetService
     }
 
     vm.rightLayoutHeight = function() {
-        if (vm.isEditingWidget && vm.editingLayoutCtx.id === 'right') {
+        if (!vm.isEditingWidget || vm.editingLayoutCtx.id === 'right') {
             return '100%';
         } else {
-            return 'auto';
+            return '0px';
         }
     }
 
@@ -369,7 +369,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
         }
     }
 
-    function openDashboardState(state) {
+    function openDashboardState(state, openRightLayout) {
         var layoutsData = dashboardUtils.getStateLayoutsData(vm.dashboard, state);
         if (layoutsData) {
             vm.dashboardCtx.state = state;
@@ -387,7 +387,7 @@ export default function DashboardController(types, dashboardUtils, widgetService
                     layoutVisibilityChanged = !vm.isMobile;
                 }
             }
-            vm.isRightLayoutOpened = false;
+            vm.isRightLayoutOpened = openRightLayout ? true : false;
             updateLayouts(layoutVisibilityChanged);
         }
 
diff --git a/ui/src/app/dashboard/states/default-state-controller.js b/ui/src/app/dashboard/states/default-state-controller.js
index 782f59e..76ea9b8 100644
--- a/ui/src/app/dashboard/states/default-state-controller.js
+++ b/ui/src/app/dashboard/states/default-state-controller.js
@@ -26,12 +26,13 @@ export default function DefaultStateController($scope, $location, $state, $state
     vm.navigatePrevState = navigatePrevState;
     vm.getStateId = getStateId;
     vm.getStateParams = getStateParams;
+    vm.getStateParamsByStateId = getStateParamsByStateId;
 
     vm.getStateName = getStateName;
 
     vm.displayStateSelection = displayStateSelection;
 
-    function openState(id, params) {
+    function openState(id, params, openRightLayout) {
         if (vm.states && vm.states[id]) {
             if (!params) {
                 params = {};
@@ -42,11 +43,11 @@ export default function DefaultStateController($scope, $location, $state, $state
             }
             //append new state
             vm.stateObject[0] = newState;
-            gotoState(vm.stateObject[0].id, true);
+            gotoState(vm.stateObject[0].id, true, openRightLayout);
         }
     }
 
-    function updateState(id, params) {
+    function updateState(id, params, openRightLayout) {
         if (vm.states && vm.states[id]) {
             if (!params) {
                 params = {};
@@ -57,7 +58,7 @@ export default function DefaultStateController($scope, $location, $state, $state
             }
             //replace with new state
             vm.stateObject[0] = newState;
-            gotoState(vm.stateObject[0].id, true);
+            gotoState(vm.stateObject[0].id, true, openRightLayout);
         }
     }
 
@@ -76,6 +77,24 @@ export default function DefaultStateController($scope, $location, $state, $state
         return vm.stateObject[vm.stateObject.length-1].params;
     }
 
+    function getStateParamsByStateId(stateId) {
+        var stateObj = getStateObjById(stateId);
+        if (stateObj) {
+            return stateObj.params;
+        } else {
+            return null;
+        }
+    }
+
+    function getStateObjById(id) {
+        for (var i=0; i < vm.stateObject.length; i++) {
+            if (vm.stateObject[i].id === id) {
+                return vm.stateObject[i];
+            }
+        }
+        return null;
+    }
+
     function getStateName(id, state) {
         var result = '';
         var translationId = types.translate.dashboardStatePrefix + id;
@@ -161,9 +180,9 @@ export default function DefaultStateController($scope, $location, $state, $state
         }, true);
     }
 
-    function gotoState(stateId, update) {
+    function gotoState(stateId, update, openRightLayout) {
         if (vm.dashboardCtrl.dashboardCtx.state != stateId) {
-            vm.dashboardCtrl.openDashboardState(stateId);
+            vm.dashboardCtrl.openDashboardState(stateId, openRightLayout);
             if (update) {
                 updateLocation();
             }
diff --git a/ui/src/app/dashboard/states/entity-state-controller.js b/ui/src/app/dashboard/states/entity-state-controller.js
index 8ee9285..51cf67d 100644
--- a/ui/src/app/dashboard/states/entity-state-controller.js
+++ b/ui/src/app/dashboard/states/entity-state-controller.js
@@ -28,12 +28,13 @@ export default function EntityStateController($scope, $location, $state, $stateP
     vm.navigatePrevState = navigatePrevState;
     vm.getStateId = getStateId;
     vm.getStateParams = getStateParams;
+    vm.getStateParamsByStateId = getStateParamsByStateId;
 
     vm.getStateName = getStateName;
 
     vm.selectedStateIndex = -1;
 
-    function openState(id, params) {
+    function openState(id, params, openRightLayout) {
         if (vm.states && vm.states[id]) {
             resolveEntity(params).then(
                 function success(entityName) {
@@ -45,13 +46,13 @@ export default function EntityStateController($scope, $location, $state, $stateP
                     //append new state
                     vm.stateObject.push(newState);
                     vm.selectedStateIndex = vm.stateObject.length-1;
-                    gotoState(vm.stateObject[vm.stateObject.length-1].id, true);
+                    gotoState(vm.stateObject[vm.stateObject.length-1].id, true, openRightLayout);
                 }
             );
         }
     }
 
-    function updateState(id, params) {
+    function updateState(id, params, openRightLayout) {
         if (vm.states && vm.states[id]) {
             resolveEntity(params).then(
                 function success(entityName) {
@@ -62,7 +63,7 @@ export default function EntityStateController($scope, $location, $state, $stateP
                     }
                     //replace with new state
                     vm.stateObject[vm.stateObject.length - 1] = newState;
-                    gotoState(vm.stateObject[vm.stateObject.length - 1].id, true);
+                    gotoState(vm.stateObject[vm.stateObject.length - 1].id, true, openRightLayout);
                 }
             );
         }
@@ -84,6 +85,24 @@ export default function EntityStateController($scope, $location, $state, $stateP
         return vm.stateObject[vm.stateObject.length-1].params;
     }
 
+    function getStateParamsByStateId(stateId) {
+        var stateObj = getStateObjById(stateId);
+        if (stateObj) {
+            return stateObj.params;
+        } else {
+            return null;
+        }
+    }
+
+    function getStateObjById(id) {
+        for (var i=0; i < vm.stateObject.length; i++) {
+            if (vm.stateObject[i].id === id) {
+                return vm.stateObject[i];
+            }
+        }
+        return null;
+    }
+
     function getStateName(index) {
         var result = '';
         if (vm.stateObject[index]) {
@@ -223,9 +242,9 @@ export default function EntityStateController($scope, $location, $state, $stateP
         });
     }
 
-    function gotoState(stateId, update) {
+    function gotoState(stateId, update, openRightLayout) {
         if (vm.dashboardCtrl.dashboardCtx.state != stateId) {
-            vm.dashboardCtrl.openDashboardState(stateId);
+            vm.dashboardCtrl.openDashboardState(stateId, openRightLayout);
             if (update) {
                 updateLocation();
             }
diff --git a/ui/src/app/dashboard/states/states-component.directive.js b/ui/src/app/dashboard/states/states-component.directive.js
index fb5e77c..538c3e9 100644
--- a/ui/src/app/dashboard/states/states-component.directive.js
+++ b/ui/src/app/dashboard/states/states-component.directive.js
@@ -29,15 +29,15 @@ export default function StatesComponent($compile, $templateCache, $controller, s
 
             var stateController = scope.dashboardCtrl.dashboardCtx.stateController;
 
-            stateController.openState = function(id, params) {
+            stateController.openState = function(id, params, openRightLayout) {
                 if (scope.statesController) {
-                    scope.statesController.openState(id, params);
+                    scope.statesController.openState(id, params, openRightLayout);
                 }
             }
 
-            stateController.updateState = function(id, params) {
+            stateController.updateState = function(id, params, openRightLayout) {
                 if (scope.statesController) {
-                    scope.statesController.updateState(id, params);
+                    scope.statesController.updateState(id, params, openRightLayout);
                 }
             }
 
@@ -62,6 +62,14 @@ export default function StatesComponent($compile, $templateCache, $controller, s
                     return {};
                 }
             }
+
+            stateController.getStateParamsByStateId = function(id) {
+                if (scope.statesController) {
+                    return scope.statesController.getStateParamsByStateId(id);
+                } else {
+                    return null;
+                }
+            }
         }
 
         scope.$on('$destroy', function callOnDestroyHook() {