Commit 2db354424c69b1863de4c0d5459aeea2d4e4b9ab

Authored by Igor Kulikov
2 parents 95a46352 6535330a

Merge with master

Showing 100 changed files with 1070 additions and 310 deletions

Too many changes to show.

To preserve performance only 100 of 138 files are displayed.

@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <artifactId>application</artifactId> 26 <artifactId>application</artifactId>
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 # 15 #
16 16
17 export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@/data" 17 export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@ -Dinstall.data_dir=@pkg.installFolder@/data"
18 -export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps" 18 +export JAVA_OPTS="$JAVA_OPTS -Xloggc:@pkg.logFolder@/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps"
19 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" 19 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
20 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" 20 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
21 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" 21 export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled"
@@ -111,6 +111,7 @@ public class AppActor extends RuleChainManagerActor { @@ -111,6 +111,7 @@ public class AppActor extends RuleChainManagerActor {
111 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG: 111 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
112 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG: 112 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
113 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG: 113 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  114 + case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
114 onToDeviceActorMsg((TenantAwareMsg) msg); 115 onToDeviceActorMsg((TenantAwareMsg) msg);
115 break; 116 break;
116 case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG: 117 case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
@@ -29,11 +29,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress; @@ -29,11 +29,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress;
29 import org.thingsboard.server.gen.cluster.ClusterAPIProtos; 29 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
30 import org.thingsboard.server.service.cluster.discovery.ServerInstance; 30 import org.thingsboard.server.service.cluster.discovery.ServerInstance;
31 31
32 -import java.util.HashMap;  
33 -import java.util.LinkedList;  
34 -import java.util.Map;  
35 -import java.util.Queue;  
36 -import java.util.UUID; 32 +import java.util.*;
37 33
38 /** 34 /**
39 * @author Andrew Shvayka 35 * @author Andrew Shvayka
@@ -88,7 +84,17 @@ public class RpcManagerActor extends ContextAwareActor { @@ -88,7 +84,17 @@ public class RpcManagerActor extends ContextAwareActor {
88 84
89 private void onMsg(RpcBroadcastMsg msg) { 85 private void onMsg(RpcBroadcastMsg msg) {
90 log.debug("Forwarding msg to session actors {}", msg); 86 log.debug("Forwarding msg to session actors {}", msg);
91 - sessionActors.keySet().forEach(address -> onMsg(msg.getMsg())); 87 + sessionActors.keySet().forEach(address -> {
  88 + ClusterAPIProtos.ClusterMessage msgWithServerAddress = msg.getMsg()
  89 + .toBuilder()
  90 + .setServerAddress(ClusterAPIProtos.ServerAddress
  91 + .newBuilder()
  92 + .setHost(address.getHost())
  93 + .setPort(address.getPort())
  94 + .build())
  95 + .build();
  96 + onMsg(msgWithServerAddress);
  97 + });
92 pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg())); 98 pendingMsgs.values().forEach(queue -> queue.add(msg.getMsg()));
93 } 99 }
94 100
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.actors.ruleChain;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.RuleChainId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.msg.MsgType;
  22 +import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
  23 +import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
  24 +
  25 +import java.io.Serializable;
  26 +
  27 +/**
  28 + * Created by ashvayka on 19.03.18.
  29 + */
  30 +@Data
  31 +final class RemoteToRuleChainTellNextMsg extends RuleNodeToRuleChainTellNextMsg implements TenantAwareMsg, RuleChainAwareMsg, Serializable {
  32 +
  33 + private static final long serialVersionUID = 2459605482321657447L;
  34 + private final TenantId tenantId;
  35 + private final RuleChainId ruleChainId;
  36 +
  37 + public RemoteToRuleChainTellNextMsg(RuleNodeToRuleChainTellNextMsg original, TenantId tenantId, RuleChainId ruleChainId) {
  38 + super(original.getOriginator(), original.getRelationTypes(), original.getMsg());
  39 + this.tenantId = tenantId;
  40 + this.ruleChainId = ruleChainId;
  41 + }
  42 +
  43 + @Override
  44 + public MsgType getMsgType() {
  45 + return MsgType.REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG;
  46 + }
  47 +
  48 +}
@@ -49,6 +49,7 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe @@ -49,6 +49,7 @@ public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMe
49 processor.onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg); 49 processor.onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
50 break; 50 break;
51 case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG: 51 case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG:
  52 + case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
52 processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg); 53 processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg);
53 break; 54 break;
54 case RULE_CHAIN_TO_RULE_CHAIN_MSG: 55 case RULE_CHAIN_TO_RULE_CHAIN_MSG:
@@ -20,6 +20,9 @@ import akka.actor.ActorRef; @@ -20,6 +20,9 @@ import akka.actor.ActorRef;
20 import akka.actor.Props; 20 import akka.actor.Props;
21 import akka.event.LoggingAdapter; 21 import akka.event.LoggingAdapter;
22 import com.datastax.driver.core.utils.UUIDs; 22 import com.datastax.driver.core.utils.UUIDs;
  23 +
  24 +import java.util.Optional;
  25 +
23 import org.thingsboard.server.actors.ActorSystemContext; 26 import org.thingsboard.server.actors.ActorSystemContext;
24 import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg; 27 import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
25 import org.thingsboard.server.actors.device.RuleEngineQueuePutAckMsg; 28 import org.thingsboard.server.actors.device.RuleEngineQueuePutAckMsg;
@@ -37,6 +40,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; @@ -37,6 +40,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
37 import org.thingsboard.server.common.data.rule.RuleNode; 40 import org.thingsboard.server.common.data.rule.RuleNode;
38 import org.thingsboard.server.common.msg.TbMsg; 41 import org.thingsboard.server.common.msg.TbMsg;
39 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg; 42 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
  43 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
40 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 44 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
41 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; 45 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
42 import org.thingsboard.server.dao.rule.RuleChainService; 46 import org.thingsboard.server.dao.rule.RuleChainService;
@@ -217,16 +221,36 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -217,16 +221,36 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
217 221
218 void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) { 222 void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) {
219 checkActive(); 223 checkActive();
220 - RuleNodeId originator = envelope.getOriginator();  
221 - List<RuleNodeRelation> relations = nodeRoutes.get(originator).stream()  
222 - .filter(r -> contains(envelope.getRelationTypes(), r.getType()))  
223 - .collect(Collectors.toList()); 224 + TbMsg msg = envelope.getMsg();
  225 + EntityId originatorEntityId = msg.getOriginator();
  226 + Optional<ServerAddress> address = systemContext.getRoutingService().resolveById(originatorEntityId);
  227 +
  228 + if (address.isPresent()) {
  229 + onRemoteTellNext(address.get(), envelope);
  230 + } else {
  231 + onLocalTellNext(envelope);
  232 + }
  233 + }
  234 +
  235 + private void onRemoteTellNext(ServerAddress serverAddress, RuleNodeToRuleChainTellNextMsg envelope) {
  236 + TbMsg msg = envelope.getMsg();
  237 + logger.debug("Forwarding [{}] msg to remote server [{}] due to changed originator id: [{}]", msg.getId(), serverAddress, msg.getOriginator());
  238 + envelope = new RemoteToRuleChainTellNextMsg(envelope, tenantId, entityId);
  239 + systemContext.getRpcService().tell(systemContext.getEncodingService().convertToProtoDataMessage(serverAddress, envelope));
  240 + }
224 241
  242 + private void onLocalTellNext(RuleNodeToRuleChainTellNextMsg envelope) {
225 TbMsg msg = envelope.getMsg(); 243 TbMsg msg = envelope.getMsg();
  244 + RuleNodeId originatorNodeId = envelope.getOriginator();
  245 + List<RuleNodeRelation> relations = nodeRoutes.get(originatorNodeId).stream()
  246 + .filter(r -> contains(envelope.getRelationTypes(), r.getType()))
  247 + .collect(Collectors.toList());
226 int relationsCount = relations.size(); 248 int relationsCount = relations.size();
227 EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId(); 249 EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
228 if (relationsCount == 0) { 250 if (relationsCount == 0) {
229 - queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition()); 251 + if (ackId != null) {
  252 + queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
  253 + }
230 } else if (relationsCount == 1) { 254 } else if (relationsCount == 1) {
231 for (RuleNodeRelation relation : relations) { 255 for (RuleNodeRelation relation : relations) {
232 pushToTarget(msg, relation.getOut(), relation.getType()); 256 pushToTarget(msg, relation.getOut(), relation.getType());
@@ -244,7 +268,9 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh @@ -244,7 +268,9 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
244 } 268 }
245 } 269 }
246 //TODO: Ideally this should happen in async way when all targets confirm that the copied messages are successfully written to corresponding target queues. 270 //TODO: Ideally this should happen in async way when all targets confirm that the copied messages are successfully written to corresponding target queues.
247 - queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition()); 271 + if (ackId != null) {
  272 + queue.ack(tenantId, msg, ackId.getId(), msg.getClusterPartition());
  273 + }
248 } 274 }
249 } 275 }
250 276
@@ -20,12 +20,13 @@ import org.thingsboard.server.common.data.id.RuleChainId; @@ -20,12 +20,13 @@ import org.thingsboard.server.common.data.id.RuleChainId;
20 import org.thingsboard.server.common.msg.MsgType; 20 import org.thingsboard.server.common.msg.MsgType;
21 import org.thingsboard.server.common.msg.TbActorMsg; 21 import org.thingsboard.server.common.msg.TbActorMsg;
22 import org.thingsboard.server.common.msg.TbMsg; 22 import org.thingsboard.server.common.msg.TbMsg;
  23 +import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
23 24
24 /** 25 /**
25 * Created by ashvayka on 19.03.18. 26 * Created by ashvayka on 19.03.18.
26 */ 27 */
27 @Data 28 @Data
28 -public final class RuleChainToRuleChainMsg implements TbActorMsg { 29 +public final class RuleChainToRuleChainMsg implements TbActorMsg, RuleChainAwareMsg {
29 30
30 private final RuleChainId target; 31 private final RuleChainId target;
31 private final RuleChainId source; 32 private final RuleChainId source;
@@ -34,6 +35,11 @@ public final class RuleChainToRuleChainMsg implements TbActorMsg { @@ -34,6 +35,11 @@ public final class RuleChainToRuleChainMsg implements TbActorMsg {
34 private final boolean enqueue; 35 private final boolean enqueue;
35 36
36 @Override 37 @Override
  38 + public RuleChainId getRuleChainId() {
  39 + return target;
  40 + }
  41 +
  42 + @Override
37 public MsgType getMsgType() { 43 public MsgType getMsgType() {
38 return MsgType.RULE_CHAIN_TO_RULE_CHAIN_MSG; 44 return MsgType.RULE_CHAIN_TO_RULE_CHAIN_MSG;
39 } 45 }
@@ -27,7 +27,7 @@ import java.util.Set; @@ -27,7 +27,7 @@ import java.util.Set;
27 * Created by ashvayka on 19.03.18. 27 * Created by ashvayka on 19.03.18.
28 */ 28 */
29 @Data 29 @Data
30 -final class RuleNodeToRuleChainTellNextMsg implements TbActorMsg { 30 +class RuleNodeToRuleChainTellNextMsg implements TbActorMsg {
31 31
32 private final RuleNodeId originator; 32 private final RuleNodeId originator;
33 private final Set<String> relationTypes; 33 private final Set<String> relationTypes;
@@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.id.TenantId;
35 import org.thingsboard.server.common.data.rule.RuleChain; 35 import org.thingsboard.server.common.data.rule.RuleChain;
36 import org.thingsboard.server.common.msg.TbActorMsg; 36 import org.thingsboard.server.common.msg.TbActorMsg;
37 import org.thingsboard.server.common.msg.aware.DeviceAwareMsg; 37 import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
  38 +import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
38 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg; 39 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
39 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; 40 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
40 import scala.concurrent.duration.Duration; 41 import scala.concurrent.duration.Duration;
@@ -94,7 +95,8 @@ public class TenantActor extends RuleChainManagerActor { @@ -94,7 +95,8 @@ public class TenantActor extends RuleChainManagerActor {
94 onToDeviceActorMsg((DeviceAwareMsg) msg); 95 onToDeviceActorMsg((DeviceAwareMsg) msg);
95 break; 96 break;
96 case RULE_CHAIN_TO_RULE_CHAIN_MSG: 97 case RULE_CHAIN_TO_RULE_CHAIN_MSG:
97 - onRuleChainMsg((RuleChainToRuleChainMsg) msg); 98 + case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
  99 + onRuleChainMsg((RuleChainAwareMsg) msg);
98 break; 100 break;
99 default: 101 default:
100 return false; 102 return false;
@@ -109,15 +111,19 @@ public class TenantActor extends RuleChainManagerActor { @@ -109,15 +111,19 @@ public class TenantActor extends RuleChainManagerActor {
109 } 111 }
110 112
111 private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) { 113 private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
  114 + if (ruleChainManager.getRootChainActor()!=null)
112 ruleChainManager.getRootChainActor().tell(msg, self()); 115 ruleChainManager.getRootChainActor().tell(msg, self());
  116 + else logger.info("[{}] No Root Chain", msg);
113 } 117 }
114 118
115 private void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg msg) { 119 private void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg msg) {
  120 + if (ruleChainManager.getRootChainActor()!=null)
116 ruleChainManager.getRootChainActor().tell(msg, self()); 121 ruleChainManager.getRootChainActor().tell(msg, self());
  122 + else logger.info("[{}] No Root Chain", msg);
117 } 123 }
118 124
119 - private void onRuleChainMsg(RuleChainToRuleChainMsg msg) {  
120 - ruleChainManager.getOrCreateActor(context(), msg.getTarget()).tell(msg, self()); 125 + private void onRuleChainMsg(RuleChainAwareMsg msg) {
  126 + ruleChainManager.getOrCreateActor(context(), msg.getRuleChainId()).tell(msg, self());
121 } 127 }
122 128
123 129
@@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -26,6 +26,7 @@ import org.springframework.web.bind.annotation.RequestParam;
26 import org.springframework.web.bind.annotation.ResponseBody; 26 import org.springframework.web.bind.annotation.ResponseBody;
27 import org.springframework.web.bind.annotation.ResponseStatus; 27 import org.springframework.web.bind.annotation.ResponseStatus;
28 import org.springframework.web.bind.annotation.RestController; 28 import org.springframework.web.bind.annotation.RestController;
  29 +import org.thingsboard.server.common.data.EntityType;
29 import org.thingsboard.server.common.data.alarm.Alarm; 30 import org.thingsboard.server.common.data.alarm.Alarm;
30 import org.thingsboard.server.common.data.alarm.AlarmId; 31 import org.thingsboard.server.common.data.alarm.AlarmId;
31 import org.thingsboard.server.common.data.alarm.AlarmInfo; 32 import org.thingsboard.server.common.data.alarm.AlarmInfo;
@@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery; @@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery;
33 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; 34 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
34 import org.thingsboard.server.common.data.alarm.AlarmSeverity; 35 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
35 import org.thingsboard.server.common.data.alarm.AlarmStatus; 36 import org.thingsboard.server.common.data.alarm.AlarmStatus;
  37 +import org.thingsboard.server.common.data.audit.ActionType;
36 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 38 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
37 import org.thingsboard.server.common.data.exception.ThingsboardException; 39 import org.thingsboard.server.common.data.exception.ThingsboardException;
38 import org.thingsboard.server.common.data.id.EntityId; 40 import org.thingsboard.server.common.data.id.EntityId;
@@ -53,7 +55,6 @@ public class AlarmController extends BaseController { @@ -53,7 +55,6 @@ public class AlarmController extends BaseController {
53 checkParameter(ALARM_ID, strAlarmId); 55 checkParameter(ALARM_ID, strAlarmId);
54 try { 56 try {
55 AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); 57 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
56 -  
57 return checkAlarmId(alarmId); 58 return checkAlarmId(alarmId);
58 } catch (Exception e) { 59 } catch (Exception e) {
59 throw handleException(e); 60 throw handleException(e);
@@ -79,8 +80,14 @@ public class AlarmController extends BaseController { @@ -79,8 +80,14 @@ public class AlarmController extends BaseController {
79 public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException { 80 public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException {
80 try { 81 try {
81 alarm.setTenantId(getCurrentUser().getTenantId()); 82 alarm.setTenantId(getCurrentUser().getTenantId());
82 - return checkNotNull(alarmService.createOrUpdateAlarm(alarm)); 83 + Alarm savedAlarm = checkNotNull(alarmService.createOrUpdateAlarm(alarm));
  84 + logEntityAction(savedAlarm.getId(), savedAlarm,
  85 + getCurrentUser().getCustomerId(),
  86 + savedAlarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
  87 + return savedAlarm;
83 } catch (Exception e) { 88 } catch (Exception e) {
  89 + logEntityAction(emptyId(EntityType.ASSET), alarm,
  90 + null, alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
84 throw handleException(e); 91 throw handleException(e);
85 } 92 }
86 } 93 }
@@ -92,8 +99,9 @@ public class AlarmController extends BaseController { @@ -92,8 +99,9 @@ public class AlarmController extends BaseController {
92 checkParameter(ALARM_ID, strAlarmId); 99 checkParameter(ALARM_ID, strAlarmId);
93 try { 100 try {
94 AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); 101 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
95 - checkAlarmId(alarmId); 102 + Alarm alarm = checkAlarmId(alarmId);
96 alarmService.ackAlarm(alarmId, System.currentTimeMillis()).get(); 103 alarmService.ackAlarm(alarmId, System.currentTimeMillis()).get();
  104 + logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null);
97 } catch (Exception e) { 105 } catch (Exception e) {
98 throw handleException(e); 106 throw handleException(e);
99 } 107 }
@@ -106,8 +114,9 @@ public class AlarmController extends BaseController { @@ -106,8 +114,9 @@ public class AlarmController extends BaseController {
106 checkParameter(ALARM_ID, strAlarmId); 114 checkParameter(ALARM_ID, strAlarmId);
107 try { 115 try {
108 AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); 116 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
109 - checkAlarmId(alarmId); 117 + Alarm alarm = checkAlarmId(alarmId);
110 alarmService.clearAlarm(alarmId, null, System.currentTimeMillis()).get(); 118 alarmService.clearAlarm(alarmId, null, System.currentTimeMillis()).get();
  119 + logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null);
111 } catch (Exception e) { 120 } catch (Exception e) {
112 throw handleException(e); 121 throw handleException(e);
113 } 122 }
@@ -529,18 +529,16 @@ public abstract class BaseController { @@ -529,18 +529,16 @@ public abstract class BaseController {
529 return baseUrl; 529 return baseUrl;
530 } 530 }
531 531
532 - protected <I extends UUIDBased & EntityId> I emptyId(EntityType entityType) { 532 + protected <I extends EntityId> I emptyId(EntityType entityType) {
533 return (I)EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); 533 return (I)EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
534 } 534 }
535 535
536 - protected <E extends BaseData<I> & HasName,  
537 - I extends UUIDBased & EntityId> void logEntityAction(I entityId, E entity, CustomerId customerId, 536 + protected <E extends HasName, I extends EntityId> void logEntityAction(I entityId, E entity, CustomerId customerId,
538 ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException { 537 ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
539 logEntityAction(getCurrentUser(), entityId, entity, customerId, actionType, e, additionalInfo); 538 logEntityAction(getCurrentUser(), entityId, entity, customerId, actionType, e, additionalInfo);
540 } 539 }
541 540
542 - protected <E extends BaseData<I> & HasName,  
543 - I extends UUIDBased & EntityId> void logEntityAction(User user, I entityId, E entity, CustomerId customerId, 541 + protected <E extends HasName, I extends EntityId> void logEntityAction(User user, I entityId, E entity, CustomerId customerId,
544 ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException { 542 ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
545 if (customerId == null || customerId.isNullUid()) { 543 if (customerId == null || customerId.isNullUid()) {
546 customerId = user.getCustomerId(); 544 customerId = user.getCustomerId();
@@ -556,8 +554,7 @@ public abstract class BaseController { @@ -556,8 +554,7 @@ public abstract class BaseController {
556 return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null; 554 return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null;
557 } 555 }
558 556
559 - private <E extends BaseData<I> & HasName,  
560 - I extends UUIDBased & EntityId> void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId, 557 + private <E extends HasName, I extends EntityId> void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId,
561 ActionType actionType, Object... additionalInfo) { 558 ActionType actionType, Object... additionalInfo) {
562 String msgType = null; 559 String msgType = null;
563 switch (actionType) { 560 switch (actionType) {
@@ -24,10 +24,13 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -24,10 +24,13 @@ import org.springframework.web.bind.annotation.RequestParam;
24 import org.springframework.web.bind.annotation.ResponseBody; 24 import org.springframework.web.bind.annotation.ResponseBody;
25 import org.springframework.web.bind.annotation.ResponseStatus; 25 import org.springframework.web.bind.annotation.ResponseStatus;
26 import org.springframework.web.bind.annotation.RestController; 26 import org.springframework.web.bind.annotation.RestController;
  27 +import org.thingsboard.server.common.data.alarm.Alarm;
  28 +import org.thingsboard.server.common.data.audit.ActionType;
27 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 29 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
28 import org.thingsboard.server.common.data.exception.ThingsboardException; 30 import org.thingsboard.server.common.data.exception.ThingsboardException;
29 import org.thingsboard.server.common.data.id.EntityId; 31 import org.thingsboard.server.common.data.id.EntityId;
30 import org.thingsboard.server.common.data.id.EntityIdFactory; 32 import org.thingsboard.server.common.data.id.EntityIdFactory;
  33 +import org.thingsboard.server.common.data.id.UUIDBased;
31 import org.thingsboard.server.common.data.relation.EntityRelation; 34 import org.thingsboard.server.common.data.relation.EntityRelation;
32 import org.thingsboard.server.common.data.relation.EntityRelationInfo; 35 import org.thingsboard.server.common.data.relation.EntityRelationInfo;
33 import org.thingsboard.server.common.data.relation.EntityRelationsQuery; 36 import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
@@ -58,7 +61,15 @@ public class EntityRelationController extends BaseController { @@ -58,7 +61,15 @@ public class EntityRelationController extends BaseController {
58 relation.setTypeGroup(RelationTypeGroup.COMMON); 61 relation.setTypeGroup(RelationTypeGroup.COMMON);
59 } 62 }
60 relationService.saveRelation(relation); 63 relationService.saveRelation(relation);
  64 + logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
  65 + ActionType.RELATION_ADD_OR_UPDATE, null, relation);
  66 + logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
  67 + ActionType.RELATION_ADD_OR_UPDATE, null, relation);
61 } catch (Exception e) { 68 } catch (Exception e) {
  69 + logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
  70 + ActionType.RELATION_ADD_OR_UPDATE, e, relation);
  71 + logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
  72 + ActionType.RELATION_ADD_OR_UPDATE, e, relation);
62 throw handleException(e); 73 throw handleException(e);
63 } 74 }
64 } 75 }
@@ -81,12 +92,21 @@ public class EntityRelationController extends BaseController { @@ -81,12 +92,21 @@ public class EntityRelationController extends BaseController {
81 checkEntityId(fromId); 92 checkEntityId(fromId);
82 checkEntityId(toId); 93 checkEntityId(toId);
83 RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON); 94 RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
  95 + EntityRelation relation = new EntityRelation(fromId, toId, strRelationType, relationTypeGroup);
84 try { 96 try {
85 Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup); 97 Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup);
86 if (!found) { 98 if (!found) {
87 throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND); 99 throw new ThingsboardException("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND);
88 } 100 }
  101 + logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
  102 + ActionType.RELATION_DELETED, null, relation);
  103 + logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
  104 + ActionType.RELATION_DELETED, null, relation);
89 } catch (Exception e) { 105 } catch (Exception e) {
  106 + logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),
  107 + ActionType.RELATION_DELETED, e, relation);
  108 + logEntityAction(relation.getTo(), null, getCurrentUser().getCustomerId(),
  109 + ActionType.RELATION_DELETED, e, relation);
90 throw handleException(e); 110 throw handleException(e);
91 } 111 }
92 } 112 }
@@ -102,7 +122,9 @@ public class EntityRelationController extends BaseController { @@ -102,7 +122,9 @@ public class EntityRelationController extends BaseController {
102 checkEntityId(entityId); 122 checkEntityId(entityId);
103 try { 123 try {
104 relationService.deleteEntityRelations(entityId); 124 relationService.deleteEntityRelations(entityId);
  125 + logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, null);
105 } catch (Exception e) { 126 } catch (Exception e) {
  127 + logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, e);
106 throw handleException(e); 128 throw handleException(e);
107 } 129 }
108 } 130 }
@@ -210,8 +232,8 @@ public class EntityRelationController extends BaseController { @@ -210,8 +232,8 @@ public class EntityRelationController extends BaseController {
210 @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {TO_ID, TO_TYPE}) 232 @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {TO_ID, TO_TYPE})
211 @ResponseBody 233 @ResponseBody
212 public List<EntityRelationInfo> findInfoByTo(@RequestParam(TO_ID) String strToId, 234 public List<EntityRelationInfo> findInfoByTo(@RequestParam(TO_ID) String strToId,
213 - @RequestParam(TO_TYPE) String strToType,  
214 - @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException { 235 + @RequestParam(TO_TYPE) String strToType,
  236 + @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
215 checkParameter(TO_ID, strToId); 237 checkParameter(TO_ID, strToId);
216 checkParameter(TO_TYPE, strToType); 238 checkParameter(TO_TYPE, strToType);
217 EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId); 239 EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
@@ -276,10 +298,11 @@ public class EntityRelationController extends BaseController { @@ -276,10 +298,11 @@ public class EntityRelationController extends BaseController {
276 298
277 private RelationTypeGroup parseRelationTypeGroup(String strRelationTypeGroup, RelationTypeGroup defaultValue) { 299 private RelationTypeGroup parseRelationTypeGroup(String strRelationTypeGroup, RelationTypeGroup defaultValue) {
278 RelationTypeGroup result = defaultValue; 300 RelationTypeGroup result = defaultValue;
279 - if (strRelationTypeGroup != null && strRelationTypeGroup.trim().length()>0) { 301 + if (strRelationTypeGroup != null && strRelationTypeGroup.trim().length() > 0) {
280 try { 302 try {
281 result = RelationTypeGroup.valueOf(strRelationTypeGroup); 303 result = RelationTypeGroup.valueOf(strRelationTypeGroup);
282 - } catch (IllegalArgumentException e) { } 304 + } catch (IllegalArgumentException e) {
  305 + }
283 } 306 }
284 return result; 307 return result;
285 } 308 }
@@ -46,7 +46,6 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr @@ -46,7 +46,6 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
46 private static final ConcurrentMap<String, String> externalSessionMap = new ConcurrentHashMap<>(); 46 private static final ConcurrentMap<String, String> externalSessionMap = new ConcurrentHashMap<>();
47 47
48 @Autowired 48 @Autowired
49 - @Lazy  
50 private TelemetryWebSocketService webSocketService; 49 private TelemetryWebSocketService webSocketService;
51 50
52 @Override 51 @Override
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
22 import com.google.protobuf.InvalidProtocolBufferException; 22 import com.google.protobuf.InvalidProtocolBufferException;
23 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
24 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.springframework.context.annotation.Lazy;
25 import org.springframework.stereotype.Service; 26 import org.springframework.stereotype.Service;
26 import org.thingsboard.rule.engine.api.RpcError; 27 import org.thingsboard.rule.engine.api.RpcError;
27 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; 28 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
@@ -67,6 +68,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService { @@ -67,6 +68,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
67 private ClusterRpcService rpcService; 68 private ClusterRpcService rpcService;
68 69
69 @Autowired 70 @Autowired
  71 + @Lazy
70 private ActorService actorService; 72 private ActorService actorService;
71 73
72 private ScheduledExecutorService rpcCallBackExecutor; 74 private ScheduledExecutorService rpcCallBackExecutor;
@@ -28,6 +28,7 @@ import lombok.Getter; @@ -28,6 +28,7 @@ import lombok.Getter;
28 import lombok.extern.slf4j.Slf4j; 28 import lombok.extern.slf4j.Slf4j;
29 import org.springframework.beans.factory.annotation.Autowired; 29 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.beans.factory.annotation.Value; 30 import org.springframework.beans.factory.annotation.Value;
  31 +import org.springframework.context.annotation.Lazy;
31 import org.springframework.stereotype.Service; 32 import org.springframework.stereotype.Service;
32 import org.thingsboard.rule.engine.api.RpcError; 33 import org.thingsboard.rule.engine.api.RpcError;
33 import org.thingsboard.server.actors.service.ActorService; 34 import org.thingsboard.server.actors.service.ActorService;
@@ -102,6 +103,7 @@ public class DefaultDeviceStateService implements DeviceStateService { @@ -102,6 +103,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
102 private AttributesService attributesService; 103 private AttributesService attributesService;
103 104
104 @Autowired 105 @Autowired
  106 + @Lazy
105 private ActorService actorService; 107 private ActorService actorService;
106 108
107 @Autowired 109 @Autowired
@@ -432,7 +432,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio @@ -432,7 +432,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
432 deviceSubscriptions.stream().filter(filter).forEach(s -> { 432 deviceSubscriptions.stream().filter(filter).forEach(s -> {
433 String sessionId = s.getWsSessionId(); 433 String sessionId = s.getWsSessionId();
434 List<TsKvEntry> subscriptionUpdate = f.apply(s); 434 List<TsKvEntry> subscriptionUpdate = f.apply(s);
435 - if (subscriptionUpdate == null || !subscriptionUpdate.isEmpty()) { 435 + if (subscriptionUpdate != null && !subscriptionUpdate.isEmpty()) {
436 SubscriptionUpdate update = new SubscriptionUpdate(s.getSubscriptionId(), subscriptionUpdate); 436 SubscriptionUpdate update = new SubscriptionUpdate(s.getSubscriptionId(), subscriptionUpdate);
437 if (s.isLocal()) { 437 if (s.isLocal()) {
438 updateSubscriptionState(sessionId, s, update); 438 updateSubscriptionState(sessionId, s, update);
@@ -225,7 +225,7 @@ sql: @@ -225,7 +225,7 @@ sql:
225 # Actor system parameters 225 # Actor system parameters
226 actors: 226 actors:
227 tenant: 227 tenant:
228 - create_components_on_init: true 228 + create_components_on_init: "${ACTORS_TENANT_CREATE_COMPONENTS_ON_INIT:true}"
229 session: 229 session:
230 max_concurrent_sessions_per_device: "${ACTORS_MAX_CONCURRENT_SESSION_PER_DEVICE:1}" 230 max_concurrent_sessions_per_device: "${ACTORS_MAX_CONCURRENT_SESSION_PER_DEVICE:1}"
231 sync: 231 sync:
@@ -328,6 +328,13 @@ spring.mvc.cors: @@ -328,6 +328,13 @@ spring.mvc.cors:
328 max-age: "1800" 328 max-age: "1800"
329 allow-credentials: "true" 329 allow-credentials: "true"
330 330
  331 +# spring serve gzip compressed static resources
  332 +spring.resources.chain:
  333 + gzipped: "true"
  334 + strategy:
  335 + content:
  336 + enabled: "true"
  337 +
331 # HSQLDB DAO Configuration 338 # HSQLDB DAO Configuration
332 spring: 339 spring:
333 data: 340 data:
@@ -378,6 +385,7 @@ audit_log: @@ -378,6 +385,7 @@ audit_log:
378 "customer": "${AUDIT_LOG_MASK_CUSTOMER:W}" 385 "customer": "${AUDIT_LOG_MASK_CUSTOMER:W}"
379 "user": "${AUDIT_LOG_MASK_USER:W}" 386 "user": "${AUDIT_LOG_MASK_USER:W}"
380 "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}" 387 "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}"
  388 + "alarm": "${AUDIT_LOG_MASK_ALARM:W}"
381 sink: 389 sink:
382 # Type of external sink. possible options: none, elasticsearch 390 # Type of external sink. possible options: none, elasticsearch
383 type: "${AUDIT_LOG_SINK_TYPE:none}" 391 type: "${AUDIT_LOG_SINK_TYPE:none}"
@@ -30,13 +30,13 @@ export SQL_DATA_FOLDER=${SQL_DATA_FOLDER:-/tmp} @@ -30,13 +30,13 @@ export SQL_DATA_FOLDER=${SQL_DATA_FOLDER:-/tmp}
30 30
31 run_user=thingsboard 31 run_user=thingsboard
32 32
33 -su -s /bin/sh -c "java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ 33 +sudo -u "$run_user" -s /bin/sh -c "java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \
34 -Dinstall.data_dir=${installDir} \ 34 -Dinstall.data_dir=${installDir} \
35 -Dinstall.load_demo=${loadDemo} \ 35 -Dinstall.load_demo=${loadDemo} \
36 -Dspring.jpa.hibernate.ddl-auto=none \ 36 -Dspring.jpa.hibernate.ddl-auto=none \
37 -Dinstall.upgrade=false \ 37 -Dinstall.upgrade=false \
38 -Dlogging.config=logback.xml \ 38 -Dlogging.config=logback.xml \
39 - org.springframework.boot.loader.PropertiesLauncher" "$run_user" 39 + org.springframework.boot.loader.PropertiesLauncher"
40 40
41 if [ $? -ne 0 ]; then 41 if [ $? -ne 0 ]; then
42 echo "ThingsBoard DB installation failed!" 42 echo "ThingsBoard DB installation failed!"
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
@@ -31,7 +31,12 @@ public enum ActionType { @@ -31,7 +31,12 @@ public enum ActionType {
31 ACTIVATED(false), // log string id 31 ACTIVATED(false), // log string id
32 SUSPENDED(false), // log string id 32 SUSPENDED(false), // log string id
33 CREDENTIALS_READ(true), // log device id 33 CREDENTIALS_READ(true), // log device id
34 - ATTRIBUTES_READ(true); // log attributes 34 + ATTRIBUTES_READ(true), // log attributes
  35 + RELATION_ADD_OR_UPDATE (false),
  36 + RELATION_DELETED (false),
  37 + RELATIONS_DELETED (false),
  38 + ALARM_ACK (false),
  39 + ALARM_CLEAR (false);
35 40
36 private final boolean isRead; 41 private final boolean isRead;
37 42
@@ -60,4 +60,5 @@ public class EntityIdFactory { @@ -60,4 +60,5 @@ public class EntityIdFactory {
60 } 60 }
61 throw new IllegalArgumentException("EntityType " + type + " is not supported!"); 61 throw new IllegalArgumentException("EntityType " + type + " is not supported!");
62 } 62 }
  63 +
63 } 64 }
@@ -33,13 +33,19 @@ public class RelationsSearchParameters { @@ -33,13 +33,19 @@ public class RelationsSearchParameters {
33 private UUID rootId; 33 private UUID rootId;
34 private EntityType rootType; 34 private EntityType rootType;
35 private EntitySearchDirection direction; 35 private EntitySearchDirection direction;
  36 + private RelationTypeGroup relationTypeGroup;
36 private int maxLevel = 1; 37 private int maxLevel = 1;
37 38
38 public RelationsSearchParameters(EntityId entityId, EntitySearchDirection direction, int maxLevel) { 39 public RelationsSearchParameters(EntityId entityId, EntitySearchDirection direction, int maxLevel) {
  40 + this(entityId, direction, maxLevel, RelationTypeGroup.COMMON);
  41 + }
  42 +
  43 + public RelationsSearchParameters(EntityId entityId, EntitySearchDirection direction, int maxLevel, RelationTypeGroup relationTypeGroup) {
39 this.rootId = entityId.getId(); 44 this.rootId = entityId.getId();
40 this.rootType = entityId.getEntityType(); 45 this.rootType = entityId.getEntityType();
41 this.direction = direction; 46 this.direction = direction;
42 this.maxLevel = maxLevel; 47 this.maxLevel = maxLevel;
  48 + this.relationTypeGroup = relationTypeGroup;
43 } 49 }
44 50
45 public EntityId getEntityId() { 51 public EntityId getEntityId() {
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
@@ -63,6 +63,11 @@ public enum MsgType { @@ -63,6 +63,11 @@ public enum MsgType {
63 RULE_TO_RULE_CHAIN_TELL_NEXT_MSG, 63 RULE_TO_RULE_CHAIN_TELL_NEXT_MSG,
64 64
65 /** 65 /**
  66 + * Message forwarded from original rule chain to remote rule chain due to change in the cluster structure or originator entity of the TbMsg.
  67 + */
  68 + REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG,
  69 +
  70 + /**
66 * Message that is sent by RuleActor implementation to RuleActor itself to log the error. 71 * Message that is sent by RuleActor implementation to RuleActor itself to log the error.
67 */ 72 */
68 RULE_TO_SELF_ERROR_MSG, 73 RULE_TO_SELF_ERROR_MSG,
@@ -101,6 +106,10 @@ public enum MsgType { @@ -101,6 +106,10 @@ public enum MsgType {
101 /** 106 /**
102 * Message that is sent from Rule Engine to the Device Actor when message is successfully pushed to queue. 107 * Message that is sent from Rule Engine to the Device Actor when message is successfully pushed to queue.
103 */ 108 */
104 - RULE_ENGINE_QUEUE_PUT_ACK_MSG, ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG, TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG, SESSION_TIMEOUT_MSG, SESSION_CTRL_MSG; 109 + RULE_ENGINE_QUEUE_PUT_ACK_MSG,
  110 + ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG,
  111 + TRANSPORT_TO_DEVICE_SESSION_ACTOR_MSG,
  112 + SESSION_TIMEOUT_MSG,
  113 + SESSION_CTRL_MSG;
105 114
106 } 115 }
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.msg.aware;
  17 +
  18 +import org.thingsboard.server.common.data.id.RuleChainId;
  19 +
  20 +public interface RuleChainAwareMsg {
  21 +
  22 + RuleChainId getRuleChainId();
  23 +
  24 +}
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard</groupId> 26 <groupId>org.thingsboard</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>common</artifactId> 24 <artifactId>common</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.common</groupId> 26 <groupId>org.thingsboard.common</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <artifactId>dao</artifactId> 26 <artifactId>dao</artifactId>
@@ -40,15 +40,14 @@ public interface AuditLogService { @@ -40,15 +40,14 @@ public interface AuditLogService {
40 40
41 TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink); 41 TimePageData<AuditLog> findAuditLogsByTenantId(TenantId tenantId, TimePageLink pageLink);
42 42
43 - <E extends BaseData<I> & HasName,  
44 - I extends UUIDBased & EntityId> ListenableFuture<List<Void>> logEntityAction(  
45 - TenantId tenantId,  
46 - CustomerId customerId,  
47 - UserId userId,  
48 - String userName,  
49 - I entityId,  
50 - E entity,  
51 - ActionType actionType,  
52 - Exception e, Object... additionalInfo); 43 + <E extends HasName, I extends EntityId> ListenableFuture<List<Void>> logEntityAction(
  44 + TenantId tenantId,
  45 + CustomerId customerId,
  46 + UserId userId,
  47 + String userName,
  48 + I entityId,
  49 + E entity,
  50 + ActionType actionType,
  51 + Exception e, Object... additionalInfo);
53 52
54 } 53 }
@@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.id.UserId; @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.id.UserId;
43 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 43 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
44 import org.thingsboard.server.common.data.page.TimePageData; 44 import org.thingsboard.server.common.data.page.TimePageData;
45 import org.thingsboard.server.common.data.page.TimePageLink; 45 import org.thingsboard.server.common.data.page.TimePageLink;
  46 +import org.thingsboard.server.common.data.relation.EntityRelation;
46 import org.thingsboard.server.common.data.rule.RuleChainMetaData; 47 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
47 import org.thingsboard.server.common.data.security.DeviceCredentials; 48 import org.thingsboard.server.common.data.security.DeviceCredentials;
48 import org.thingsboard.server.dao.audit.sink.AuditLogSink; 49 import org.thingsboard.server.dao.audit.sink.AuditLogSink;
@@ -115,7 +116,7 @@ public class AuditLogServiceImpl implements AuditLogService { @@ -115,7 +116,7 @@ public class AuditLogServiceImpl implements AuditLogService {
115 } 116 }
116 117
117 @Override 118 @Override
118 - public <E extends BaseData<I> & HasName, I extends UUIDBased & EntityId> ListenableFuture<List<Void>> 119 + public <E extends HasName, I extends EntityId> ListenableFuture<List<Void>>
119 logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, 120 logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity,
120 ActionType actionType, Exception e, Object... additionalInfo) { 121 ActionType actionType, Exception e, Object... additionalInfo) {
121 if (canLog(entityId.getEntityType(), actionType)) { 122 if (canLog(entityId.getEntityType(), actionType)) {
@@ -156,14 +157,16 @@ public class AuditLogServiceImpl implements AuditLogService { @@ -156,14 +157,16 @@ public class AuditLogServiceImpl implements AuditLogService {
156 } 157 }
157 } 158 }
158 159
159 - private <E extends BaseData<I> & HasName, I extends UUIDBased & EntityId> JsonNode constructActionData(I entityId,  
160 - E entity, 160 + private <E extends HasName, I extends EntityId> JsonNode constructActionData(I entityId, E entity,
161 ActionType actionType, 161 ActionType actionType,
162 Object... additionalInfo) { 162 Object... additionalInfo) {
163 ObjectNode actionData = objectMapper.createObjectNode(); 163 ObjectNode actionData = objectMapper.createObjectNode();
164 switch(actionType) { 164 switch(actionType) {
165 case ADDED: 165 case ADDED:
166 case UPDATED: 166 case UPDATED:
  167 + case ALARM_ACK:
  168 + case ALARM_CLEAR:
  169 + case RELATIONS_DELETED:
167 if (entity != null) { 170 if (entity != null) {
168 ObjectNode entityNode = objectMapper.valueToTree(entity); 171 ObjectNode entityNode = objectMapper.valueToTree(entity);
169 if (entityId.getEntityType() == EntityType.DASHBOARD) { 172 if (entityId.getEntityType() == EntityType.DASHBOARD) {
@@ -240,6 +243,11 @@ public class AuditLogServiceImpl implements AuditLogService { @@ -240,6 +243,11 @@ public class AuditLogServiceImpl implements AuditLogService {
240 actionData.put("unassignedCustomerId", strCustomerId); 243 actionData.put("unassignedCustomerId", strCustomerId);
241 actionData.put("unassignedCustomerName", strCustomerName); 244 actionData.put("unassignedCustomerName", strCustomerName);
242 break; 245 break;
  246 + case RELATION_ADD_OR_UPDATE:
  247 + case RELATION_DELETED:
  248 + EntityRelation relation = extractParameter(EntityRelation.class, 0, additionalInfo);
  249 + actionData.set("relation", objectMapper.valueToTree(relation));
  250 + break;
243 } 251 }
244 return actionData; 252 return actionData;
245 } 253 }
@@ -57,7 +57,7 @@ public class DummyAuditLogServiceImpl implements AuditLogService { @@ -57,7 +57,7 @@ public class DummyAuditLogServiceImpl implements AuditLogService {
57 } 57 }
58 58
59 @Override 59 @Override
60 - public <E extends BaseData<I> & HasName, I extends UUIDBased & EntityId> ListenableFuture<List<Void>> logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) { 60 + public <E extends HasName, I extends EntityId> ListenableFuture<List<Void>> logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) {
61 return null; 61 return null;
62 } 62 }
63 63
@@ -16,8 +16,10 @@ @@ -16,8 +16,10 @@
16 package org.thingsboard.server.dao.cache; 16 package org.thingsboard.server.dao.cache;
17 17
18 import com.github.benmanes.caffeine.cache.Caffeine; 18 import com.github.benmanes.caffeine.cache.Caffeine;
  19 +import com.github.benmanes.caffeine.cache.RemovalCause;
19 import com.github.benmanes.caffeine.cache.Ticker; 20 import com.github.benmanes.caffeine.cache.Ticker;
20 import lombok.Data; 21 import lombok.Data;
  22 +import lombok.extern.slf4j.Slf4j;
21 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 23 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
22 import org.springframework.boot.context.properties.ConfigurationProperties; 24 import org.springframework.boot.context.properties.ConfigurationProperties;
23 import org.springframework.cache.CacheManager; 25 import org.springframework.cache.CacheManager;
@@ -28,6 +30,7 @@ import org.springframework.cache.support.SimpleCacheManager; @@ -28,6 +30,7 @@ import org.springframework.cache.support.SimpleCacheManager;
28 import org.springframework.context.annotation.Bean; 30 import org.springframework.context.annotation.Bean;
29 import org.springframework.context.annotation.Configuration; 31 import org.springframework.context.annotation.Configuration;
30 32
  33 +import java.util.Arrays;
31 import java.util.List; 34 import java.util.List;
32 import java.util.Map; 35 import java.util.Map;
33 import java.util.concurrent.TimeUnit; 36 import java.util.concurrent.TimeUnit;
@@ -38,12 +41,14 @@ import java.util.stream.Collectors; @@ -38,12 +41,14 @@ import java.util.stream.Collectors;
38 @ConfigurationProperties(prefix = "caffeine") 41 @ConfigurationProperties(prefix = "caffeine")
39 @EnableCaching 42 @EnableCaching
40 @Data 43 @Data
  44 +@Slf4j
41 public class CaffeineCacheConfiguration { 45 public class CaffeineCacheConfiguration {
42 46
43 private Map<String, CacheSpecs> specs; 47 private Map<String, CacheSpecs> specs;
44 48
45 @Bean 49 @Bean
46 public CacheManager cacheManager() { 50 public CacheManager cacheManager() {
  51 + log.trace("Initializing cache: {}", Arrays.toString(RemovalCause.values()));
47 SimpleCacheManager manager = new SimpleCacheManager(); 52 SimpleCacheManager manager = new SimpleCacheManager();
48 if (specs != null) { 53 if (specs != null) {
49 List<CaffeineCache> caches = 54 List<CaffeineCache> caches =
@@ -402,7 +402,7 @@ public class BaseRelationService implements RelationService { @@ -402,7 +402,7 @@ public class BaseRelationService implements RelationService {
402 int maxLvl = params.getMaxLevel() > 0 ? params.getMaxLevel() : Integer.MAX_VALUE; 402 int maxLvl = params.getMaxLevel() > 0 ? params.getMaxLevel() : Integer.MAX_VALUE;
403 403
404 try { 404 try {
405 - ListenableFuture<Set<EntityRelation>> relationSet = findRelationsRecursively(params.getEntityId(), params.getDirection(), maxLvl, new ConcurrentHashMap<>()); 405 + ListenableFuture<Set<EntityRelation>> relationSet = findRelationsRecursively(params.getEntityId(), params.getDirection(), params.getRelationTypeGroup(), maxLvl, new ConcurrentHashMap<>());
406 return Futures.transform(relationSet, input -> { 406 return Futures.transform(relationSet, input -> {
407 List<EntityRelation> relations = new ArrayList<>(); 407 List<EntityRelation> relations = new ArrayList<>();
408 if (filters == null || filters.isEmpty()) { 408 if (filters == null || filters.isEmpty()) {
@@ -518,14 +518,15 @@ public class BaseRelationService implements RelationService { @@ -518,14 +518,15 @@ public class BaseRelationService implements RelationService {
518 } 518 }
519 } 519 }
520 520
521 - private ListenableFuture<Set<EntityRelation>> findRelationsRecursively(final EntityId rootId, final EntitySearchDirection direction, int lvl, 521 + private ListenableFuture<Set<EntityRelation>> findRelationsRecursively(final EntityId rootId, final EntitySearchDirection direction,
  522 + RelationTypeGroup relationTypeGroup, int lvl,
522 final ConcurrentHashMap<EntityId, Boolean> uniqueMap) throws Exception { 523 final ConcurrentHashMap<EntityId, Boolean> uniqueMap) throws Exception {
523 if (lvl == 0) { 524 if (lvl == 0) {
524 return Futures.immediateFuture(Collections.emptySet()); 525 return Futures.immediateFuture(Collections.emptySet());
525 } 526 }
526 lvl--; 527 lvl--;
527 //TODO: try to remove this blocking operation 528 //TODO: try to remove this blocking operation
528 - Set<EntityRelation> children = new HashSet<>(findRelations(rootId, direction).get()); 529 + Set<EntityRelation> children = new HashSet<>(findRelations(rootId, direction, relationTypeGroup).get());
529 Set<EntityId> childrenIds = new HashSet<>(); 530 Set<EntityId> childrenIds = new HashSet<>();
530 for (EntityRelation childRelation : children) { 531 for (EntityRelation childRelation : children) {
531 log.trace("Found Relation: {}", childRelation); 532 log.trace("Found Relation: {}", childRelation);
@@ -544,7 +545,7 @@ public class BaseRelationService implements RelationService { @@ -544,7 +545,7 @@ public class BaseRelationService implements RelationService {
544 } 545 }
545 List<ListenableFuture<Set<EntityRelation>>> futures = new ArrayList<>(); 546 List<ListenableFuture<Set<EntityRelation>>> futures = new ArrayList<>();
546 for (EntityId entityId : childrenIds) { 547 for (EntityId entityId : childrenIds) {
547 - futures.add(findRelationsRecursively(entityId, direction, lvl, uniqueMap)); 548 + futures.add(findRelationsRecursively(entityId, direction, relationTypeGroup, lvl, uniqueMap));
548 } 549 }
549 //TODO: try to remove this blocking operation 550 //TODO: try to remove this blocking operation
550 List<Set<EntityRelation>> relations = Futures.successfulAsList(futures).get(); 551 List<Set<EntityRelation>> relations = Futures.successfulAsList(futures).get();
@@ -552,12 +553,15 @@ public class BaseRelationService implements RelationService { @@ -552,12 +553,15 @@ public class BaseRelationService implements RelationService {
552 return Futures.immediateFuture(children); 553 return Futures.immediateFuture(children);
553 } 554 }
554 555
555 - private ListenableFuture<List<EntityRelation>> findRelations(final EntityId rootId, final EntitySearchDirection direction) { 556 + private ListenableFuture<List<EntityRelation>> findRelations(final EntityId rootId, final EntitySearchDirection direction, RelationTypeGroup relationTypeGroup) {
556 ListenableFuture<List<EntityRelation>> relations; 557 ListenableFuture<List<EntityRelation>> relations;
  558 + if (relationTypeGroup == null) {
  559 + relationTypeGroup = RelationTypeGroup.COMMON;
  560 + }
557 if (direction == EntitySearchDirection.FROM) { 561 if (direction == EntitySearchDirection.FROM) {
558 - relations = findByFromAsync(rootId, RelationTypeGroup.COMMON); 562 + relations = findByFromAsync(rootId, relationTypeGroup);
559 } else { 563 } else {
560 - relations = findByToAsync(rootId, RelationTypeGroup.COMMON); 564 + relations = findByToAsync(rootId, relationTypeGroup);
561 } 565 }
562 return relations; 566 return relations;
563 } 567 }
1 -VERSION=2.0.3 1 +VERSION=2.1.0
2 PROJECT=thingsboard 2 PROJECT=thingsboard
3 APP=cassandra-setup 3 APP=cassandra-setup
4 4
  1 +#
  2 +# Copyright © 2016-2018 The Thingsboard Authors
  3 +#
  4 +# Licensed under the Apache License, Version 2.0 (the "License");
  5 +# you may not use this file except in compliance with the License.
  6 +# You may obtain a copy of the License at
  7 +#
  8 +# http://www.apache.org/licenses/LICENSE-2.0
  9 +#
  10 +# Unless required by applicable law or agreed to in writing, software
  11 +# distributed under the License is distributed on an "AS IS" BASIS,
  12 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +# See the License for the specific language governing permissions and
  14 +# limitations under the License.
  15 +#
  16 +
  17 +FROM openjdk:8-jre
  18 +
  19 +ADD upgrade.sh /upgrade.sh
  20 +ADD thingsboard.deb /thingsboard.deb
  21 +
  22 +RUN apt-get update \
  23 + && apt-get install -y nmap \
  24 + && chmod +x /upgrade.sh
  1 +VERSION=2.1.0
  2 +PROJECT=thingsboard
  3 +APP=cassandra-upgrade
  4 +
  5 +build:
  6 + cp ../../application/target/thingsboard.deb .
  7 + docker build --pull -t ${PROJECT}/${APP}:${VERSION} -t ${PROJECT}/${APP}:latest .
  8 + rm thingsboard.deb
  9 +
  10 +push: build
  11 + docker push ${PROJECT}/${APP}:${VERSION}
  12 + docker push ${PROJECT}/${APP}:latest
  1 +#!/bin/bash
  2 +#
  3 +# Copyright © 2016-2018 The Thingsboard Authors
  4 +#
  5 +# Licensed under the Apache License, Version 2.0 (the "License");
  6 +# you may not use this file except in compliance with the License.
  7 +# You may obtain a copy of the License at
  8 +#
  9 +# http://www.apache.org/licenses/LICENSE-2.0
  10 +#
  11 +# Unless required by applicable law or agreed to in writing, software
  12 +# distributed under the License is distributed on an "AS IS" BASIS,
  13 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14 +# See the License for the specific language governing permissions and
  15 +# limitations under the License.
  16 +#
  17 +
  18 +
  19 +dpkg -i /thingsboard.deb
  20 +
  21 +until nmap $CASSANDRA_HOST -p $CASSANDRA_PORT | grep "$CASSANDRA_PORT/tcp open"
  22 +do
  23 + echo "Wait for cassandra db to start..."
  24 + sleep 10
  25 +done
  26 +
  27 +echo "Upgrading 'Thingsboard' schema..."
  28 +/usr/share/thingsboard/bin/install/upgrade.sh --fromVersion=$UPGRADE_FROM_VERSION
1 -VERSION=2.0.3 1 +VERSION=2.1.0
2 PROJECT=thingsboard 2 PROJECT=thingsboard
3 APP=cassandra 3 APP=cassandra
4 4
@@ -18,7 +18,7 @@ version: '2' @@ -18,7 +18,7 @@ version: '2'
18 18
19 services: 19 services:
20 tb: 20 tb:
21 - image: "thingsboard/application:2.0.3" 21 + image: "thingsboard/application:2.1.0"
22 ports: 22 ports:
23 - "8080:8080" 23 - "8080:8080"
24 - "1883:1883" 24 - "1883:1883"
@@ -22,7 +22,7 @@ spec: @@ -22,7 +22,7 @@ spec:
22 containers: 22 containers:
23 - name: cassandra-setup 23 - name: cassandra-setup
24 imagePullPolicy: Always 24 imagePullPolicy: Always
25 - image: thingsboard/cassandra-setup:2.0.3 25 + image: thingsboard/cassandra-setup:2.1.0
26 env: 26 env:
27 - name: ADD_DEMO_DATA 27 - name: ADD_DEMO_DATA
28 value: "true" 28 value: "true"
  1 +#
  2 +# Copyright © 2016-2018 The Thingsboard Authors
  3 +#
  4 +# Licensed under the Apache License, Version 2.0 (the "License");
  5 +# you may not use this file except in compliance with the License.
  6 +# You may obtain a copy of the License at
  7 +#
  8 +# http://www.apache.org/licenses/LICENSE-2.0
  9 +#
  10 +# Unless required by applicable law or agreed to in writing, software
  11 +# distributed under the License is distributed on an "AS IS" BASIS,
  12 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +# See the License for the specific language governing permissions and
  14 +# limitations under the License.
  15 +#
  16 +
  17 +apiVersion: v1
  18 +kind: Pod
  19 +metadata:
  20 + name: cassandra-upgrade
  21 +spec:
  22 + containers:
  23 + - name: cassandra-upgrade
  24 + imagePullPolicy: Always
  25 + image: thingsboard/cassandra-upgrade:2.1.0
  26 + env:
  27 + - name: ADD_DEMO_DATA
  28 + value: "true"
  29 + - name : CASSANDRA_HOST
  30 + value: "cassandra-headless"
  31 + - name : CASSANDRA_PORT
  32 + value: "9042"
  33 + - name : DATABASE_TYPE
  34 + value: "cassandra"
  35 + - name : CASSANDRA_URL
  36 + value: "cassandra-headless:9042"
  37 + - name : UPGRADE_FROM_VERSION
  38 + value: "1.4.0"
  39 + command:
  40 + - sh
  41 + - -c
  42 + - /upgrade.sh
  43 + restartPolicy: Never
@@ -54,7 +54,7 @@ spec: @@ -54,7 +54,7 @@ spec:
54 topologyKey: "kubernetes.io/hostname" 54 topologyKey: "kubernetes.io/hostname"
55 containers: 55 containers:
56 - name: cassandra 56 - name: cassandra
57 - image: thingsboard/cassandra:2.0.3 57 + image: thingsboard/cassandra:2.1.0
58 imagePullPolicy: Always 58 imagePullPolicy: Always
59 ports: 59 ports:
60 - containerPort: 7000 60 - containerPort: 7000
@@ -84,7 +84,7 @@ spec: @@ -84,7 +84,7 @@ spec:
84 containers: 84 containers:
85 - name: tb 85 - name: tb
86 imagePullPolicy: Always 86 imagePullPolicy: Always
87 - image: thingsboard/application:2.0.3 87 + image: thingsboard/application:2.1.0
88 ports: 88 ports:
89 - containerPort: 8080 89 - containerPort: 8080
90 name: ui 90 name: ui
@@ -87,7 +87,7 @@ spec: @@ -87,7 +87,7 @@ spec:
87 containers: 87 containers:
88 - name: zk 88 - name: zk
89 imagePullPolicy: Always 89 imagePullPolicy: Always
90 - image: thingsboard/zk:2.0.3 90 + image: thingsboard/zk:2.1.0
91 ports: 91 ports:
92 - containerPort: 2181 92 - containerPort: 2181
93 name: client 93 name: client
1 -VERSION=2.0.3 1 +VERSION=2.1.0
2 PROJECT=thingsboard 2 PROJECT=thingsboard
3 APP=application 3 APP=application
4 4
1 -VERSION=2.0.3 1 +VERSION=2.1.0
2 PROJECT=thingsboard 2 PROJECT=thingsboard
3 APP=zk 3 APP=zk
4 4
@@ -19,12 +19,12 @@ @@ -19,12 +19,12 @@
19 <modelVersion>4.0.0</modelVersion> 19 <modelVersion>4.0.0</modelVersion>
20 <parent> 20 <parent>
21 <groupId>org.thingsboard</groupId> 21 <groupId>org.thingsboard</groupId>
22 - <version>2.0.3</version> 22 + <version>2.1.0-SNAPSHOT</version>
23 <artifactId>thingsboard</artifactId> 23 <artifactId>thingsboard</artifactId>
24 </parent> 24 </parent>
25 <groupId>org.thingsboard</groupId> 25 <groupId>org.thingsboard</groupId>
26 <artifactId>netty-mqtt</artifactId> 26 <artifactId>netty-mqtt</artifactId>
27 - <version>2.0.3</version> 27 + <version>2.1.0-SNAPSHOT</version>
28 <packaging>jar</packaging> 28 <packaging>jar</packaging>
29 29
30 <name>Netty MQTT Client</name> 30 <name>Netty MQTT Client</name>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <groupId>org.thingsboard</groupId> 21 <groupId>org.thingsboard</groupId>
22 <artifactId>thingsboard</artifactId> 22 <artifactId>thingsboard</artifactId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <packaging>pom</packaging> 24 <packaging>pom</packaging>
25 25
26 <name>Thingsboard</name> 26 <name>Thingsboard</name>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <artifactId>rule-engine</artifactId> 26 <artifactId>rule-engine</artifactId>
@@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
22 <modelVersion>4.0.0</modelVersion> 22 <modelVersion>4.0.0</modelVersion>
23 <parent> 23 <parent>
24 <groupId>org.thingsboard</groupId> 24 <groupId>org.thingsboard</groupId>
25 - <version>2.0.3</version> 25 + <version>2.1.0-SNAPSHOT</version>
26 <artifactId>rule-engine</artifactId> 26 <artifactId>rule-engine</artifactId>
27 </parent> 27 </parent>
28 <groupId>org.thingsboard.rule-engine</groupId> 28 <groupId>org.thingsboard.rule-engine</groupId>
@@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
22 <modelVersion>4.0.0</modelVersion> 22 <modelVersion>4.0.0</modelVersion>
23 <parent> 23 <parent>
24 <groupId>org.thingsboard</groupId> 24 <groupId>org.thingsboard</groupId>
25 - <version>2.0.3</version> 25 + <version>2.1.0-SNAPSHOT</version>
26 <artifactId>rule-engine</artifactId> 26 <artifactId>rule-engine</artifactId>
27 </parent> 27 </parent>
28 <groupId>org.thingsboard.rule-engine</groupId> 28 <groupId>org.thingsboard.rule-engine</groupId>
@@ -47,11 +47,12 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; @@ -47,11 +47,12 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
47 47
48 public class TbMsgGeneratorNode implements TbNode { 48 public class TbMsgGeneratorNode implements TbNode {
49 49
50 - public static final String TB_MSG_GENERATOR_NODE_MSG = "TbMsgGeneratorNodeMsg"; 50 + private static final String TB_MSG_GENERATOR_NODE_MSG = "TbMsgGeneratorNodeMsg";
51 51
52 private TbMsgGeneratorNodeConfiguration config; 52 private TbMsgGeneratorNodeConfiguration config;
53 private ScriptEngine jsEngine; 53 private ScriptEngine jsEngine;
54 private long delay; 54 private long delay;
  55 + private long lastScheduledTs;
55 private EntityId originatorId; 56 private EntityId originatorId;
56 private UUID nextTickId; 57 private UUID nextTickId;
57 private TbMsg prevMsg; 58 private TbMsg prevMsg;
@@ -66,28 +67,40 @@ public class TbMsgGeneratorNode implements TbNode { @@ -66,28 +67,40 @@ public class TbMsgGeneratorNode implements TbNode {
66 originatorId = ctx.getSelfId(); 67 originatorId = ctx.getSelfId();
67 } 68 }
68 this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "prevMsg", "prevMetadata", "prevMsgType"); 69 this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "prevMsg", "prevMetadata", "prevMsgType");
69 - sentTickMsg(ctx); 70 + scheduleTickMsg(ctx);
70 } 71 }
71 72
72 @Override 73 @Override
73 public void onMsg(TbContext ctx, TbMsg msg) { 74 public void onMsg(TbContext ctx, TbMsg msg) {
74 if (msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) { 75 if (msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) {
75 withCallback(generate(ctx), 76 withCallback(generate(ctx),
76 - m -> {ctx.tellNext(m, SUCCESS); sentTickMsg(ctx);},  
77 - t -> {ctx.tellFailure(msg, t); sentTickMsg(ctx);}); 77 + m -> {
  78 + ctx.tellNext(m, SUCCESS);
  79 + scheduleTickMsg(ctx);
  80 + },
  81 + t -> {
  82 + ctx.tellFailure(msg, t);
  83 + scheduleTickMsg(ctx);
  84 + });
78 } 85 }
79 } 86 }
80 87
81 - private void sentTickMsg(TbContext ctx) { 88 + private void scheduleTickMsg(TbContext ctx) {
  89 + long curTs = System.currentTimeMillis();
  90 + if (lastScheduledTs == 0L) {
  91 + lastScheduledTs = curTs;
  92 + }
  93 + lastScheduledTs = lastScheduledTs + delay;
  94 + long curDelay = Math.max(0L, (lastScheduledTs - curTs));
82 TbMsg tickMsg = ctx.newMsg(TB_MSG_GENERATOR_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), ""); 95 TbMsg tickMsg = ctx.newMsg(TB_MSG_GENERATOR_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), "");
83 nextTickId = tickMsg.getId(); 96 nextTickId = tickMsg.getId();
84 - ctx.tellSelf(tickMsg, delay); 97 + ctx.tellSelf(tickMsg, curDelay);
85 } 98 }
86 99
87 private ListenableFuture<TbMsg> generate(TbContext ctx) { 100 private ListenableFuture<TbMsg> generate(TbContext ctx) {
88 return ctx.getJsExecutor().executeAsync(() -> { 101 return ctx.getJsExecutor().executeAsync(() -> {
89 if (prevMsg == null) { 102 if (prevMsg == null) {
90 - prevMsg = ctx.newMsg( "", originatorId, new TbMsgMetaData(), "{}"); 103 + prevMsg = ctx.newMsg("", originatorId, new TbMsgMetaData(), "{}");
91 } 104 }
92 TbMsg generated = jsEngine.executeGenerate(prevMsg); 105 TbMsg generated = jsEngine.executeGenerate(prevMsg);
93 prevMsg = ctx.newMsg(generated.getType(), originatorId, generated.getMetaData(), generated.getData()); 106 prevMsg = ctx.newMsg(generated.getType(), originatorId, generated.getMetaData(), generated.getData());
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.delay;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.rule.engine.api.RuleNode;
  20 +import org.thingsboard.rule.engine.api.TbContext;
  21 +import org.thingsboard.rule.engine.api.TbNode;
  22 +import org.thingsboard.rule.engine.api.TbNodeConfiguration;
  23 +import org.thingsboard.rule.engine.api.TbNodeException;
  24 +import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  25 +import org.thingsboard.server.common.data.plugin.ComponentType;
  26 +import org.thingsboard.server.common.msg.TbMsg;
  27 +import org.thingsboard.server.common.msg.TbMsgMetaData;
  28 +
  29 +import java.util.HashMap;
  30 +import java.util.Map;
  31 +import java.util.UUID;
  32 +import java.util.concurrent.TimeUnit;
  33 +
  34 +import static org.thingsboard.rule.engine.api.TbRelationTypes.FAILURE;
  35 +import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
  36 +
  37 +@Slf4j
  38 +@RuleNode(
  39 + type = ComponentType.ACTION,
  40 + name = "delay",
  41 + configClazz = TbMsgDelayNodeConfiguration.class,
  42 + nodeDescription = "Delays incoming message",
  43 + nodeDetails = "Delays messages for configurable period.",
  44 + icon = "pause",
  45 + uiResources = {"static/rulenode/rulenode-core-config.js"},
  46 + configDirective = "tbActionNodeMsgDelayConfig"
  47 +)
  48 +
  49 +public class TbMsgDelayNode implements TbNode {
  50 +
  51 + private static final String TB_MSG_DELAY_NODE_MSG = "TbMsgDelayNodeMsg";
  52 +
  53 + private TbMsgDelayNodeConfiguration config;
  54 + private long delay;
  55 + private Map<UUID, TbMsg> pendingMsgs;
  56 +
  57 + @Override
  58 + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
  59 + this.config = TbNodeUtils.convert(configuration, TbMsgDelayNodeConfiguration.class);
  60 + this.delay = TimeUnit.SECONDS.toMillis(config.getPeriodInSeconds());
  61 + this.pendingMsgs = new HashMap<>();
  62 + }
  63 +
  64 + @Override
  65 + public void onMsg(TbContext ctx, TbMsg msg) {
  66 + if (msg.getType().equals(TB_MSG_DELAY_NODE_MSG)) {
  67 + TbMsg pendingMsg = pendingMsgs.remove(UUID.fromString(msg.getData()));
  68 + if (pendingMsg != null) {
  69 + ctx.tellNext(pendingMsg, SUCCESS);
  70 + }
  71 + } else {
  72 + if(pendingMsgs.size() < config.getMaxPendingMsgs()) {
  73 + pendingMsgs.put(msg.getId(), msg);
  74 + TbMsg tickMsg = ctx.newMsg(TB_MSG_DELAY_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), msg.getId().toString());
  75 + ctx.tellSelf(tickMsg, delay);
  76 + } else {
  77 + ctx.tellNext(msg, FAILURE, new RuntimeException("Max limit of pending messages reached!"));
  78 + }
  79 + }
  80 + }
  81 +
  82 + @Override
  83 + public void destroy() {
  84 + pendingMsgs.clear();
  85 + }
  86 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.delay;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.rule.engine.api.NodeConfiguration;
  20 +import org.thingsboard.server.common.data.EntityType;
  21 +
  22 +@Data
  23 +public class TbMsgDelayNodeConfiguration implements NodeConfiguration<TbMsgDelayNodeConfiguration> {
  24 +
  25 + private int periodInSeconds;
  26 + private int maxPendingMsgs;
  27 +
  28 + @Override
  29 + public TbMsgDelayNodeConfiguration defaultConfiguration() {
  30 + TbMsgDelayNodeConfiguration configuration = new TbMsgDelayNodeConfiguration();
  31 + configuration.setPeriodInSeconds(60);
  32 + configuration.setMaxPendingMsgs(1000);
  33 + return configuration;
  34 + }
  35 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.filter;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  20 +import org.thingsboard.rule.engine.api.*;
  21 +import org.thingsboard.server.common.data.EntityType;
  22 +import org.thingsboard.server.common.data.plugin.ComponentType;
  23 +import org.thingsboard.server.common.msg.TbMsg;
  24 +
  25 +@Slf4j
  26 +@RuleNode(
  27 + type = ComponentType.FILTER,
  28 + name = "originator type",
  29 + configClazz = TbOriginatorTypeFilterNodeConfiguration.class,
  30 + relationTypes = {"True", "False"},
  31 + nodeDescription = "Filter incoming messages by message Originator Type",
  32 + nodeDetails = "If Originator Type of incoming message is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.",
  33 + uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
  34 + configDirective = "tbFilterNodeOriginatorTypeConfig")
  35 +public class TbOriginatorTypeFilterNode implements TbNode {
  36 +
  37 + TbOriginatorTypeFilterNodeConfiguration config;
  38 +
  39 + @Override
  40 + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
  41 + this.config = TbNodeUtils.convert(configuration, TbOriginatorTypeFilterNodeConfiguration.class);
  42 + }
  43 +
  44 + @Override
  45 + public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException {
  46 + EntityType originatorType = msg.getOriginator().getEntityType();
  47 + ctx.tellNext(msg, config.getOriginatorTypes().contains(originatorType) ? "True" : "False");
  48 + }
  49 +
  50 + @Override
  51 + public void destroy() {
  52 +
  53 + }
  54 +}
  1 +/**
  2 + * Copyright © 2016-2018 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.filter;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.rule.engine.api.NodeConfiguration;
  20 +import org.thingsboard.server.common.data.EntityType;
  21 +
  22 +import java.util.Arrays;
  23 +import java.util.List;
  24 +
  25 +@Data
  26 +public class TbOriginatorTypeFilterNodeConfiguration implements NodeConfiguration<TbOriginatorTypeFilterNodeConfiguration> {
  27 +
  28 + private List<EntityType> originatorTypes;
  29 +
  30 + @Override
  31 + public TbOriginatorTypeFilterNodeConfiguration defaultConfiguration() {
  32 + TbOriginatorTypeFilterNodeConfiguration configuration = new TbOriginatorTypeFilterNodeConfiguration();
  33 + configuration.setOriginatorTypes(Arrays.asList(
  34 + EntityType.DEVICE
  35 + ));
  36 + return configuration;
  37 + }
  38 +}
1 -!function(e){function t(a){if(n[a])return n[a].exports;var r=n[a]={exports:{},id:a,loaded:!1};return e[a].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),a=e[t[0]];return function(e,t,r){a.apply(this,[e,t,r].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(72)},function(e,t){},1,1,1,function(e,t){e.exports=' <section ng-form name=attributesConfigForm layout=column> <md-input-container class=md-block> <label translate>attribute.attributes-scope</label> <md-select ng-model=configuration.scope ng-disabled=$root.loading> <md-option ng-repeat="scope in types.attributesScope" ng-value=scope.value> {{scope.name | translate}} </md-option> </md-select> </md-input-container> </section> '},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <md-input-container class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <section layout=column layout-gt-sm=row> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-severity</label> <md-select required name=severity ng-model=configuration.severity> <md-option ng-repeat=\"(severityKey, severity) in types.alarmSeverity\" ng-value=severityKey> {{ severity.name | translate}} </md-option> </md-select> <div ng-messages=alarmConfigForm.severity.$error> <div ng-message=required translate>tb.rulenode.alarm-severity-required</div> </div> </md-input-container> </section> <md-checkbox aria-label=\"{{ 'tb.rulenode.propagate' | translate }}\" ng-model=configuration.propagate>{{ 'tb.rulenode.propagate' | translate }} </md-checkbox> </section> "},function(e,t){e.exports=" <section class=tb-generator-config ng-form name=generatorConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.message-count</label> <input ng-required=true type=number step=1 name=messageCount ng-model=configuration.msgCount min=0> <div ng-messages=generatorConfigForm.messageCount.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.message-count-required</div> <div ng-message=min translate>tb.rulenode.min-message-count-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=1> <div ng-messages=generatorConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-seconds-message</div> </div> </md-input-container> <div layout=column> <label class=tb-small>{{ 'tb.rulenode.originator' | translate }}</label> <tb-entity-select the-form=generatorConfigForm tb-required=false ng-model=originator> </tb-entity-select> </div> <label translate class=\"tb-title no-padding\">tb.rulenode.generate</label> <tb-js-func ng-model=configuration.jsScript function-name=Generate function-args=\"{{ ['prevMsg', 'prevMetadata', 'prevMsgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-generator-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section ng-form name=kafkaConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=kafkaConfigForm.topicPattern.$error> <div ng-message=required translate>tb.rulenode.topic-pattern-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bootstrap-servers</label> <input ng-required=true name=bootstrapServers ng-model=configuration.bootstrapServers> <div ng-messages=kafkaConfigForm.bootstrapServers.$error> <div ng-message=required translate>tb.rulenode.bootstrap-servers-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.retries</label> <input type=number step=1 name=retries ng-model=configuration.retries min=0> <div ng-messages=kafkaConfigForm.retries.$error> <div ng-message=min translate>tb.rulenode.min-retries-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.batch-size-bytes</label> <input type=number step=1 name=batchSize ng-model=configuration.batchSize min=0> <div ng-messages=kafkaConfigForm.batchSize.$error> <div ng-message=min translate>tb.rulenode.min-batch-size-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.linger-ms</label> <input type=number step=1 name=linger ng-model=configuration.linger min=0> <div ng-messages=kafkaConfigForm.linger.$error> <div ng-message=min translate>tb.rulenode.min-linger-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.buffer-memory-bytes</label> <input type=number step=1 name=bufferMemory ng-model=configuration.bufferMemory min=0> <div ng-messages=kafkaConfigForm.bufferMemory.$error> <div ng-message=min translate>tb.rulenode.min-buffer-memory-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.acks</label> <md-select ng-model=configuration.acks ng-disabled=$root.loading> <md-option ng-repeat="ackValue in ackValues" ng-value=ackValue> {{ ackValue }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.key-serializer</label> <input ng-required=true name=keySerializer ng-model=configuration.keySerializer> <div ng-messages=kafkaConfigForm.keySerializer.$error> <div ng-message=required translate>tb.rulenode.key-serializer-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.value-serializer</label> <input ng-required=true name=valueSerializer ng-model=configuration.valueSerializer> <div ng-messages=kafkaConfigForm.valueSerializer.$error> <div ng-message=required translate>tb.rulenode.value-serializer-required</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.other-properties</label> <tb-kv-map-config ng-model=configuration.otherProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.to-string</label> <tb-js-func ng-model=configuration.jsScript function-name=ToString function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-to-string-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-mqtt-config ng-form name=mqttConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=mqttConfigForm.topicPattern.$error> <div translate ng-message=required>tb.rulenode.topic-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.mqtt-topic-pattern-hint</div> </md-input-container> <div flex layout=column layout-gt-sm=row> <md-input-container flex=60 class=md-block> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=mqttConfigForm.host.$error> <div translate ng-message=required>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.port> <div ng-messages=mqttConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.port-required</div> <div translate ng-message=min>tb.rulenode.port-range</div> <div translate ng-message=max>tb.rulenode.port-range</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.connect-timeout</label> <input type=number step=1 min=1 max=200 ng-required=true name=connectTimeoutSec ng-model=configuration.connectTimeoutSec> <div ng-messages=mqttConfigForm.connectTimeoutSec.$error> <div translate ng-message=required>tb.rulenode.connect-timeout-required</div> <div translate ng-message=min>tb.rulenode.connect-timeout-range</div> <div translate ng-message=max>tb.rulenode.connect-timeout-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.client-id</label> <input name=clientId ng-model=configuration.clientId> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.clean-session\' | translate }}" ng-model=configuration.cleanSession> {{ \'tb.rulenode.clean-session\' | translate }} </md-checkbox> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-ssl\' | translate }}" ng-model=configuration.ssl> {{ \'tb.rulenode.enable-ssl\' | translate }} </md-checkbox> <md-expansion-panel-group class=tb-credentials-panel-group ng-class="{\'disabled\': $root.loading || readonly}" md-component-id=credentialsPanelGroup> <md-expansion-panel md-component-id=credentialsPanel> <md-expansion-panel-collapsed> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-collapsed> <md-expansion-panel-expanded> <md-expansion-panel-header ng-click="$mdExpansionPanel(\'credentialsPanel\').collapse()"> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-header> <md-expansion-panel-content> <div layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.credentials-type</label> <md-select ng-required=true name=credentialsType ng-model=configuration.credentials.type ng-disabled="$root.loading || readonly" ng-change=credentialsTypeChanged()> <md-option ng-repeat="(credentialsType, credentialsValue) in ruleNodeTypes.mqttCredentialTypes" ng-value=credentialsValue.value> {{credentialsValue.name | translate}} </md-option> </md-select> <div ng-messages=mqttConfigForm.credentialsType.$error> <div translate ng-message=required>tb.rulenode.credentials-type-required</div> </div> </md-input-container> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes.basic.value"> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input ng-required=true name=mqttUsername ng-model=configuration.credentials.username> <div ng-messages=mqttConfigForm.mqttUsername.$error> <div translate ng-message=required>tb.rulenode.username-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input type=password ng-required=true name=mqttPassword ng-model=configuration.credentials.password> <div ng-messages=mqttConfigForm.mqttPassword.$error> <div translate ng-message=required>tb.rulenode.password-required</div> </div> </md-input-container> </section> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes[\'cert.PEM\'].value" class=dropdown-section> <div class=tb-container ng-class="configuration.credentials.caCertFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.ca-cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'caCert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'caCert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=caCertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=caCertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.caCertFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.caCertFileName>{{configuration.credentials.caCertFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.certFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'Cert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'Cert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=CertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=CertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.certFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.certFileName>{{configuration.credentials.certFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.privateKeyFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.private-key</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'privateKey\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'privateKey\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=privateKeySelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=privateKeySelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.privateKeyFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.privateKeyFileName>{{configuration.credentials.privateKeyFileName}}</div> </div> <md-input-container class=md-block> <label translate>tb.rulenode.private-key-password</label> <input type=password name=privateKeyPassword ng-model=configuration.credentials.password> </md-input-container> </section> </div> </md-expansion-panel-content> </md-expansion-panel-expanded> </md-expansion-panel> </md-expansion-panel-group> </section>'},function(e,t){e.exports=' <section ng-form name=rabbitMqConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.exchange-name-pattern</label> <input name=exchangeNamePattern ng-model=configuration.exchangeNamePattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.routing-key-pattern</label> <input name=routingKeyPattern ng-model=configuration.routingKeyPattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.message-properties</label> <md-select ng-model=configuration.messageProperties ng-disabled="$root.loading || readonly"> <md-option ng-repeat="property in messageProperties" ng-value=property> {{ property }} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=rabbitMqConfigForm.host.$error> <div ng-message=required translate>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.port</label> <input ng-required=true type=number step=1 name=port ng-model=configuration.port min=0 max=65535> <div ng-messages=rabbitMqConfigForm.port.$error> <div ng-message=required translate>tb.rulenode.port-required</div> <div ng-message=min translate>tb.rulenode.port-range</div> <div ng-message=max translate>tb.rulenode.port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.virtual-host</label> <input name=virtualHost ng-model=configuration.virtualHost> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=virtualHost ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=virtualHost type=password ng-model=configuration.password> </md-input-container> <md-input-container class=md-block> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.automatic-recovery\' | translate }}" ng-model=ruleNode.automaticRecoveryEnabled>{{ \'tb.rulenode.automatic-recovery\' | translate }} </md-checkbox> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.connection-timeout-ms</label> <input type=number step=1 name=connectionTimeout ng-model=configuration.connectionTimeout min=0> <div ng-messages=rabbitMqConfigForm.connectionTimeout.$error> <div ng-message=min translate>tb.rulenode.min-connection-timeout-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.handshake-timeout-ms</label> <input type=number step=1 name=handshakeTimeout ng-model=configuration.handshakeTimeout min=0> <div ng-messages=rabbitMqConfigForm.handshakeTimeout.$error> <div ng-message=min translate>tb.rulenode.min-handshake-timeout-ms-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.client-properties</label> <tb-kv-map-config ng-model=configuration.clientProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=' <section ng-form name=restApiCallConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.endpoint-url-pattern</label> <input ng-required=true name=endpointUrlPattern ng-model=configuration.restEndpointUrlPattern> <div ng-messages=restApiCallConfigForm.endpointUrlPattern.$error> <div ng-message=required translate>tb.rulenode.endpoint-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.endpoint-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.request-method</label> <md-select ng-model=configuration.requestMethod ng-disabled=$root.loading> <md-option ng-repeat="type in ruleNodeTypes.httpRequestType" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <label translate class=tb-title>tb.rulenode.headers</label> <div class=tb-hint translate>tb.rulenode.headers-hint</div> <tb-kv-map-config ng-model=configuration.headers ng-required=false key-text="\'tb.rulenode.header\'" key-required-text="\'tb.rulenode.header-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section ng-form name=rpcReplyConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.request-id-metadata-attribute</label> <input name=requestIdMetaDataAttribute ng-model=configuration.requestIdMetaDataAttribute> </md-input-container> </section> "},function(e,t){e.exports=" <section ng-form name=rpcRequestConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-sec</label> <input ng-required=true type=number step=1 name=timeoutInSeconds ng-model=configuration.timeoutInSeconds min=0> <div ng-messages=rpcRequestConfigForm.timeoutInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.timeout-required</div> <div ng-message=min translate>tb.rulenode.min-timeout-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sendEmailConfigForm layout=column> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}" ng-model=configuration.useSystemSmtpSettings> {{ \'tb.rulenode.use-system-smtp-settings\' | translate }} </md-checkbox> <section layout=column ng-if=!configuration.useSystemSmtpSettings> <md-input-container class=md-block> <label translate>tb.rulenode.smtp-protocol</label> <md-select ng-disabled="$root.loading || readonly" ng-model=configuration.smtpProtocol> <md-option ng-repeat="smtpProtocol in smtpProtocols" value={{smtpProtocol}}> {{smtpProtocol.toUpperCase()}} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.smtp-host</label> <input ng-required=true name=smtpHost ng-model=configuration.smtpHost> <div ng-messages=sendEmailConfigForm.smtpHost.$error> <div translate ng-message=required>tb.rulenode.smtp-host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.smtp-port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.smtpPort> <div ng-messages=sendEmailConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.smtp-port-required</div> <div translate ng-message=min>tb.rulenode.smtp-port-range</div> <div translate ng-message=max>tb.rulenode.smtp-port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-msec</label> <input type=number step=1 min=0 ng-required=true name=timeout ng-model=configuration.timeout> <div ng-messages=sendEmailConfigForm.timeout.$error> <div translate ng-message=required>tb.rulenode.timeout-required</div> <div translate ng-message=min>tb.rulenode.min-timeout-msec-message</div> </div> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-tls\' | translate }}" ng-model=configuration.enableTls>{{ \'tb.rulenode.enable-tls\' | translate }}</md-checkbox> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=username placeholder="{{ \'tb.rulenode.enter-username\' | translate }}" ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=password placeholder="{{ \'tb.rulenode.enter-password\' | translate }}" type=password ng-model=configuration.password> </md-input-container> </section> </section> '},function(e,t){e.exports=" <section ng-form name=snsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-arn-pattern</label> <input ng-required=true name=topicArnPattern ng-model=configuration.topicArnPattern> <div ng-messages=snsConfigForm.topicArnPattern.$error> <div ng-message=required translate>tb.rulenode.topic-arn-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.topic-arn-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sqsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.queue-type</label> <md-select ng-model=configuration.queueType ng-disabled="$root.loading || readonly"> <md-option ng-repeat="type in ruleNodeTypes.sqsQueueType" ng-value=type.value> {{ type.name | translate }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.queue-url-pattern</label> <input ng-required=true name=queueUrlPattern ng-model=configuration.queueUrlPattern> <div ng-messages=sqsConfigForm.queueUrlPattern.$error> <div ng-message=required translate>tb.rulenode.queue-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.queue-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block ng-if="configuration.queueType == ruleNodeTypes.sqsQueueType.STANDARD.value"> <label translate>tb.rulenode.delay-seconds</label> <input type=number step=1 name=delaySeconds ng-model=configuration.delaySeconds min=0 max=900> <div ng-messages=sqsConfigForm.delaySeconds.$error> <div ng-message=min translate>tb.rulenode.min-delay-seconds-message</div> <div ng-message=max translate>tb.rulenode.max-delay-seconds-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.message-attributes</label> <div class=tb-hint translate>tb.rulenode.message-attributes-hint</div> <tb-kv-map-config ng-model=configuration.messageAttributes ng-required=false key-text="\'tb.rulenode.name\'" key-required-text="\'tb.rulenode.name-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> '},function(e,t){e.exports=" <section ng-form name=timeseriesConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.default-ttl</label> <input ng-required=true type=number step=1 name=defaultTTL ng-model=configuration.defaultTTL min=0> <div ng-messages=timeseriesConfigForm.defaultTTL.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.default-ttl-required</div> <div ng-message=min translate>tb.rulenode.min-default-ttl-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat="direction in types.entitySearchDirection" ng-value=direction> {{ (\'relation.search-direction.\' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder="{{ \'tb.rulenode.unlimited-level\' | translate }}" ng-model=query.maxLevel aria-label="{{ \'tb.rulenode.max-relation-level\' | translate }}"> </md-input-container> </div> <div class=md-caption style=color:rgba(0,0,0,.57) translate>relation.relation-type</div> <tb-relation-type-autocomplete flex hide-label ng-model=query.relationType tb-required=false> </tb-relation-type-autocomplete> <div class="md-caption tb-required" style=color:rgba(0,0,0,.57) translate>device.device-types</div> <tb-entity-subtype-list tb-required=true entity-type=types.entityType.device ng-model=query.deviceTypes> </tb-entity-subtype-list> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> ";  
2 -},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.device-relations-query</label> <tb-device-relations-query-config style=padding-bottom:15px ng-model=configuration.deviceRelationsQuery> </tb-device-relations-query-config> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.fields-mapping</label> <tb-kv-map-config ng-model=configuration.fieldsMapping ng-required=true required-text="\'tb.rulenode.fields-mapping-required\'" key-text="\'tb.rulenode.source-field\'" key-required-text="\'tb.rulenode.source-field-required\'" val-text="\'tb.rulenode.target-attribute\'" val-required-text="\'tb.rulenode.target-attribute-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},21,function(e,t){e.exports=" <section ng-form name=checkRelationConfigForm> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=configuration.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <div layout=row class=tb-entity-select> <tb-entity-type-select style=min-width:100px the-form=checkRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> <tb-entity-autocomplete flex ng-if=configuration.entityType the-form=checkRelationConfigForm tb-required=true entity-type=configuration.entityType ng-model=configuration.entityId> </tb-entity-autocomplete> </div> <tb-relation-type-autocomplete hide-label ng-model=configuration.relationType tb-required=true> </tb-relation-type-autocomplete> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" ng-class="{\'tb-required\': required}">tb.rulenode.message-types-filter</label> <md-chips id=message_type_chips ng-required=required readonly=readonly ng-model=messageTypes md-autocomplete-snap md-transform-chip=transformMessageTypeChip($chip) md-require-match=false> <md-autocomplete id=message_type md-no-cache=true md-selected-item=selectedMessageType md-search-text=messageTypeSearchText md-items="item in messageTypesSearch(messageTypeSearchText)" md-item-text=item.name md-min-length=0 placeholder="{{\'tb.rulenode.message-type\' | translate }}" md-menu-class=tb-message-type-autocomplete> <span md-highlight-text=messageTypeSearchText md-highlight-flags=^i>{{item}}</span> <md-not-found> <div class=tb-not-found> <div class=tb-no-entries ng-if="!messageTypeSearchText || !messageTypeSearchText.length"> <span translate>tb.rulenode.no-message-types-found</span> </div> <div ng-if="messageTypeSearchText && messageTypeSearchText.length"> <span translate translate-values=\'{ messageType: "{{messageTypeSearchText | truncate:true:6:&apos;...&apos;}}" }\'>tb.rulenode.no-message-type-matching</span> <span> <a translate ng-click="createMessageType($event, \'#message_type_chips\')">tb.rulenode.create-new-message-type</a> </span> </div> </div> </md-not-found> </md-autocomplete> <md-chip-template> <span>{{$chip.name}}</span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=messageTypes class=tb-error-message>tb.rulenode.message-types-required</div> </div> </section>'},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-filter-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.switch</label> <tb-js-func ng-model=configuration.jsScript function-name=Switch function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-switch-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-kv-map-config layout=column> <div class=header flex layout=row> <span class=cell flex translate>{{ keyText }}</span> <span class=cell flex translate>{{ valText }}</span> <span ng-show=!disabled style=width:52px>&nbsp</span> </div> <div class=body> <div class=row ng-form name=kvForm flex layout=row layout-align="start center" ng-repeat="keyVal in kvList track by $index"> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ keyText | translate }}" ng-required=true name=key ng-model=keyVal.key> <div ng-messages=kvForm.key.$error> <div translate ng-message=required>{{keyRequiredText}}</div> </div> </md-input-container> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ valText | translate }}" ng-required=true name=value ng-model=keyVal.value> <div ng-messages=kvForm.value.$error> <div translate ng-message=required>{{valRequiredText}}</div> </div> </md-input-container> <md-button ng-show=!disabled ng-disabled=loading class="md-icon-button md-primary" ng-click=removeKeyVal($index) aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.remove-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.delete\' | translate }}" class=material-icons> close </md-icon> </md-button> </div> </div> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=kvMap class=tb-error-message>{{requiredText}}</div> </div> <div> <md-button ng-show=!disabled ng-disabled=loading class="md-primary md-raised" ng-click=addKeyVal() aria-label="{{ \'action.add\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.add-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.add\' | translate }}" class=material-icons> add </md-icon> {{ \'action.add\' | translate }} </md-button> </div> </section> '},function(e,t){e.exports=" <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder=\"{{ 'tb.rulenode.unlimited-level' | translate }}\" ng-model=query.maxLevel aria-label=\"{{ 'tb.rulenode.max-relation-level' | translate }}\"> </md-input-container> </div> <div class=md-caption style=padding-bottom:10px;color:rgba(0,0,0,.57) translate>relation.relation-filters</div> <tb-relation-filters ng-model=query.filters> </tb-relation-filters> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.originator-source</label> <md-select required ng-model=configuration.originatorSource> <md-option ng-repeat="source in ruleNodeTypes.originatorSource" ng-value=source.value> {{ source.name | translate}} </md-option> </md-select> </md-input-container> <section layout=column ng-if="configuration.originatorSource == ruleNodeTypes.originatorSource.RELATED.value"> <label translate class="tb-title tb-required">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> </section> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.transform</label> <tb-js-func ng-model=configuration.jsScript function-name=Transform function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-transformer-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section ng-form name=toEmailConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.from-template</label> <textarea ng-required=true name=fromTemplate ng-model=configuration.fromTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.fromTemplate.$error> <div ng-message=required translate>tb.rulenode.from-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.from-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.to-template</label> <textarea ng-required=true name=toTemplate ng-model=configuration.toTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.toTemplate.$error> <div ng-message=required translate>tb.rulenode.to-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.cc-template</label> <textarea name=ccTemplate ng-model=configuration.ccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bcc-template</label> <textarea name=ccTemplate ng-model=configuration.bccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.subject-template</label> <textarea ng-required=true name=subjectTemplate ng-model=configuration.subjectTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.subjectTemplate.$error> <div ng-message=required translate>tb.rulenode.subject-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.subject-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.body-template</label> <textarea ng-required=true name=bodyTemplate ng-model=configuration.bodyTemplate rows=6></textarea> <div ng-messages=toEmailConfigForm.bodyTemplate.$error> <div ng-message=required translate>tb.rulenode.body-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.body-template-hint</div> </md-input-container> </section> "},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(5),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(6),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue},r.testDetailsBuildJs=function(e){var n=angular.copy(r.configuration.alarmDetailsBuildJs);a.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(7),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n,a){var r=function(r,i,l,s){var u=o.default;i.html(u),r.types=n,r.originator=null,r.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(r.configuration)}),s.$render=function(){r.configuration=s.$viewValue,r.configuration.originatorId&&r.configuration.originatorType?r.originator={id:r.configuration.originatorId,entityType:r.configuration.originatorType}:r.originator=null,r.$watch("originator",function(e,t){angular.equals(e,t)||(r.originator?(s.$viewValue.originatorId=r.originator.id,s.$viewValue.originatorType=r.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},r.testScript=function(e){var n=angular.copy(r.configuration.jsScript);a.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}r.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(1);var i=n(8),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(51),i=a(r),o=n(36),l=a(o),s=n(39),u=a(s),d=n(38),c=a(d),m=n(37),g=a(m),p=n(42),f=a(p),b=n(46),v=a(b),y=n(47),q=a(y),h=n(45),T=a(h),$=n(41),k=a($),w=n(49),C=a(w),_=n(50),x=a(_),E=n(44),M=a(E),S=n(43),N=a(S),V=n(48),P=a(V);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",u.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",q.default).directive("tbActionNodeRestApiCallConfig",T.default).directive("tbActionNodeKafkaConfig",k.default).directive("tbActionNodeSnsConfig",C.default).directive("tbActionNodeSqsConfig",x.default).directive("tbActionNodeRabbitMqConfig",M.default).directive("tbActionNodeMqttConfig",N.default).directive("tbActionNodeSendEmailConfig",P.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(9),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(10),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$mdExpansionPanel=t,a.ruleNodeTypes=n,a.credentialsTypeChanged=function(){var e=a.configuration.credentials.type;a.configuration.credentials={},a.configuration.credentials.type=e,a.updateValidity()},a.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){a.$apply(function(){if(n.target.result){l.$setDirty();var r=n.target.result;r&&r.length>0&&("caCert"==t&&(a.configuration.credentials.caCertFileName=e.name,a.configuration.credentials.caCert=r),"privateKey"==t&&(a.configuration.credentials.privateKeyFileName=e.name,a.configuration.credentials.privateKey=r),"Cert"==t&&(a.configuration.credentials.certFileName=e.name,a.configuration.credentials.cert=r)),a.updateValidity()}})},n.readAsText(e.file)},a.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(a.configuration.credentials.caCertFileName=null,a.configuration.credentials.caCert=null),"privateKey"==e&&(a.configuration.credentials.privateKeyFileName=null,a.configuration.credentials.privateKey=null),"Cert"==e&&(a.configuration.credentials.certFileName=null,a.configuration.credentials.cert=null),a.updateValidity()},a.updateValidity=function(){var e=!0,t=a.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(2);var i=n(11),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(12),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(13),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(14),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(15),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(16),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(17),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(18),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(19),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(20),o=a(i)},function(e,t){"use strict";function n(e){var t=function(t,n,a,r){n.html("<div></div>"),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(21),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(22),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(57),i=a(r),o=n(58),l=a(o),s=n(55),u=a(s),d=n(59),c=a(d),m=n(54),g=a(m),p=n(60),f=a(p);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",u.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(23),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(24),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(26),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(27),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(64),i=a(r),o=n(63),l=a(o),s=n(65),u=a(s),d=n(61),c=a(d);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",u.default).directive("tbFilterNodeCheckRelationConfig",c.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t<a.messageTypes.length;t++)e.push(a.messageTypes[t].value);l.$viewValue.messageTypes=e,u()}}function u(){if(a.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var d=o.default;r.html(d),a.selectedMessageType=null,a.messageTypeSearchText=null,a.ngModelCtrl=l;var c=[];for(var m in n.messageType){var g={name:n.messageType[m].name,value:n.messageType[m].value};c.push(g)}a.transformMessageTypeChip=function(e){  
3 -var n,a=t("filter")(c,{name:e},!0);return n=a&&a.length?angular.copy(a[0]):{name:e,value:e}},a.messageTypesSearch=function(e){var n=e?t("filter")(c,{name:e}):c;return n.map(function(e){return e.name})},a.createMessageType=function(e,t){var n=angular.element(t,r)[0].firstElementChild,a=angular.element(n),i=a.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),a.scope().$mdChipsCtrl.appendChip(i.trim()),a.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var r=0;r<e.messageTypes.length;r++){var i=e.messageTypes[r];n.messageType[i]?t.push(angular.copy(n.messageType[i])):t.push({name:i,value:i})}a.messageTypes=t,a.$watch("messageTypes",function(e,t){angular.equals(e,t)||s()},!0)},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:a}}r.$inject=["$compile","$filter","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(3);var i=n(28),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"filter",t.instant("tb.rulenode.filter")+"","Filter",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(29),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"switch",t.instant("tb.rulenode.switch")+"","Switch",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(30),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){function i(e){e>-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),r.$setViewValue(e),u()}function u(){var e=!0;t.required&&!t.kvList.length&&(e=!1),r.$setValidity("kvMap",e)}var d=o.default;n.html(d),t.ngModelCtrl=r,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||r.$setViewValue(t.query)}),r.$render=function(){if(r.$viewValue){var e=r.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),u()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(31),o=a(i);n(4)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(32),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){var n=function(n,a,r,i){var l=o.default;a.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(a.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}r.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(33),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(68),i=a(r),o=n(70),l=a(o),s=n(71),u=a(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",u.default).name},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t,n){var a=function(a,r,i,l){var s=o.default;r.html(s),a.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(a.configuration)}),l.$render=function(){a.configuration=l.$viewValue},a.testScript=function(e){var r=angular.copy(a.configuration.jsScript);n.testNodeScript(e,r,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,l.$setDirty()})},e(r.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}r.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(34),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e){var t=function(t,n,a,r){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||r.$setViewValue(t.configuration)}),r.$render=function(){t.configuration=r.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}r.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(35),o=a(i)},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var r=n(75),i=a(r),o=n(62),l=a(o),s=n(56),u=a(s),d=n(69),c=a(d),m=n(40),g=a(m),p=n(53),f=a(p),b=n(67),v=a(b),y=n(52),q=a(y),h=n(66),T=a(h),$=n(74),k=a($);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,u.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",q.default).directive("tbKvMapConfig",T.default).config(k.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use <code>${metaKeyName}</code> to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use <code>${metaKeyName}</code> to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use <code>${metaKeyName}</code> to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use <code>${metaKeyName}</code> to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","request-method":"Request method",headers:"Headers","headers-hint":"Use <code>${metaKeyName}</code> in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use <code>${metaKeyName}</code> in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};angular.merge(e.en_US,t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function a(e){return e&&e.__esModule?e:{default:e}}function r(e,t){(0,o.default)(t);for(var n in t){var a=t[n];e.translations(n,a)}}r.$inject=["$translateProvider","locales"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(73),o=a(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{messageType:{POST_ATTRIBUTES_REQUEST:{name:"Post attributes",value:"POST_ATTRIBUTES_REQUEST"},POST_TELEMETRY_REQUEST:{name:"Post telemetry",value:"POST_TELEMETRY_REQUEST"},TO_SERVER_RPC_REQUEST:{name:"RPC Request from Device",value:"TO_SERVER_RPC_REQUEST"},RPC_CALL_FROM_SERVER_TO_DEVICE:{name:"RPC Request to Device",value:"RPC_CALL_FROM_SERVER_TO_DEVICE"},ACTIVITY_EVENT:{name:"Activity Event",value:"ACTIVITY_EVENT"},INACTIVITY_EVENT:{name:"Inactivity Event",value:"INACTIVITY_EVENT"},CONNECT_EVENT:{name:"Connect Event",value:"CONNECT_EVENT"},DISCONNECT_EVENT:{name:"Disconnect Event",value:"DISCONNECT_EVENT"},ENTITY_CREATED:{name:"Entity Created",value:"ENTITY_CREATED"},ENTITY_UPDATED:{name:"Entity Updated",value:"ENTITY_UPDATED"},ENTITY_DELETED:{name:"Entity Deleted",value:"ENTITY_DELETED"},ENTITY_ASSIGNED:{name:"Entity Assigned",value:"ENTITY_ASSIGNED"},ENTITY_UNASSIGNED:{name:"Entity Unassigned",value:"ENTITY_UNASSIGNED"},ATTRIBUTES_UPDATED:{name:"Attributes Updated",value:"ATTRIBUTES_UPDATED"},ATTRIBUTES_DELETED:{name:"Attributes Deleted",value:"ATTRIBUTES_DELETED"}},originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); 1 +!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={exports:{},id:r,loaded:!1};return e[r].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}(function(e){for(var t in e)if(Object.prototype.hasOwnProperty.call(e,t))switch(typeof e[t]){case"function":break;case"object":e[t]=function(t){var n=t.slice(1),r=e[t[0]];return function(e,t,a){r.apply(this,[e,t,a].concat(n))}}(e[t]);break;default:e[t]=e[e[t]]}return e}([function(e,t,n){e.exports=n(76)},function(e,t){},1,1,1,function(e,t){e.exports=' <section ng-form name=attributesConfigForm layout=column> <md-input-container class=md-block> <label translate>attribute.attributes-scope</label> <md-select ng-model=configuration.scope ng-disabled=$root.loading> <md-option ng-repeat="scope in types.attributesScope" ng-value=scope.value> {{scope.name | translate}} </md-option> </md-select> </md-input-container> </section> '},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <md-input-container class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=" <section class=tb-alarm-config ng-form name=alarmConfigForm layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.alarm-details-builder</label> <tb-js-func ng-model=configuration.alarmDetailsBuildJs function-name=Details function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testDetailsBuildJs($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-details-function' | translate }} </md-button> </div> <section layout=column layout-gt-sm=row> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-type</label> <input ng-required=true name=alarmType ng-model=configuration.alarmType> <div ng-messages=alarmConfigForm.alarmType.$error> <div ng-message=required translate>tb.rulenode.alarm-type-required</div> </div> </md-input-container> <md-input-container flex class=md-block> <label translate>tb.rulenode.alarm-severity</label> <md-select required name=severity ng-model=configuration.severity> <md-option ng-repeat=\"(severityKey, severity) in types.alarmSeverity\" ng-value=severityKey> {{ severity.name | translate}} </md-option> </md-select> <div ng-messages=alarmConfigForm.severity.$error> <div ng-message=required translate>tb.rulenode.alarm-severity-required</div> </div> </md-input-container> </section> <md-checkbox aria-label=\"{{ 'tb.rulenode.propagate' | translate }}\" ng-model=configuration.propagate>{{ 'tb.rulenode.propagate' | translate }} </md-checkbox> </section> "},function(e,t){e.exports=" <section class=tb-generator-config ng-form name=generatorConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.message-count</label> <input ng-required=true type=number step=1 name=messageCount ng-model=configuration.msgCount min=0> <div ng-messages=generatorConfigForm.messageCount.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.message-count-required</div> <div ng-message=min translate>tb.rulenode.min-message-count-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=1> <div ng-messages=generatorConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-seconds-message</div> </div> </md-input-container> <div layout=column> <label class=tb-small>{{ 'tb.rulenode.originator' | translate }}</label> <tb-entity-select the-form=generatorConfigForm tb-required=false ng-model=originator> </tb-entity-select> </div> <label translate class=\"tb-title no-padding\">tb.rulenode.generate</label> <tb-js-func ng-model=configuration.jsScript function-name=Generate function-args=\"{{ ['prevMsg', 'prevMetadata', 'prevMsgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-generator-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section ng-form name=kafkaConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=kafkaConfigForm.topicPattern.$error> <div ng-message=required translate>tb.rulenode.topic-pattern-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bootstrap-servers</label> <input ng-required=true name=bootstrapServers ng-model=configuration.bootstrapServers> <div ng-messages=kafkaConfigForm.bootstrapServers.$error> <div ng-message=required translate>tb.rulenode.bootstrap-servers-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.retries</label> <input type=number step=1 name=retries ng-model=configuration.retries min=0> <div ng-messages=kafkaConfigForm.retries.$error> <div ng-message=min translate>tb.rulenode.min-retries-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.batch-size-bytes</label> <input type=number step=1 name=batchSize ng-model=configuration.batchSize min=0> <div ng-messages=kafkaConfigForm.batchSize.$error> <div ng-message=min translate>tb.rulenode.min-batch-size-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.linger-ms</label> <input type=number step=1 name=linger ng-model=configuration.linger min=0> <div ng-messages=kafkaConfigForm.linger.$error> <div ng-message=min translate>tb.rulenode.min-linger-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.buffer-memory-bytes</label> <input type=number step=1 name=bufferMemory ng-model=configuration.bufferMemory min=0> <div ng-messages=kafkaConfigForm.bufferMemory.$error> <div ng-message=min translate>tb.rulenode.min-buffer-memory-bytes-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.acks</label> <md-select ng-model=configuration.acks ng-disabled=$root.loading> <md-option ng-repeat="ackValue in ackValues" ng-value=ackValue> {{ ackValue }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.key-serializer</label> <input ng-required=true name=keySerializer ng-model=configuration.keySerializer> <div ng-messages=kafkaConfigForm.keySerializer.$error> <div ng-message=required translate>tb.rulenode.key-serializer-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.value-serializer</label> <input ng-required=true name=valueSerializer ng-model=configuration.valueSerializer> <div ng-messages=kafkaConfigForm.valueSerializer.$error> <div ng-message=required translate>tb.rulenode.value-serializer-required</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.other-properties</label> <tb-kv-map-config ng-model=configuration.otherProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.to-string</label> <tb-js-func ng-model=configuration.jsScript function-name=ToString function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-to-string-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-mqtt-config ng-form name=mqttConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-pattern</label> <input ng-required=true name=topicPattern ng-model=configuration.topicPattern> <div ng-messages=mqttConfigForm.topicPattern.$error> <div translate ng-message=required>tb.rulenode.topic-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.mqtt-topic-pattern-hint</div> </md-input-container> <div flex layout=column layout-gt-sm=row> <md-input-container flex=60 class=md-block> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=mqttConfigForm.host.$error> <div translate ng-message=required>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.port> <div ng-messages=mqttConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.port-required</div> <div translate ng-message=min>tb.rulenode.port-range</div> <div translate ng-message=max>tb.rulenode.port-range</div> </div> </md-input-container> <md-input-container flex=40 class=md-block> <label translate>tb.rulenode.connect-timeout</label> <input type=number step=1 min=1 max=200 ng-required=true name=connectTimeoutSec ng-model=configuration.connectTimeoutSec> <div ng-messages=mqttConfigForm.connectTimeoutSec.$error> <div translate ng-message=required>tb.rulenode.connect-timeout-required</div> <div translate ng-message=min>tb.rulenode.connect-timeout-range</div> <div translate ng-message=max>tb.rulenode.connect-timeout-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.client-id</label> <input name=clientId ng-model=configuration.clientId> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.clean-session\' | translate }}" ng-model=configuration.cleanSession> {{ \'tb.rulenode.clean-session\' | translate }} </md-checkbox> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-ssl\' | translate }}" ng-model=configuration.ssl> {{ \'tb.rulenode.enable-ssl\' | translate }} </md-checkbox> <md-expansion-panel-group class=tb-credentials-panel-group ng-class="{\'disabled\': $root.loading || readonly}" md-component-id=credentialsPanelGroup> <md-expansion-panel md-component-id=credentialsPanel> <md-expansion-panel-collapsed> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-collapsed> <md-expansion-panel-expanded> <md-expansion-panel-header ng-click="$mdExpansionPanel(\'credentialsPanel\').collapse()"> <div class=tb-panel-title>{{ \'tb.rulenode.credentials\' | translate }}</div> <div class=tb-panel-prompt>{{ ruleNodeTypes.mqttCredentialTypes[configuration.credentials.type].name | translate }}</div> <span flex></span> <md-expansion-panel-icon></md-expansion-panel-icon> </md-expansion-panel-header> <md-expansion-panel-content> <div layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.credentials-type</label> <md-select ng-required=true name=credentialsType ng-model=configuration.credentials.type ng-disabled="$root.loading || readonly" ng-change=credentialsTypeChanged()> <md-option ng-repeat="(credentialsType, credentialsValue) in ruleNodeTypes.mqttCredentialTypes" ng-value=credentialsValue.value> {{credentialsValue.name | translate}} </md-option> </md-select> <div ng-messages=mqttConfigForm.credentialsType.$error> <div translate ng-message=required>tb.rulenode.credentials-type-required</div> </div> </md-input-container> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes.basic.value"> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input ng-required=true name=mqttUsername ng-model=configuration.credentials.username> <div ng-messages=mqttConfigForm.mqttUsername.$error> <div translate ng-message=required>tb.rulenode.username-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input type=password ng-required=true name=mqttPassword ng-model=configuration.credentials.password> <div ng-messages=mqttConfigForm.mqttPassword.$error> <div translate ng-message=required>tb.rulenode.password-required</div> </div> </md-input-container> </section> <section flex layout=column ng-if="configuration.credentials.type == ruleNodeTypes.mqttCredentialTypes[\'cert.PEM\'].value" class=dropdown-section> <div class=tb-container ng-class="configuration.credentials.caCertFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.ca-cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'caCert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'caCert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=caCertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=caCertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.caCertFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.caCertFileName>{{configuration.credentials.caCertFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.certFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.cert</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'Cert\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'Cert\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=CertSelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=CertSelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.certFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.certFileName>{{configuration.credentials.certFileName}}</div> </div> <div class=tb-container ng-class="configuration.credentials.privateKeyFileName ? \'ng-valid\' : \'ng-invalid\'"> <label class=tb-label translate>tb.rulenode.private-key</label> <div flow-init={singleFile:true} flow-file-added="certFileAdded($file, \'privateKey\')" class=tb-file-select-container> <div class=tb-file-clear-container> <md-button ng-click="clearCertFile(\'privateKey\')" class="tb-file-clear-btn md-icon-button md-primary" aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'action.remove\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.remove\' | translate }}" class=material-icons>close</md-icon> </md-button> </div> <div class="alert tb-flow-drop" flow-drop> <label for=privateKeySelect translate>tb.rulenode.drop-file</label> <input class=file-input flow-btn id=privateKeySelect> </div> </div> </div> <div class=dropdown-messages> <div ng-if=!configuration.credentials.privateKeyFileName class=tb-error-message translate>tb.rulenode.no-file</div> <div ng-if=configuration.credentials.privateKeyFileName>{{configuration.credentials.privateKeyFileName}}</div> </div> <md-input-container class=md-block> <label translate>tb.rulenode.private-key-password</label> <input type=password name=privateKeyPassword ng-model=configuration.credentials.password> </md-input-container> </section> </div> </md-expansion-panel-content> </md-expansion-panel-expanded> </md-expansion-panel> </md-expansion-panel-group> </section>'},function(e,t){e.exports=" <section ng-form name=msgDelayConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.period-seconds</label> <input ng-required=true type=number step=1 name=periodInSeconds ng-model=configuration.periodInSeconds min=0> <div ng-messages=msgDelayConfigForm.periodInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.period-seconds-required</div> <div ng-message=min translate>tb.rulenode.min-period-0-seconds-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-pending-messages</label> <input ng-required=true type=number step=1 name=maxPendingMsgs ng-model=configuration.maxPendingMsgs min=1 max=100000> <div ng-messages=msgDelayConfigForm.maxPendingMsgs.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.max-pending-messages-required</div> <div ng-message=min translate>tb.rulenode.max-pending-messages-range</div> <div ng-message=max translate>tb.rulenode.max-pending-messages-range</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=rabbitMqConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.exchange-name-pattern</label> <input name=exchangeNamePattern ng-model=configuration.exchangeNamePattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.routing-key-pattern</label> <input name=routingKeyPattern ng-model=configuration.routingKeyPattern> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.message-properties</label> <md-select ng-model=configuration.messageProperties ng-disabled="$root.loading || readonly"> <md-option ng-repeat="property in messageProperties" ng-value=property> {{ property }} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.host</label> <input ng-required=true name=host ng-model=configuration.host> <div ng-messages=rabbitMqConfigForm.host.$error> <div ng-message=required translate>tb.rulenode.host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.port</label> <input ng-required=true type=number step=1 name=port ng-model=configuration.port min=0 max=65535> <div ng-messages=rabbitMqConfigForm.port.$error> <div ng-message=required translate>tb.rulenode.port-required</div> <div ng-message=min translate>tb.rulenode.port-range</div> <div ng-message=max translate>tb.rulenode.port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.virtual-host</label> <input name=virtualHost ng-model=configuration.virtualHost> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=virtualHost ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=virtualHost type=password ng-model=configuration.password> </md-input-container> <md-input-container class=md-block> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.automatic-recovery\' | translate }}" ng-model=ruleNode.automaticRecoveryEnabled>{{ \'tb.rulenode.automatic-recovery\' | translate }} </md-checkbox> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.connection-timeout-ms</label> <input type=number step=1 name=connectionTimeout ng-model=configuration.connectionTimeout min=0> <div ng-messages=rabbitMqConfigForm.connectionTimeout.$error> <div ng-message=min translate>tb.rulenode.min-connection-timeout-ms-message</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.handshake-timeout-ms</label> <input type=number step=1 name=handshakeTimeout ng-model=configuration.handshakeTimeout min=0> <div ng-messages=rabbitMqConfigForm.handshakeTimeout.$error> <div ng-message=min translate>tb.rulenode.min-handshake-timeout-ms-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.client-properties</label> <tb-kv-map-config ng-model=configuration.clientProperties ng-required=false key-text="\'tb.rulenode.key\'" key-required-text="\'tb.rulenode.key-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=' <section ng-form name=restApiCallConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.endpoint-url-pattern</label> <input ng-required=true name=endpointUrlPattern ng-model=configuration.restEndpointUrlPattern> <div ng-messages=restApiCallConfigForm.endpointUrlPattern.$error> <div ng-message=required translate>tb.rulenode.endpoint-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.endpoint-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.request-method</label> <md-select ng-model=configuration.requestMethod ng-disabled=$root.loading> <md-option ng-repeat="type in ruleNodeTypes.httpRequestType" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <label translate class=tb-title>tb.rulenode.headers</label> <div class=tb-hint translate>tb.rulenode.headers-hint</div> <tb-kv-map-config ng-model=configuration.headers ng-required=false key-text="\'tb.rulenode.header\'" key-required-text="\'tb.rulenode.header-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section ng-form name=rpcReplyConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.request-id-metadata-attribute</label> <input name=requestIdMetaDataAttribute ng-model=configuration.requestIdMetaDataAttribute> </md-input-container> </section> "},function(e,t){e.exports=" <section ng-form name=rpcRequestConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-sec</label> <input ng-required=true type=number step=1 name=timeoutInSeconds ng-model=configuration.timeoutInSeconds min=0> <div ng-messages=rpcRequestConfigForm.timeoutInSeconds.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.timeout-required</div> <div ng-message=min translate>tb.rulenode.min-timeout-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sendEmailConfigForm layout=column> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.use-system-smtp-settings\' | translate }}" ng-model=configuration.useSystemSmtpSettings> {{ \'tb.rulenode.use-system-smtp-settings\' | translate }} </md-checkbox> <section layout=column ng-if=!configuration.useSystemSmtpSettings> <md-input-container class=md-block> <label translate>tb.rulenode.smtp-protocol</label> <md-select ng-disabled="$root.loading || readonly" ng-model=configuration.smtpProtocol> <md-option ng-repeat="smtpProtocol in smtpProtocols" value={{smtpProtocol}}> {{smtpProtocol.toUpperCase()}} </md-option> </md-select> </md-input-container> <div layout-gt-sm=row> <md-input-container class=md-block flex=100 flex-gt-sm=60> <label translate>tb.rulenode.smtp-host</label> <input ng-required=true name=smtpHost ng-model=configuration.smtpHost> <div ng-messages=sendEmailConfigForm.smtpHost.$error> <div translate ng-message=required>tb.rulenode.smtp-host-required</div> </div> </md-input-container> <md-input-container class=md-block flex=100 flex-gt-sm=40> <label translate>tb.rulenode.smtp-port</label> <input type=number step=1 min=1 max=65535 ng-required=true name=port ng-model=configuration.smtpPort> <div ng-messages=sendEmailConfigForm.port.$error> <div translate ng-message=required>tb.rulenode.smtp-port-required</div> <div translate ng-message=min>tb.rulenode.smtp-port-range</div> <div translate ng-message=max>tb.rulenode.smtp-port-range</div> </div> </md-input-container> </div> <md-input-container class=md-block> <label translate>tb.rulenode.timeout-msec</label> <input type=number step=1 min=0 ng-required=true name=timeout ng-model=configuration.timeout> <div ng-messages=sendEmailConfigForm.timeout.$error> <div translate ng-message=required>tb.rulenode.timeout-required</div> <div translate ng-message=min>tb.rulenode.min-timeout-msec-message</div> </div> </md-input-container> <md-checkbox ng-disabled="$root.loading || readonly" aria-label="{{ \'tb.rulenode.enable-tls\' | translate }}" ng-model=configuration.enableTls>{{ \'tb.rulenode.enable-tls\' | translate }}</md-checkbox> <md-input-container class=md-block> <label translate>tb.rulenode.username</label> <input name=username placeholder="{{ \'tb.rulenode.enter-username\' | translate }}" ng-model=configuration.username> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.password</label> <input name=password placeholder="{{ \'tb.rulenode.enter-password\' | translate }}" type=password ng-model=configuration.password> </md-input-container> </section> </section> '},function(e,t){e.exports=" <section ng-form name=snsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.topic-arn-pattern</label> <input ng-required=true name=topicArnPattern ng-model=configuration.topicArnPattern> <div ng-messages=snsConfigForm.topicArnPattern.$error> <div ng-message=required translate>tb.rulenode.topic-arn-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.topic-arn-pattern-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section ng-form name=sqsConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.queue-type</label> <md-select ng-model=configuration.queueType ng-disabled="$root.loading || readonly"> <md-option ng-repeat="type in ruleNodeTypes.sqsQueueType" ng-value=type.value> {{ type.name | translate }} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.queue-url-pattern</label> <input ng-required=true name=queueUrlPattern ng-model=configuration.queueUrlPattern> <div ng-messages=sqsConfigForm.queueUrlPattern.$error> <div ng-message=required translate>tb.rulenode.queue-url-pattern-required</div> </div> <div class=tb-hint translate>tb.rulenode.queue-url-pattern-hint</div> </md-input-container> <md-input-container class=md-block ng-if="configuration.queueType == ruleNodeTypes.sqsQueueType.STANDARD.value"> <label translate>tb.rulenode.delay-seconds</label> <input type=number step=1 name=delaySeconds ng-model=configuration.delaySeconds min=0 max=900> <div ng-messages=sqsConfigForm.delaySeconds.$error> <div ng-message=min translate>tb.rulenode.min-delay-seconds-message</div> <div ng-message=max translate>tb.rulenode.max-delay-seconds-message</div> </div> </md-input-container> <label translate class=tb-title>tb.rulenode.message-attributes</label> <div class=tb-hint translate>tb.rulenode.message-attributes-hint</div> <tb-kv-map-config ng-model=configuration.messageAttributes ng-required=false key-text="\'tb.rulenode.name\'" key-required-text="\'tb.rulenode.name-required\'" val-text="\'tb.rulenode.value\'" val-required-text="\'tb.rulenode.value-required\'"> </tb-kv-map-config> <md-input-container class=md-block> <label translate>tb.rulenode.aws-access-key-id</label> <input ng-required=true name=accessKeyId ng-model=configuration.accessKeyId> <div ng-messages=snsConfigForm.accessKeyId.$error> <div ng-message=required translate>tb.rulenode.aws-access-key-id-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-secret-access-key</label> <input ng-required=true name=secretAccessKey ng-model=configuration.secretAccessKey> <div ng-messages=snsConfigForm.secretAccessKey.$error> <div ng-message=required translate>tb.rulenode.aws-secret-access-key-required</div> </div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.aws-region</label> <input ng-required=true name=region ng-model=configuration.region> <div ng-messages=snsConfigForm.region.$error> <div ng-message=required translate>tb.rulenode.aws-region-required</div> </div> </md-input-container> </section> '},function(e,t){e.exports=" <section ng-form name=timeseriesConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.default-ttl</label> <input ng-required=true type=number step=1 name=defaultTTL ng-model=configuration.defaultTTL min=0> <div ng-messages=timeseriesConfigForm.defaultTTL.$error multiple=multiple md-auto-hide=false> <div ng-message=required translate>tb.rulenode.default-ttl-required</div> <div ng-message=min translate>tb.rulenode.min-default-ttl-message</div> </div> </md-input-container> </section> "},function(e,t){e.exports=' <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat="direction in types.entitySearchDirection" ng-value=direction> {{ (\'relation.search-direction.\' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder="{{ \'tb.rulenode.unlimited-level\' | translate }}" ng-model=query.maxLevel aria-label="{{ \'tb.rulenode.max-relation-level\' | translate }}"> </md-input-container> </div> <div class=md-caption style=color:rgba(0,0,0,.57) translate>relation.relation-type</div> <tb-relation-type-autocomplete flex hide-label ng-model=query.relationType tb-required=false> </tb-relation-type-autocomplete> <div class="md-caption tb-required" style=color:rgba(0,0,0,.57) translate>device.device-types</div> <tb-entity-subtype-list tb-required=true entity-type=types.entityType.device ng-model=query.deviceTypes> </tb-entity-subtype-list> </section> ';
  2 +},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.device-relations-query</label> <tb-device-relations-query-config style=padding-bottom:15px ng-model=configuration.deviceRelationsQuery> </tb-device-relations-query-config> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding">tb.rulenode.client-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.clientAttributeNames placeholder="{{\'tb.rulenode.client-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.shared-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.sharedAttributeNames placeholder="{{\'tb.rulenode.shared-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.server-attributes</label> <md-chips style=padding-bottom:15px ng-required=false readonly=readonly ng-model=configuration.serverAttributeNames placeholder="{{\'tb.rulenode.server-attributes\' | translate}}" md-separator-keys=separatorKeys> </md-chips> <label translate class="tb-title no-padding">tb.rulenode.latest-timeseries</label> <md-chips ng-required=false readonly=readonly ng-model=configuration.latestTsKeyNames placeholder="{{\'tb.rulenode.latest-timeseries\' | translate}}" md-separator-keys=separatorKeys> </md-chips> </section> '},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title tb-required">tb.rulenode.fields-mapping</label> <tb-kv-map-config ng-model=configuration.fieldsMapping ng-required=true required-text="\'tb.rulenode.fields-mapping-required\'" key-text="\'tb.rulenode.source-field\'" key-required-text="\'tb.rulenode.source-field-required\'" val-text="\'tb.rulenode.target-attribute\'" val-required-text="\'tb.rulenode.target-attribute-required\'"> </tb-kv-map-config> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title tb-required\">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> <label translate class=\"tb-title tb-required\">tb.rulenode.attr-mapping</label> <md-checkbox aria-label=\"{{ 'tb.rulenode.latest-telemetry' | translate }}\" ng-model=configuration.telemetry>{{ 'tb.rulenode.latest-telemetry' | translate }} </md-checkbox> <tb-kv-map-config ng-model=configuration.attrMapping ng-required=true required-text=\"'tb.rulenode.attr-mapping-required'\" key-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry' : 'tb.rulenode.source-attribute'\" key-required-text=\"configuration.telemetry ? 'tb.rulenode.source-telemetry-required' : 'tb.rulenode.source-attribute-required'\" val-text=\"'tb.rulenode.target-attribute'\" val-required-text=\"'tb.rulenode.target-attribute-required'\"> </tb-kv-map-config> </section> "},22,function(e,t){e.exports=" <section ng-form name=checkRelationConfigForm> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=configuration.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <div layout=row class=tb-entity-select> <tb-entity-type-select style=min-width:100px the-form=checkRelationConfigForm tb-required=true ng-model=configuration.entityType> </tb-entity-type-select> <tb-entity-autocomplete flex ng-if=configuration.entityType the-form=checkRelationConfigForm tb-required=true entity-type=configuration.entityType ng-model=configuration.entityId> </tb-entity-autocomplete> </div> <tb-relation-type-autocomplete hide-label ng-model=configuration.relationType tb-required=true> </tb-relation-type-autocomplete> </section> "},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" ng-class="{\'tb-required\': required}">tb.rulenode.message-types-filter</label> <md-chips id=message_type_chips ng-required=required readonly=readonly ng-model=messageTypes md-autocomplete-snap md-transform-chip=transformMessageTypeChip($chip) md-require-match=false> <md-autocomplete id=message_type md-no-cache=true md-selected-item=selectedMessageType md-search-text=messageTypeSearchText md-items="item in messageTypesSearch(messageTypeSearchText)" md-item-text=item.name md-min-length=0 placeholder="{{\'tb.rulenode.message-type\' | translate }}" md-menu-class=tb-message-type-autocomplete> <span md-highlight-text=messageTypeSearchText md-highlight-flags=^i>{{item}}</span> <md-not-found> <div class=tb-not-found> <div class=tb-no-entries ng-if="!messageTypeSearchText || !messageTypeSearchText.length"> <span translate>tb.rulenode.no-message-types-found</span> </div> <div ng-if="messageTypeSearchText && messageTypeSearchText.length"> <span translate translate-values=\'{ messageType: "{{messageTypeSearchText | truncate:true:6:&apos;...&apos;}}" }\'>tb.rulenode.no-message-type-matching</span> <span> <a translate ng-click="createMessageType($event, \'#message_type_chips\')">tb.rulenode.create-new-message-type</a> </span> </div> </div> </md-not-found> </md-autocomplete> <md-chip-template> <span>{{$chip.name}}</span> </md-chip-template> </md-chips> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=messageTypes class=tb-error-message>tb.rulenode.message-types-required</div> </div> </section>'},function(e,t){e.exports=' <section layout=column> <label translate class="tb-title no-padding" class=required>tb.rulenode.originator-types-filter</label> <tb-entity-type-list flex ng-model=configuration.originatorTypes allowed-entity-types=allowedEntityTypes ignore-authority-filter=true tb-required=true> </tb-entity-type-list> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-filter-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.switch</label> <tb-js-func ng-model=configuration.jsScript function-name=Switch function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-switch-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=' <section class=tb-kv-map-config layout=column> <div class=header flex layout=row> <span class=cell flex translate>{{ keyText }}</span> <span class=cell flex translate>{{ valText }}</span> <span ng-show=!disabled style=width:52px>&nbsp</span> </div> <div class=body> <div class=row ng-form name=kvForm flex layout=row layout-align="start center" ng-repeat="keyVal in kvList track by $index"> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ keyText | translate }}" ng-required=true name=key ng-model=keyVal.key> <div ng-messages=kvForm.key.$error> <div translate ng-message=required>{{keyRequiredText}}</div> </div> </md-input-container> <md-input-container class="cell md-block" flex md-no-float> <input placeholder="{{ valText | translate }}" ng-required=true name=value ng-model=keyVal.value> <div ng-messages=kvForm.value.$error> <div translate ng-message=required>{{valRequiredText}}</div> </div> </md-input-container> <md-button ng-show=!disabled ng-disabled=loading class="md-icon-button md-primary" ng-click=removeKeyVal($index) aria-label="{{ \'action.remove\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.remove-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.delete\' | translate }}" class=material-icons> close </md-icon> </md-button> </div> </div> <div class=tb-error-messages ng-messages=ngModelCtrl.$error role=alert> <div translate ng-message=kvMap class=tb-error-message>{{requiredText}}</div> </div> <div> <md-button ng-show=!disabled ng-disabled=loading class="md-primary md-raised" ng-click=addKeyVal() aria-label="{{ \'action.add\' | translate }}"> <md-tooltip md-direction=top> {{ \'tb.key-val.add-entry\' | translate }} </md-tooltip> <md-icon aria-label="{{ \'action.add\' | translate }}" class=material-icons> add </md-icon> {{ \'action.add\' | translate }} </md-button> </div> </section> '},function(e,t){e.exports=" <section layout=column> <div layout=row> <md-input-container class=md-block style=min-width:100px> <label translate>relation.direction</label> <md-select required ng-model=query.direction> <md-option ng-repeat=\"direction in types.entitySearchDirection\" ng-value=direction> {{ ('relation.search-direction.' + direction) | translate}} </md-option> </md-select> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.max-relation-level</label> <input name=maxRelationLevel type=number min=1 step=1 placeholder=\"{{ 'tb.rulenode.unlimited-level' | translate }}\" ng-model=query.maxLevel aria-label=\"{{ 'tb.rulenode.max-relation-level' | translate }}\"> </md-input-container> </div> <div class=md-caption style=padding-bottom:10px;color:rgba(0,0,0,.57) translate>relation.relation-filters</div> <tb-relation-filters ng-model=query.filters> </tb-relation-filters> </section> "},function(e,t){e.exports=' <section layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.originator-source</label> <md-select required ng-model=configuration.originatorSource> <md-option ng-repeat="source in ruleNodeTypes.originatorSource" ng-value=source.value> {{ source.name | translate}} </md-option> </md-select> </md-input-container> <section layout=column ng-if="configuration.originatorSource == ruleNodeTypes.originatorSource.RELATED.value"> <label translate class="tb-title tb-required">tb.rulenode.relations-query</label> <tb-relations-query-config style=padding-bottom:15px ng-model=configuration.relationsQuery> </tb-relations-query-config> </section> </section> '},function(e,t){e.exports=" <section layout=column> <label translate class=\"tb-title no-padding\">tb.rulenode.transform</label> <tb-js-func ng-model=configuration.jsScript function-name=Transform function-args=\"{{ ['msg', 'metadata', 'msgType'] }}\" no-validate=true> </tb-js-func> <div layout=row style=padding-bottom:15px> <md-button ng-click=testScript($event) class=\"md-primary md-raised\"> {{ 'tb.rulenode.test-transformer-function' | translate }} </md-button> </div> </section> "},function(e,t){e.exports=" <section ng-form name=toEmailConfigForm layout=column> <md-input-container class=md-block> <label translate>tb.rulenode.from-template</label> <textarea ng-required=true name=fromTemplate ng-model=configuration.fromTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.fromTemplate.$error> <div ng-message=required translate>tb.rulenode.from-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.from-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.to-template</label> <textarea ng-required=true name=toTemplate ng-model=configuration.toTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.toTemplate.$error> <div ng-message=required translate>tb.rulenode.to-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.cc-template</label> <textarea name=ccTemplate ng-model=configuration.ccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.bcc-template</label> <textarea name=ccTemplate ng-model=configuration.bccTemplate rows=2></textarea> <div class=tb-hint translate>tb.rulenode.mail-address-list-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.subject-template</label> <textarea ng-required=true name=subjectTemplate ng-model=configuration.subjectTemplate rows=2></textarea> <div ng-messages=toEmailConfigForm.subjectTemplate.$error> <div ng-message=required translate>tb.rulenode.subject-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.subject-template-hint</div> </md-input-container> <md-input-container class=md-block> <label translate>tb.rulenode.body-template</label> <textarea ng-required=true name=bodyTemplate ng-model=configuration.bodyTemplate rows=6></textarea> <div ng-messages=toEmailConfigForm.bodyTemplate.$error> <div ng-message=required translate>tb.rulenode.body-template-required</div> </div> <div class=tb-hint translate>tb.rulenode.body-template-hint</div> </md-input-container> </section> "},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(5),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,r){var a=function(a,i,l,s){var u=o.default;i.html(u),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);r.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(6),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,r){var a=function(a,i,l,s){var u=o.default;i.html(u),a.types=n,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue},a.testDetailsBuildJs=function(e){var n=angular.copy(a.configuration.alarmDetailsBuildJs);r.testNodeScript(e,n,"json",t.instant("tb.rulenode.details")+"","Details",["msg","metadata","msgType"],a.ruleNodeId).then(function(e){a.configuration.alarmDetailsBuildJs=e,s.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(7),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n,r){var a=function(a,i,l,s){var u=o.default;i.html(u),a.types=n,a.originator=null,a.$watch("configuration",function(e,t){angular.equals(e,t)||s.$setViewValue(a.configuration)}),s.$render=function(){a.configuration=s.$viewValue,a.configuration.originatorId&&a.configuration.originatorType?a.originator={id:a.configuration.originatorId,entityType:a.configuration.originatorType}:a.originator=null,a.$watch("originator",function(e,t){angular.equals(e,t)||(a.originator?(s.$viewValue.originatorId=a.originator.id,s.$viewValue.originatorType=a.originator.entityType):(s.$viewValue.originatorId=null,s.$viewValue.originatorType=null))},!0)},a.testScript=function(e){var n=angular.copy(a.configuration.jsScript);r.testNodeScript(e,n,"generate",t.instant("tb.rulenode.generator")+"","Generate",["prevMsg","prevMetadata","prevMsgType"],a.ruleNodeId).then(function(e){a.configuration.jsScript=e,s.$setDirty()})},e(i.contents())(a)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:a}}a.$inject=["$compile","$translate","types","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(1);var i=n(8),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(54),i=r(a),o=n(38),l=r(o),s=n(41),u=r(s),d=n(40),c=r(d),m=n(39),g=r(m),p=n(44),f=r(p),b=n(49),v=r(b),y=n(50),q=r(y),h=n(48),$=r(h),k=n(43),T=r(k),w=n(52),x=r(w),C=n(53),M=r(C),_=n(47),S=r(_),N=n(45),V=r(N),j=n(51),P=r(j),F=n(46),E=r(F);t.default=angular.module("thingsboard.ruleChain.config.action",[]).directive("tbActionNodeTimeseriesConfig",i.default).directive("tbActionNodeAttributesConfig",l.default).directive("tbActionNodeGeneratorConfig",u.default).directive("tbActionNodeCreateAlarmConfig",c.default).directive("tbActionNodeClearAlarmConfig",g.default).directive("tbActionNodeLogConfig",f.default).directive("tbActionNodeRpcReplyConfig",v.default).directive("tbActionNodeRpcRequestConfig",q.default).directive("tbActionNodeRestApiCallConfig",$.default).directive("tbActionNodeKafkaConfig",T.default).directive("tbActionNodeSnsConfig",x.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",S.default).directive("tbActionNodeMqttConfig",V.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",E.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.ackValues=["all","-1","0","1"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(9),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){var s=o.default;a.html(s),r.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(r.configuration)}),l.$render=function(){r.configuration=l.$viewValue},r.testScript=function(e){var a=angular.copy(r.configuration.jsScript);n.testNodeScript(e,a,"string",t.instant("tb.rulenode.to-string")+"","ToString",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(10),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){var s=o.default;a.html(s),r.$mdExpansionPanel=t,r.ruleNodeTypes=n,r.credentialsTypeChanged=function(){var e=r.configuration.credentials.type;r.configuration.credentials={},r.configuration.credentials.type=e,r.updateValidity()},r.certFileAdded=function(e,t){var n=new FileReader;n.onload=function(n){r.$apply(function(){if(n.target.result){l.$setDirty();var a=n.target.result;a&&a.length>0&&("caCert"==t&&(r.configuration.credentials.caCertFileName=e.name,r.configuration.credentials.caCert=a),"privateKey"==t&&(r.configuration.credentials.privateKeyFileName=e.name,r.configuration.credentials.privateKey=a),"Cert"==t&&(r.configuration.credentials.certFileName=e.name,r.configuration.credentials.cert=a)),r.updateValidity()}})},n.readAsText(e.file)},r.clearCertFile=function(e){l.$setDirty(),"caCert"==e&&(r.configuration.credentials.caCertFileName=null,r.configuration.credentials.caCert=null),"privateKey"==e&&(r.configuration.credentials.privateKeyFileName=null,r.configuration.credentials.privateKey=null),"Cert"==e&&(r.configuration.credentials.certFileName=null,r.configuration.credentials.cert=null),r.updateValidity()},r.updateValidity=function(){var e=!0,t=r.configuration.credentials;t.type==n.mqttCredentialTypes["cert.PEM"].value&&(t.caCert&&t.cert&&t.privateKey||(e=!1)),l.$setValidity("Certs",e)},r.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(r.configuration)}),l.$render=function(){r.configuration=l.$viewValue},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:r}}a.$inject=["$compile","$mdExpansionPanel","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(2);var i=n(11),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(12),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.messageProperties=[null,"BASIC","TEXT_PLAIN","MINIMAL_BASIC","MINIMAL_PERSISTENT_BASIC","PERSISTENT_BASIC","PERSISTENT_TEXT_PLAIN"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(13),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(14),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(15),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(16),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.smtpProtocols=["smtp","smtps"],t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(17),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(18),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{readonly:"=ngReadonly"},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(19),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(20),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(21),o=r(i)},function(e,t){"use strict";function n(e){var t=function(t,n,r,a){n.html("<div></div>"),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(22),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(23),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(60),i=r(a),o=n(61),l=r(o),s=n(58),u=r(s),d=n(62),c=r(d),m=n(57),g=r(m),p=n(63),f=r(p);t.default=angular.module("thingsboard.ruleChain.config.enrichment",[]).directive("tbEnrichmentNodeOriginatorAttributesConfig",i.default).directive("tbEnrichmentNodeOriginatorFieldsConfig",l.default).directive("tbEnrichmentNodeDeviceAttributesConfig",u.default).directive("tbEnrichmentNodeRelatedAttributesConfig",c.default).directive("tbEnrichmentNodeCustomerAttributesConfig",g.default).directive("tbEnrichmentNodeTenantAttributesConfig",f.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l);var s=186;n.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,s],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","$mdConstant"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(24),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(25),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(26),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{
  3 +value:!0}),t.default=a;var i=n(27),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.types=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(28),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(68),i=r(a),o=n(66),l=r(o),s=n(69),u=r(s),d=n(64),c=r(d),m=n(67),g=r(m);t.default=angular.module("thingsboard.ruleChain.config.filter",[]).directive("tbFilterNodeScriptConfig",i.default).directive("tbFilterNodeMessageTypeConfig",l.default).directive("tbFilterNodeSwitchConfig",u.default).directive("tbFilterNodeCheckRelationConfig",c.default).directive("tbFilterNodeOriginatorTypeConfig",g.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){function s(){if(l.$viewValue){for(var e=[],t=0;t<r.messageTypes.length;t++)e.push(r.messageTypes[t].value);l.$viewValue.messageTypes=e,u()}}function u(){if(r.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var d=o.default;a.html(d),r.selectedMessageType=null,r.messageTypeSearchText=null,r.ngModelCtrl=l;var c=[];for(var m in n.messageType){var g={name:n.messageType[m].name,value:n.messageType[m].value};c.push(g)}r.transformMessageTypeChip=function(e){var n,r=t("filter")(c,{name:e},!0);return n=r&&r.length?angular.copy(r[0]):{name:e,value:e}},r.messageTypesSearch=function(e){var n=e?t("filter")(c,{name:e}):c;return n.map(function(e){return e.name})},r.createMessageType=function(e,t){var n=angular.element(t,a)[0].firstElementChild,r=angular.element(n),i=r.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),r.scope().$mdChipsCtrl.appendChip(i.trim()),r.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){r.messageTypesWatch&&(r.messageTypesWatch(),r.messageTypesWatch=null);var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var a=0;a<e.messageTypes.length;a++){var i=e.messageTypes[a];n.messageType[i]?t.push(angular.copy(n.messageType[i])):t.push({name:i,value:i})}r.messageTypes=t,r.messageTypesWatch=r.$watch("messageTypes",function(e,t){angular.equals(e,t)||s()},!0)},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:r}}a.$inject=["$compile","$filter","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a,n(3);var i=n(29),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.allowedEntityTypes=[t.entityType.device,t.entityType.asset,t.entityType.tenant,t.entityType.customer,t.entityType.user,t.entityType.dashboard,t.entityType.rulechain,t.entityType.rulenode],n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(30),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){var s=o.default;a.html(s),r.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(r.configuration)}),l.$render=function(){r.configuration=l.$viewValue},r.testScript=function(e){var a=angular.copy(r.configuration.jsScript);n.testNodeScript(e,a,"filter",t.instant("tb.rulenode.filter")+"","Filter",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(31),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){var s=o.default;a.html(s),r.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(r.configuration)}),l.$render=function(){r.configuration=l.$viewValue},r.testScript=function(e){var a=angular.copy(r.configuration.jsScript);n.testNodeScript(e,a,"switch",t.instant("tb.rulenode.switch")+"","Switch",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(32),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){function i(e){e>-1&&t.kvList.splice(e,1)}function l(){t.kvList||(t.kvList=[]),t.kvList.push({key:"",value:""})}function s(){var e={};t.kvList.forEach(function(t){t.key&&(e[t.key]=t.value)}),a.$setViewValue(e),u()}function u(){var e=!0;t.required&&!t.kvList.length&&(e=!1),a.$setValidity("kvMap",e)}var d=o.default;n.html(d),t.ngModelCtrl=a,t.removeKeyVal=i,t.addKeyVal=l,t.kvList=[],t.$watch("query",function(e,n){angular.equals(e,n)||a.$setViewValue(t.query)}),a.$render=function(){if(a.$viewValue){var e=a.$viewValue;t.kvList.length=0;for(var n in e)t.kvList.push({key:n,value:e[n]})}t.$watch("kvList",function(e,t){angular.equals(e,t)||s()},!0),u()},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",disabled:"=ngDisabled",requiredText:"=",keyText:"=",keyRequiredText:"=",valText:"=",valRequiredText:"="},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(33),o=r(i);n(4)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.types=t,n.$watch("query",function(e,t){angular.equals(e,t)||i.$setViewValue(n.query)}),i.$render=function(){n.query=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(34),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t){var n=function(n,r,a,i){var l=o.default;r.html(l),n.ruleNodeTypes=t,n.$watch("configuration",function(e,t){angular.equals(e,t)||i.$setViewValue(n.configuration)}),i.$render=function(){n.configuration=i.$viewValue},e(r.contents())(n)};return{restrict:"E",require:"^ngModel",scope:{},link:n}}a.$inject=["$compile","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(35),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(72),i=r(a),o=n(74),l=r(o),s=n(75),u=r(s);t.default=angular.module("thingsboard.ruleChain.config.transform",[]).directive("tbTransformationNodeChangeOriginatorConfig",i.default).directive("tbTransformationNodeScriptConfig",l.default).directive("tbTransformationNodeToEmailConfig",u.default).name},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e,t,n){var r=function(r,a,i,l){var s=o.default;a.html(s),r.$watch("configuration",function(e,t){angular.equals(e,t)||l.$setViewValue(r.configuration)}),l.$render=function(){r.configuration=l.$viewValue},r.testScript=function(e){var a=angular.copy(r.configuration.jsScript);n.testNodeScript(e,a,"update",t.instant("tb.rulenode.transformer")+"","Transform",["msg","metadata","msgType"],r.ruleNodeId).then(function(e){r.configuration.jsScript=e,l.$setDirty()})},e(a.contents())(r)};return{restrict:"E",require:"^ngModel",scope:{ruleNodeId:"="},link:r}}a.$inject=["$compile","$translate","ruleNodeScriptTest"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(36),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=function(t,n,r,a){var i=o.default;n.html(i),t.$watch("configuration",function(e,n){angular.equals(e,n)||a.$setViewValue(t.configuration)}),a.$render=function(){t.configuration=a.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}a.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(37),o=r(i)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(79),i=r(a),o=n(65),l=r(o),s=n(59),u=r(s),d=n(73),c=r(d),m=n(42),g=r(m),p=n(56),f=r(p),b=n(71),v=r(b),y=n(55),q=r(y),h=n(70),$=r(h),k=n(78),T=r(k);t.default=angular.module("thingsboard.ruleChain.config",[i.default,l.default,u.default,c.default,g.default]).directive("tbNodeEmptyConfig",f.default).directive("tbRelationsQueryConfig",v.default).directive("tbDeviceRelationsQueryConfig",q.default).directive("tbKvMapConfig",$.default).config(T.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{filter:"Filter",switch:"Switch","message-type":"Message type","message-type-required":"Message type is required.","message-types-filter":"Message types filter","no-message-types-found":"No message types found","no-message-type-matching":"'{{messageType}}' not found.","create-new-message-type":"Create a new one!","message-types-required":"Message types are required.","client-attributes":"Client attributes","shared-attributes":"Shared attributes","server-attributes":"Server attributes","latest-timeseries":"Latest timeseries","relations-query":"Relations query","device-relations-query":"Device relations query","max-relation-level":"Max relation level","unlimited-level":"Unlimited level","latest-telemetry":"Latest telemetry","attr-mapping":"Attributes mapping","source-attribute":"Source attribute","source-attribute-required":"Source attribute is required.","source-telemetry":"Source telemetry","source-telemetry-required":"Source telemetry is required.","target-attribute":"Target attribute","target-attribute-required":"Target attribute is required.","attr-mapping-required":"At least one attribute mapping should be specified.","fields-mapping":"Fields mapping","fields-mapping-required":"At least one field mapping should be specified.","source-field":"Source field","source-field-required":"Source field is required.","originator-source":"Originator source","originator-customer":"Customer","originator-tenant":"Tenant","originator-related":"Related","clone-message":"Clone message",transform:"Transform","default-ttl":"Default TTL in seconds","default-ttl-required":"Default TTL is required.","min-default-ttl-message":"Only 0 minimum TTL is allowed.","message-count":"Message count (0 - unlimited)","message-count-required":"Message count is required.","min-message-count-message":"Only 0 minimum message count is allowed.","period-seconds":"Period in seconds","period-seconds-required":"Period is required.","min-period-seconds-message":"Only 1 second minimum period is allowed.",originator:"Originator","message-body":"Message body","message-metadata":"Message metadata",generate:"Generate","test-generator-function":"Test generator function",generator:"Generator","test-filter-function":"Test filter function","test-switch-function":"Test switch function","test-transformer-function":"Test transformer function",transformer:"Transformer","alarm-create-condition":"Alarm create condition","test-condition-function":"Test condition function","alarm-clear-condition":"Alarm clear condition","alarm-details-builder":"Alarm details builder","test-details-function":"Test details function","alarm-type":"Alarm type","alarm-type-required":"Alarm type is required.","alarm-severity":"Alarm severity","alarm-severity-required":"Alarm severity is required",propagate:"Propagate",condition:"Condition",details:"Details","to-string":"To string","test-to-string-function":"Test to string function","from-template":"From Template","from-template-required":"From Template is required","from-template-hint":"From address template, use <code>${metaKeyName}</code> to substitute variables from metadata","to-template":"To Template","to-template-required":"To Template is required","mail-address-list-template-hint":"Comma separated address list, use <code>${metaKeyName}</code> to substitute variables from metadata","cc-template":"Cc Template","bcc-template":"Bcc Template","subject-template":"Subject Template","subject-template-required":"Subject Template is required","subject-template-hint":"Mail subject template, use <code>${metaKeyName}</code> to substitute variables from metadata","body-template":"Body Template","body-template-required":"Body Template is required","body-template-hint":"Mail body template, use <code>${metaKeyName}</code> to substitute variables from metadata","request-id-metadata-attribute":"Request Id Metadata attribute name","timeout-sec":"Timeout in seconds","timeout-required":"Timeout is required","min-timeout-message":"Only 0 minimum timeout value is allowed.","endpoint-url-pattern":"Endpoint URL pattern","endpoint-url-pattern-required":"Endpoint URL pattern is required","endpoint-url-pattern-hint":"HTTP URL address pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","request-method":"Request method",headers:"Headers","headers-hint":"Use <code>${metaKeyName}</code> in header/value fields to substitute variables from metadata",header:"Header","header-required":"Header is required",value:"Value","value-required":"Value is required","topic-pattern":"Topic pattern","topic-pattern-required":"Topic pattern is required","mqtt-topic-pattern-hint":"MQTT topic pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","bootstrap-servers":"Bootstrap servers","bootstrap-servers-required":"Bootstrap servers value is required","other-properties":"Other properties",key:"Key","key-required":"Key is required",retries:"Automatically retry times if fails","min-retries-message":"Only 0 minimum retries is allowed.","batch-size-bytes":"Produces batch size in bytes","min-batch-size-bytes-message":"Only 0 minimum batch size is allowed.","linger-ms":"Time to buffer locally (ms)","min-linger-ms-message":"Only 0 ms minimum value is allowed.","buffer-memory-bytes":"Client buffer max size in bytes","min-buffer-memory-message":"Only 0 minimum buffer size is allowed.",acks:"Number of acknowledgments","key-serializer":"Key serializer","key-serializer-required":"Key serializer is required","value-serializer":"Value serializer","value-serializer-required":"Value serializer is required","topic-arn-pattern":"Topic ARN pattern","topic-arn-pattern-required":"Topic ARN pattern is required","topic-arn-pattern-hint":"Topic ARN pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","aws-access-key-id":"AWS Access Key ID","aws-access-key-id-required":"AWS Access Key ID is required","aws-secret-access-key":"AWS Secret Access Key","aws-secret-access-key-required":"AWS Secret Access Key is required","aws-region":"AWS Region","aws-region-required":"AWS Region is required","exchange-name-pattern":"Exchange name pattern","routing-key-pattern":"Routing key pattern","message-properties":"Message properties",host:"Host","host-required":"Host is required",port:"Port","port-required":"Port is required","port-range":"Port should be in a range from 1 to 65535.","virtual-host":"Virtual host",username:"Username",password:"Password","automatic-recovery":"Automatic recovery","connection-timeout-ms":"Connection timeout (ms)","min-connection-timeout-ms-message":"Only 0 ms minimum value is allowed.","handshake-timeout-ms":"Handshake timeout (ms)","min-handshake-timeout-ms-message":"Only 0 ms minimum value is allowed.","client-properties":"Client properties","queue-url-pattern":"Queue URL pattern","queue-url-pattern-required":"Queue URL pattern is required","queue-url-pattern-hint":"Queue URL pattern, use <code>${metaKeyName}</code> to substitute variables from metadata","delay-seconds":"Delay (seconds)","min-delay-seconds-message":"Only 0 seconds minimum value is allowed.","max-delay-seconds-message":"Only 900 seconds maximum value is allowed.",name:"Name","name-required":"Name is required","queue-type":"Queue type","sqs-queue-standard":"Standard","sqs-queue-fifo":"FIFO","message-attributes":"Message attributes","message-attributes-hint":"Use <code>${metaKeyName}</code> in name/value fields to substitute variables from metadata","connect-timeout":"Connection timeout (sec)","connect-timeout-required":"Connection timeout is required.","connect-timeout-range":"Connection timeout should be in a range from 1 to 200.","client-id":"Client ID","clean-session":"Clean session","enable-ssl":"Enable SSL",credentials:"Credentials","credentials-type":"Credentials type","credentials-type-required":"Credentials type is required.","credentials-anonymous":"Anonymous","credentials-basic":"Basic","credentials-pem":"PEM","username-required":"Username is required.","password-required":"Password is required.","ca-cert":"CA certificate file *","private-key":"Private key file *",cert:"Certificate file *","no-file":"No file selected.","drop-file":"Drop a file or click to select a file to upload.","private-key-password":"Private key password","use-system-smtp-settings":"Use system SMTP settings","smtp-protocol":"Protocol","smtp-host":"SMTP host","smtp-host-required":"SMTP host is required.","smtp-port":"SMTP port","smtp-port-required":"You must supply a smtp port.","smtp-port-range":"SMTP port should be in a range from 1 to 65535.","timeout-msec":"Timeout ms","min-timeout-msec-message":"Only 0 ms minimum value is allowed.","enter-username":"Enter username","enter-password":"Enter password","enable-tls":"Enable TLS","min-period-0-seconds-message":"Only 0 second minimum period is allowed.","max-pending-messages":"Maximum pending messages","max-pending-messages-required":"Maximum pending messages is required.","max-pending-messages-range":"Maximum pending messages should be in a range from 1 to 100000.","originator-types-filter":"Originator types filter"},"key-val":{key:"Key",value:"Value","remove-entry":"Remove entry","add-entry":"Add entry"}}};e.translations("en_US",t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){(0,o.default)(e)}a.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=a;var i=n(77),o=r(i)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{originatorSource:{CUSTOMER:{name:"tb.rulenode.originator-customer",value:"CUSTOMER"},TENANT:{name:"tb.rulenode.originator-tenant",value:"TENANT"},RELATED:{name:"tb.rulenode.originator-related",value:"RELATED"}},httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}]));
4 //# sourceMappingURL=rulenode-core-config.js.map 4 //# sourceMappingURL=rulenode-core-config.js.map
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard</groupId> 26 <groupId>org.thingsboard</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.transport</groupId> 26 <groupId>org.thingsboard.transport</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.transport</groupId> 26 <groupId>org.thingsboard.transport</groupId>
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>transport</artifactId> 24 <artifactId>transport</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard.transport</groupId> 26 <groupId>org.thingsboard.transport</groupId>
@@ -17,6 +17,7 @@ package org.thingsboard.server.transport.mqtt.session; @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.mqtt.session;
17 17
18 import com.google.gson.JsonArray; 18 import com.google.gson.JsonArray;
19 import com.google.gson.JsonElement; 19 import com.google.gson.JsonElement;
  20 +import com.google.gson.JsonNull;
20 import com.google.gson.JsonObject; 21 import com.google.gson.JsonObject;
21 import com.google.gson.JsonSyntaxException; 22 import com.google.gson.JsonSyntaxException;
22 import io.netty.channel.ChannelHandlerContext; 23 import io.netty.channel.ChannelHandlerContext;
@@ -240,7 +241,7 @@ public class GatewaySessionCtx { @@ -240,7 +241,7 @@ public class GatewaySessionCtx {
240 241
241 private String getDeviceType(JsonElement json) throws AdaptorException { 242 private String getDeviceType(JsonElement json) throws AdaptorException {
242 JsonElement type = json.getAsJsonObject().get("type"); 243 JsonElement type = json.getAsJsonObject().get("type");
243 - return type == null ? DEFAULT_DEVICE_TYPE : type.getAsString(); 244 + return type == null || type instanceof JsonNull ? DEFAULT_DEVICE_TYPE : type.getAsString();
244 } 245 }
245 246
246 private JsonElement getJson(MqttPublishMessage mqttMsg) throws AdaptorException { 247 private JsonElement getJson(MqttPublishMessage mqttMsg) throws AdaptorException {
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard</groupId> 26 <groupId>org.thingsboard</groupId>
1 { 1 {
2 "name": "thingsboard", 2 "name": "thingsboard",
3 "private": true, 3 "private": true,
4 - "version": "2.0.3", 4 + "version": "2.1.0",
5 "description": "Thingsboard UI", 5 "description": "Thingsboard UI",
6 "licenses": [ 6 "licenses": [
7 { 7 {
@@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
15 }, 15 },
16 "dependencies": { 16 "dependencies": {
17 "@flowjs/ng-flow": "^2.7.1", 17 "@flowjs/ng-flow": "^2.7.1",
18 - "ace-builds": "1.3.1",  
19 "angular": "1.5.8", 18 "angular": "1.5.8",
20 "angular-animate": "1.5.8", 19 "angular-animate": "1.5.8",
21 "angular-aria": "1.5.8", 20 "angular-aria": "1.5.8",
@@ -37,17 +36,17 @@ @@ -37,17 +36,17 @@
37 "angular-socialshare": "^2.3.8", 36 "angular-socialshare": "^2.3.8",
38 "angular-storage": "0.0.15", 37 "angular-storage": "0.0.15",
39 "angular-touch": "1.5.8", 38 "angular-touch": "1.5.8",
40 - "angular-translate": "2.13.1",  
41 - "angular-translate-handler-log": "2.13.1",  
42 - "angular-translate-interpolation-messageformat": "2.13.1",  
43 - "angular-translate-loader-static-files": "2.13.1",  
44 - "angular-translate-storage-cookie": "2.13.1",  
45 - "angular-translate-storage-local": "2.13.1", 39 + "angular-translate": "2.18.1",
  40 + "angular-translate-handler-log": "2.18.1",
  41 + "angular-translate-interpolation-messageformat": "2.18.1",
  42 + "angular-translate-loader-static-files": "2.18.1",
  43 + "angular-translate-storage-cookie": "2.18.1",
  44 + "angular-translate-storage-local": "2.18.1",
46 "angular-ui-ace": "^0.2.3", 45 "angular-ui-ace": "^0.2.3",
47 "angular-ui-router": "^0.3.1", 46 "angular-ui-router": "^0.3.1",
48 "angular-websocket": "^2.0.1", 47 "angular-websocket": "^2.0.1",
49 "base64-js": "^1.2.1", 48 "base64-js": "^1.2.1",
50 - "brace": "^0.8.0", 49 + "brace": "^0.10.0",
51 "canvas-gauges": "^2.0.9", 50 "canvas-gauges": "^2.0.9",
52 "clipboard": "^1.5.15", 51 "clipboard": "^1.5.15",
53 "compass-sass-mixins": "^0.12.7", 52 "compass-sass-mixins": "^0.12.7",
@@ -96,6 +95,7 @@ @@ -96,6 +95,7 @@
96 "babel-loader": "^6.2.5", 95 "babel-loader": "^6.2.5",
97 "babel-preset-es2015": "^6.14.0", 96 "babel-preset-es2015": "^6.14.0",
98 "babel-preset-react": "^6.16.0", 97 "babel-preset-react": "^6.16.0",
  98 + "compression-webpack-plugin": "^1.1.11",
99 "connect-history-api-fallback": "^1.3.0", 99 "connect-history-api-fallback": "^1.3.0",
100 "copy-webpack-plugin": "^3.0.1", 100 "copy-webpack-plugin": "^3.0.1",
101 "cross-env": "^3.2.4", 101 "cross-env": "^3.2.4",
@@ -127,7 +127,9 @@ @@ -127,7 +127,9 @@
127 "webpack-dev-middleware": "^1.6.1", 127 "webpack-dev-middleware": "^1.6.1",
128 "webpack-dev-server": "^1.15.1", 128 "webpack-dev-server": "^1.15.1",
129 "webpack-hot-middleware": "^2.12.2", 129 "webpack-hot-middleware": "^2.12.2",
130 - "webpack-material-design-icons": "^0.1.0" 130 + "webpack-material-design-icons": "^0.1.0",
  131 + "directory-tree": "^2.1.0",
  132 + "jsonminify": "^0.4.1"
131 }, 133 },
132 "engine": "node >= 5.9.0", 134 "engine": "node >= 5.9.0",
133 "nyc": { 135 "nyc": {
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <modelVersion>4.0.0</modelVersion> 20 <modelVersion>4.0.0</modelVersion>
21 <parent> 21 <parent>
22 <groupId>org.thingsboard</groupId> 22 <groupId>org.thingsboard</groupId>
23 - <version>2.0.3</version> 23 + <version>2.1.0-SNAPSHOT</version>
24 <artifactId>thingsboard</artifactId> 24 <artifactId>thingsboard</artifactId>
25 </parent> 25 </parent>
26 <groupId>org.thingsboard</groupId> 26 <groupId>org.thingsboard</groupId>
@@ -146,6 +146,7 @@ export default class AliasController { @@ -146,6 +146,7 @@ export default class AliasController {
146 newDatasource.entityId = resolvedEntity.id; 146 newDatasource.entityId = resolvedEntity.id;
147 newDatasource.entityType = resolvedEntity.entityType; 147 newDatasource.entityType = resolvedEntity.entityType;
148 newDatasource.entityName = resolvedEntity.name; 148 newDatasource.entityName = resolvedEntity.name;
  149 + newDatasource.entityDescription = resolvedEntity.entityDescription
149 newDatasource.name = resolvedEntity.name; 150 newDatasource.name = resolvedEntity.name;
150 newDatasource.generated = i > 0 ? true : false; 151 newDatasource.generated = i > 0 ? true : false;
151 datasources.push(newDatasource); 152 datasources.push(newDatasource);
@@ -167,6 +168,7 @@ export default class AliasController { @@ -167,6 +168,7 @@ export default class AliasController {
167 datasource.entityType = entity.entityType; 168 datasource.entityType = entity.entityType;
168 datasource.entityName = entity.name; 169 datasource.entityName = entity.name;
169 datasource.name = entity.name; 170 datasource.name = entity.name;
  171 + datasource.entityDescription = entity.entityDescription;
170 deferred.resolve([datasource]); 172 deferred.resolve([datasource]);
171 } else { 173 } else {
172 if (aliasInfo.stateEntity) { 174 if (aliasInfo.stateEntity) {
@@ -252,7 +252,7 @@ function DashboardService($rootScope, $http, $q, $location, $filter) { @@ -252,7 +252,7 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
252 if (port != 80 && port != 443) { 252 if (port != 80 && port != 443) {
253 url += ":" + port; 253 url += ":" + port;
254 } 254 }
255 - url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId; 255 + url += "/dashboard/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId;
256 return url; 256 return url;
257 } 257 }
258 258
@@ -329,7 +329,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device @@ -329,7 +329,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
329 } 329 }
330 330
331 function entityToEntityInfo(entity) { 331 function entityToEntityInfo(entity) {
332 - return { name: entity.name, entityType: entity.id.entityType, id: entity.id.id }; 332 + return { name: entity.name, entityType: entity.id.entityType, id: entity.id.id, entityDescription: entity.additionalInfo?entity.additionalInfo.description:"" };
333 } 333 }
334 334
335 function entityRelationInfoToEntityInfo(entityRelationInfo, direction) { 335 function entityRelationInfoToEntityInfo(entityRelationInfo, direction) {
@@ -32,6 +32,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co @@ -32,6 +32,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
32 getRuleNodeComponents: getRuleNodeComponents, 32 getRuleNodeComponents: getRuleNodeComponents,
33 getRuleNodeComponentByClazz: getRuleNodeComponentByClazz, 33 getRuleNodeComponentByClazz: getRuleNodeComponentByClazz,
34 getRuleNodeSupportedLinks: getRuleNodeSupportedLinks, 34 getRuleNodeSupportedLinks: getRuleNodeSupportedLinks,
  35 + ruleNodeAllowCustomLinks: ruleNodeAllowCustomLinks,
35 resolveTargetRuleChains: resolveTargetRuleChains, 36 resolveTargetRuleChains: resolveTargetRuleChains,
36 testScript: testScript, 37 testScript: testScript,
37 getLatestRuleNodeDebugInput: getLatestRuleNodeDebugInput 38 getLatestRuleNodeDebugInput: getLatestRuleNodeDebugInput
@@ -127,21 +128,21 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co @@ -127,21 +128,21 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
127 128
128 function getRuleNodeSupportedLinks(component) { 129 function getRuleNodeSupportedLinks(component) {
129 var relationTypes = component.configurationDescriptor.nodeDefinition.relationTypes; 130 var relationTypes = component.configurationDescriptor.nodeDefinition.relationTypes;
130 - var customRelations = component.configurationDescriptor.nodeDefinition.customRelations;  
131 - var linkLabels = []; 131 + var linkLabels = {};
132 for (var i=0;i<relationTypes.length;i++) { 132 for (var i=0;i<relationTypes.length;i++) {
133 - linkLabels.push({  
134 - name: relationTypes[i], custom: false  
135 - });  
136 - }  
137 - if (customRelations) {  
138 - linkLabels.push(  
139 - { name: 'Custom', custom: true }  
140 - ); 133 + var label = relationTypes[i];
  134 + linkLabels[label] = {
  135 + name: label,
  136 + value: label
  137 + };
141 } 138 }
142 return linkLabels; 139 return linkLabels;
143 } 140 }
144 141
  142 + function ruleNodeAllowCustomLinks(component) {
  143 + return component.configurationDescriptor.nodeDefinition.customRelations;
  144 + }
  145 +
145 function getRuleNodeComponents() { 146 function getRuleNodeComponents() {
146 var deferred = $q.defer(); 147 var deferred = $q.defer();
147 if (ruleNodeComponents) { 148 if (ruleNodeComponents) {
@@ -226,7 +227,10 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co @@ -226,7 +227,10 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
226 if (res && res.length) { 227 if (res && res.length) {
227 return res[0]; 228 return res[0];
228 } 229 }
229 - return null; 230 + var unknownComponent = angular.copy(types.unknownNodeComponent);
  231 + unknownComponent.clazz = clazz;
  232 + unknownComponent.configurationDescriptor.nodeDefinition.details = "Unknown Rule Node class: " + clazz;
  233 + return unknownComponent;
230 } 234 }
231 235
232 function resolveTargetRuleChains(ruleChainConnections) { 236 function resolveTargetRuleChains(ruleChainConnections) {
@@ -32,84 +32,7 @@ const MAX_LIMIT = 500; @@ -32,84 +32,7 @@ const MAX_LIMIT = 500;
32 /*@ngInject*/ 32 /*@ngInject*/
33 function TimeService($translate, types) { 33 function TimeService($translate, types) {
34 34
35 - var predefIntervals = [  
36 - {  
37 - name: $translate.instant('timeinterval.seconds-interval', {seconds: 1}, 'messageformat'),  
38 - value: 1 * SECOND  
39 - },  
40 - {  
41 - name: $translate.instant('timeinterval.seconds-interval', {seconds: 5}, 'messageformat'),  
42 - value: 5 * SECOND  
43 - },  
44 - {  
45 - name: $translate.instant('timeinterval.seconds-interval', {seconds: 10}, 'messageformat'),  
46 - value: 10 * SECOND  
47 - },  
48 - {  
49 - name: $translate.instant('timeinterval.seconds-interval', {seconds: 15}, 'messageformat'),  
50 - value: 15 * SECOND  
51 - },  
52 - {  
53 - name: $translate.instant('timeinterval.seconds-interval', {seconds: 30}, 'messageformat'),  
54 - value: 30 * SECOND  
55 - },  
56 - {  
57 - name: $translate.instant('timeinterval.minutes-interval', {minutes: 1}, 'messageformat'),  
58 - value: 1 * MINUTE  
59 - },  
60 - {  
61 - name: $translate.instant('timeinterval.minutes-interval', {minutes: 2}, 'messageformat'),  
62 - value: 2 * MINUTE  
63 - },  
64 - {  
65 - name: $translate.instant('timeinterval.minutes-interval', {minutes: 5}, 'messageformat'),  
66 - value: 5 * MINUTE  
67 - },  
68 - {  
69 - name: $translate.instant('timeinterval.minutes-interval', {minutes: 10}, 'messageformat'),  
70 - value: 10 * MINUTE  
71 - },  
72 - {  
73 - name: $translate.instant('timeinterval.minutes-interval', {minutes: 15}, 'messageformat'),  
74 - value: 15 * MINUTE  
75 - },  
76 - {  
77 - name: $translate.instant('timeinterval.minutes-interval', {minutes: 30}, 'messageformat'),  
78 - value: 30 * MINUTE  
79 - },  
80 - {  
81 - name: $translate.instant('timeinterval.hours-interval', {hours: 1}, 'messageformat'),  
82 - value: 1 * HOUR  
83 - },  
84 - {  
85 - name: $translate.instant('timeinterval.hours-interval', {hours: 2}, 'messageformat'),  
86 - value: 2 * HOUR  
87 - },  
88 - {  
89 - name: $translate.instant('timeinterval.hours-interval', {hours: 5}, 'messageformat'),  
90 - value: 5 * HOUR  
91 - },  
92 - {  
93 - name: $translate.instant('timeinterval.hours-interval', {hours: 10}, 'messageformat'),  
94 - value: 10 * HOUR  
95 - },  
96 - {  
97 - name: $translate.instant('timeinterval.hours-interval', {hours: 12}, 'messageformat'),  
98 - value: 12 * HOUR  
99 - },  
100 - {  
101 - name: $translate.instant('timeinterval.days-interval', {days: 1}, 'messageformat'),  
102 - value: 1 * DAY  
103 - },  
104 - {  
105 - name: $translate.instant('timeinterval.days-interval', {days: 7}, 'messageformat'),  
106 - value: 7 * DAY  
107 - },  
108 - {  
109 - name: $translate.instant('timeinterval.days-interval', {days: 30}, 'messageformat'),  
110 - value: 30 * DAY  
111 - }  
112 - ]; 35 + var predefIntervals;
113 36
114 var service = { 37 var service = {
115 minIntervalLimit: minIntervalLimit, 38 minIntervalLimit: minIntervalLimit,
@@ -166,6 +89,7 @@ function TimeService($translate, types) { @@ -166,6 +89,7 @@ function TimeService($translate, types) {
166 min = boundMinInterval(min); 89 min = boundMinInterval(min);
167 max = boundMaxInterval(max); 90 max = boundMaxInterval(max);
168 var intervals = []; 91 var intervals = [];
  92 + initPredefIntervals();
169 for (var i in predefIntervals) { 93 for (var i in predefIntervals) {
170 var interval = predefIntervals[i]; 94 var interval = predefIntervals[i];
171 if (interval.value >= min && interval.value <= max) { 95 if (interval.value >= min && interval.value <= max) {
@@ -175,6 +99,89 @@ function TimeService($translate, types) { @@ -175,6 +99,89 @@ function TimeService($translate, types) {
175 return intervals; 99 return intervals;
176 } 100 }
177 101
  102 + function initPredefIntervals() {
  103 + if (!predefIntervals) {
  104 + predefIntervals = [
  105 + {
  106 + name: $translate.instant('timeinterval.seconds-interval', {seconds: 1}, 'messageformat'),
  107 + value: 1 * SECOND
  108 + },
  109 + {
  110 + name: $translate.instant('timeinterval.seconds-interval', {seconds: 5}, 'messageformat'),
  111 + value: 5 * SECOND
  112 + },
  113 + {
  114 + name: $translate.instant('timeinterval.seconds-interval', {seconds: 10}, 'messageformat'),
  115 + value: 10 * SECOND
  116 + },
  117 + {
  118 + name: $translate.instant('timeinterval.seconds-interval', {seconds: 15}, 'messageformat'),
  119 + value: 15 * SECOND
  120 + },
  121 + {
  122 + name: $translate.instant('timeinterval.seconds-interval', {seconds: 30}, 'messageformat'),
  123 + value: 30 * SECOND
  124 + },
  125 + {
  126 + name: $translate.instant('timeinterval.minutes-interval', {minutes: 1}, 'messageformat'),
  127 + value: 1 * MINUTE
  128 + },
  129 + {
  130 + name: $translate.instant('timeinterval.minutes-interval', {minutes: 2}, 'messageformat'),
  131 + value: 2 * MINUTE
  132 + },
  133 + {
  134 + name: $translate.instant('timeinterval.minutes-interval', {minutes: 5}, 'messageformat'),
  135 + value: 5 * MINUTE
  136 + },
  137 + {
  138 + name: $translate.instant('timeinterval.minutes-interval', {minutes: 10}, 'messageformat'),
  139 + value: 10 * MINUTE
  140 + },
  141 + {
  142 + name: $translate.instant('timeinterval.minutes-interval', {minutes: 15}, 'messageformat'),
  143 + value: 15 * MINUTE
  144 + },
  145 + {
  146 + name: $translate.instant('timeinterval.minutes-interval', {minutes: 30}, 'messageformat'),
  147 + value: 30 * MINUTE
  148 + },
  149 + {
  150 + name: $translate.instant('timeinterval.hours-interval', {hours: 1}, 'messageformat'),
  151 + value: 1 * HOUR
  152 + },
  153 + {
  154 + name: $translate.instant('timeinterval.hours-interval', {hours: 2}, 'messageformat'),
  155 + value: 2 * HOUR
  156 + },
  157 + {
  158 + name: $translate.instant('timeinterval.hours-interval', {hours: 5}, 'messageformat'),
  159 + value: 5 * HOUR
  160 + },
  161 + {
  162 + name: $translate.instant('timeinterval.hours-interval', {hours: 10}, 'messageformat'),
  163 + value: 10 * HOUR
  164 + },
  165 + {
  166 + name: $translate.instant('timeinterval.hours-interval', {hours: 12}, 'messageformat'),
  167 + value: 12 * HOUR
  168 + },
  169 + {
  170 + name: $translate.instant('timeinterval.days-interval', {days: 1}, 'messageformat'),
  171 + value: 1 * DAY
  172 + },
  173 + {
  174 + name: $translate.instant('timeinterval.days-interval', {days: 7}, 'messageformat'),
  175 + value: 7 * DAY
  176 + },
  177 + {
  178 + name: $translate.instant('timeinterval.days-interval', {days: 30}, 'messageformat'),
  179 + value: 30 * DAY
  180 + }
  181 + ];
  182 + }
  183 + }
  184 +
178 function matchesExistingInterval(min, max, intervalMs) { 185 function matchesExistingInterval(min, max, intervalMs) {
179 var intervals = getIntervals(min, max); 186 var intervals = getIntervals(min, max);
180 for (var i in intervals) { 187 for (var i in intervals) {
@@ -488,7 +488,8 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi @@ -488,7 +488,8 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
488 } else { 488 } else {
489 return true; 489 return true;
490 } 490 }
491 - } else if (to.name === 'home.dashboards.dashboard' && allowedDashboardIds.indexOf(params.dashboardId) > -1) { 491 + } else if ((to.name === 'home.dashboards.dashboard' || to.name === 'dashboard')
  492 + && allowedDashboardIds.indexOf(params.dashboardId) > -1) {
492 return false; 493 return false;
493 } else { 494 } else {
494 return true; 495 return true;
@@ -504,10 +505,10 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi @@ -504,10 +505,10 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
504 var place = 'home.links'; 505 var place = 'home.links';
505 if (currentUser.authority === 'TENANT_ADMIN' || currentUser.authority === 'CUSTOMER_USER') { 506 if (currentUser.authority === 'TENANT_ADMIN' || currentUser.authority === 'CUSTOMER_USER') {
506 if (userHasDefaultDashboard()) { 507 if (userHasDefaultDashboard()) {
507 - place = 'home.dashboards.dashboard'; 508 + place = $rootScope.forceFullscreen ? 'dashboard' : 'home.dashboards.dashboard';
508 params = {dashboardId: currentUserDetails.additionalInfo.defaultDashboardId}; 509 params = {dashboardId: currentUserDetails.additionalInfo.defaultDashboardId};
509 } else if (isPublic()) { 510 } else if (isPublic()) {
510 - place = 'home.dashboards.dashboard'; 511 + place = 'dashboard';
511 params = {dashboardId: lastPublicDashboardId}; 512 params = {dashboardId: lastPublicDashboardId};
512 } 513 }
513 } else if (currentUser.authority === 'SYS_ADMIN') { 514 } else if (currentUser.authority === 'SYS_ADMIN') {
@@ -15,10 +15,6 @@ @@ -15,10 +15,6 @@
15 */ 15 */
16 import injectTapEventPlugin from 'react-tap-event-plugin'; 16 import injectTapEventPlugin from 'react-tap-event-plugin';
17 import UrlHandler from './url.handler'; 17 import UrlHandler from './url.handler';
18 -import addLocaleKorean from './locale/locale.constant-ko';  
19 -import addLocaleChinese from './locale/locale.constant-zh';  
20 -import addLocaleRussian from './locale/locale.constant-ru';  
21 -import addLocaleSpanish from './locale/locale.constant-es';  
22 18
23 /* eslint-disable import/no-unresolved, import/default */ 19 /* eslint-disable import/no-unresolved, import/default */
24 20
@@ -38,46 +34,28 @@ export default function AppConfig($provide, @@ -38,46 +34,28 @@ export default function AppConfig($provide,
38 $mdThemingProvider, 34 $mdThemingProvider,
39 $httpProvider, 35 $httpProvider,
40 $translateProvider, 36 $translateProvider,
41 - storeProvider,  
42 - locales) { 37 + storeProvider) {
43 38
44 injectTapEventPlugin(); 39 injectTapEventPlugin();
45 $locationProvider.html5Mode(true); 40 $locationProvider.html5Mode(true);
46 $urlRouterProvider.otherwise(UrlHandler); 41 $urlRouterProvider.otherwise(UrlHandler);
47 storeProvider.setCaching(false); 42 storeProvider.setCaching(false);
48 -  
49 - $translateProvider.useSanitizeValueStrategy(null);  
50 - $translateProvider.useMissingTranslationHandler('tbMissingTranslationHandler');  
51 - $translateProvider.addInterpolation('$translateMessageFormatInterpolation');  
52 - $translateProvider.fallbackLanguage('en_US');  
53 -  
54 - addLocaleKorean(locales);  
55 - addLocaleChinese(locales);  
56 - addLocaleRussian(locales);  
57 - addLocaleSpanish(locales);  
58 -  
59 - for (var langKey in locales) {  
60 - var translationTable = locales[langKey];  
61 - $translateProvider.translations(langKey, translationTable);  
62 - }  
63 -  
64 - var lang = $translateProvider.resolveClientLocale();  
65 - if (lang) {  
66 - lang = lang.toLowerCase();  
67 - if (lang.startsWith('ko')) {  
68 - $translateProvider.preferredLanguage('ko_KR');  
69 - } else if (lang.startsWith('zh')) {  
70 - $translateProvider.preferredLanguage('zh_CN');  
71 - } else if (lang.startsWith('es')) {  
72 - $translateProvider.preferredLanguage('es_ES');  
73 - } else if (lang.startsWith('ru')) {  
74 - $translateProvider.preferredLanguage('ru_RU');  
75 - } else {  
76 - $translateProvider.preferredLanguage('en_US');  
77 - }  
78 - } else {  
79 - $translateProvider.preferredLanguage('en_US');  
80 - } 43 +
  44 + $translateProvider.useSanitizeValueStrategy(null)
  45 + .useMissingTranslationHandler('tbMissingTranslationHandler')
  46 + .addInterpolation('$translateMessageFormatInterpolation')
  47 + .useStaticFilesLoader({
  48 + files: [
  49 + {
  50 + prefix: PUBLIC_PATH + 'locale/locale.constant-', //eslint-disable-line
  51 + suffix: '.json'
  52 + }
  53 + ]
  54 + })
  55 + .registerAvailableLanguageKeys(SUPPORTED_LANGS, getLanguageAliases(SUPPORTED_LANGS)) //eslint-disable-line
  56 + .fallbackLanguage('en_US') // must be before determinePreferredLanguage
  57 + .uniformLanguageTag('java') // must be before determinePreferredLanguage
  58 + .determinePreferredLanguage();
81 59
82 $httpProvider.interceptors.push('globalInterceptor'); 60 $httpProvider.interceptors.push('globalInterceptor');
83 61
@@ -168,4 +146,24 @@ export default function AppConfig($provide, @@ -168,4 +146,24 @@ export default function AppConfig($provide,
168 //$mdThemingProvider.alwaysWatchTheme(true); 146 //$mdThemingProvider.alwaysWatchTheme(true);
169 } 147 }
170 148
  149 + function getLanguageAliases(supportedLangs) {
  150 + var aliases = {};
  151 +
  152 + supportedLangs.sort().forEach(function(item, index, array) {
  153 + if (item.length === 2) {
  154 + aliases[item] = item;
  155 + aliases[item + '_*'] = item;
  156 + } else {
  157 + var key = item.slice(0, 2);
  158 + if (index === 0 || key !== array[index - 1].slice(0, 2)) {
  159 + aliases[key] = item;
  160 + aliases[key + '_*'] = item;
  161 + } else {
  162 + aliases[item] = item;
  163 + }
  164 + }
  165 + });
  166 +
  167 + return aliases;
  168 + }
171 } 169 }
@@ -51,7 +51,7 @@ import react from 'ngreact'; @@ -51,7 +51,7 @@ import react from 'ngreact';
51 import '@flowjs/ng-flow/dist/ng-flow-standalone.min'; 51 import '@flowjs/ng-flow/dist/ng-flow-standalone.min';
52 import 'ngFlowchart/dist/ngFlowchart'; 52 import 'ngFlowchart/dist/ngFlowchart';
53 53
54 -import thingsboardLocales from './locale/locale.constant'; 54 +import thingsboardTranslateHandler from './locale/translate-handler';
55 import thingsboardLogin from './login'; 55 import thingsboardLogin from './login';
56 import thingsboardDialogs from './components/datakey-config-dialog.controller'; 56 import thingsboardDialogs from './components/datakey-config-dialog.controller';
57 import thingsboardMenu from './services/menu.service'; 57 import thingsboardMenu from './services/menu.service';
@@ -117,7 +117,7 @@ angular.module('thingsboard', [ @@ -117,7 +117,7 @@ angular.module('thingsboard', [
117 react.name, 117 react.name,
118 'flow', 118 'flow',
119 'flowchart', 119 'flowchart',
120 - thingsboardLocales, 120 + thingsboardTranslateHandler,
121 thingsboardLogin, 121 thingsboardLogin,
122 thingsboardDialogs, 122 thingsboardDialogs,
123 thingsboardMenu, 123 thingsboardMenu,
@@ -113,7 +113,10 @@ export default function AppRun($rootScope, $window, $injector, $location, $log, @@ -113,7 +113,10 @@ export default function AppRun($rootScope, $window, $injector, $location, $log,
113 showForbiddenDialog(); 113 showForbiddenDialog();
114 } else if (to.redirectTo) { 114 } else if (to.redirectTo) {
115 evt.preventDefault(); 115 evt.preventDefault();
116 - $state.go(to.redirectTo, params) 116 + $state.go(to.redirectTo, params);
  117 + } else if (to.name === 'home.dashboards.dashboard' && $rootScope.forceFullscreen) {
  118 + evt.preventDefault();
  119 + $state.go('dashboard', params);
117 } 120 }
118 } 121 }
119 } else { 122 } else {
@@ -138,7 +141,7 @@ export default function AppRun($rootScope, $window, $injector, $location, $log, @@ -138,7 +141,7 @@ export default function AppRun($rootScope, $window, $injector, $location, $log,
138 $rootScope.pageTitle = 'ThingsBoard'; 141 $rootScope.pageTitle = 'ThingsBoard';
139 142
140 $rootScope.stateChangeSuccessHandle = $rootScope.$on('$stateChangeSuccess', function (evt, to, params) { 143 $rootScope.stateChangeSuccessHandle = $rootScope.$on('$stateChangeSuccess', function (evt, to, params) {
141 - if (userService.isPublic() && to.name === 'home.dashboards.dashboard') { 144 + if (userService.isPublic() && to.name === 'dashboard') {
142 $location.search('publicId', userService.getPublicId()); 145 $location.search('publicId', userService.getPublicId());
143 userService.updateLastPublicDashboardId(params.dashboardId); 146 userService.updateLastPublicDashboardId(params.dashboardId);
144 } 147 }
@@ -195,6 +195,21 @@ export default angular.module('thingsboard.types', []) @@ -195,6 +195,21 @@ export default angular.module('thingsboard.types', [])
195 }, 195 },
196 "ATTRIBUTES_READ": { 196 "ATTRIBUTES_READ": {
197 name: "audit-log.type-attributes-read" 197 name: "audit-log.type-attributes-read"
  198 + },
  199 + "RELATION_ADD_OR_UPDATE": {
  200 + name: "audit-log.type-relation-add-or-update"
  201 + },
  202 + "RELATION_DELETED": {
  203 + name: "audit-log.type-relation-delete"
  204 + },
  205 + "RELATIONS_DELETED": {
  206 + name: "audit-log.type-relations-delete"
  207 + },
  208 + "ALARM_ACK": {
  209 + name: "audit-log.type-alarm-ack"
  210 + },
  211 + "ALARM_CLEAR": {
  212 + name: "audit-log.type-alarm-clear"
198 } 213 }
199 }, 214 },
200 auditLogActionStatus: { 215 auditLogActionStatus: {
@@ -366,6 +381,12 @@ export default angular.module('thingsboard.types', []) @@ -366,6 +381,12 @@ export default angular.module('thingsboard.types', [])
366 list: 'entity.list-of-rulechains', 381 list: 'entity.list-of-rulechains',
367 nameStartsWith: 'entity.rulechain-name-starts-with' 382 nameStartsWith: 'entity.rulechain-name-starts-with'
368 }, 383 },
  384 + "RULE_NODE": {
  385 + type: 'entity.type-rulenode',
  386 + typePlural: 'entity.type-rulenodes',
  387 + list: 'entity.list-of-rulenodes',
  388 + nameStartsWith: 'entity.rulenode-name-starts-with'
  389 + },
369 "CURRENT_CUSTOMER": { 390 "CURRENT_CUSTOMER": {
370 type: 'entity.type-current-customer', 391 type: 'entity.type-current-customer',
371 list: 'entity.type-current-customer' 392 list: 'entity.type-current-customer'
@@ -510,6 +531,22 @@ export default angular.module('thingsboard.types', []) @@ -510,6 +531,22 @@ export default angular.module('thingsboard.types', [])
510 } 531 }
511 } 532 }
512 }, 533 },
  534 + unknownNodeComponent: {
  535 + type: 'UNKNOWN',
  536 + name: 'unknown',
  537 + clazz: 'tb.internal.Unknown',
  538 + configurationDescriptor: {
  539 + nodeDefinition: {
  540 + description: "",
  541 + details: "",
  542 + inEnabled: true,
  543 + outEnabled: true,
  544 + relationTypes: [],
  545 + customRelations: false,
  546 + defaultConfiguration: {}
  547 + }
  548 + }
  549 + },
513 inputNodeComponent: { 550 inputNodeComponent: {
514 type: 'INPUT', 551 type: 'INPUT',
515 name: 'Input', 552 name: 'Input',
@@ -565,6 +602,75 @@ export default angular.module('thingsboard.types', []) @@ -565,6 +602,75 @@ export default angular.module('thingsboard.types', [])
565 nodeClass: "tb-input-type", 602 nodeClass: "tb-input-type",
566 icon: "input", 603 icon: "input",
567 special: true 604 special: true
  605 + },
  606 + UNKNOWN: {
  607 + value: "UNKNOWN",
  608 + name: "rulenode.type-unknown",
  609 + details: "rulenode.type-unknown-details",
  610 + nodeClass: "tb-unknown-type",
  611 + icon: "help_outline"
  612 + }
  613 + },
  614 + messageType: {
  615 + 'POST_ATTRIBUTES_REQUEST': {
  616 + name: 'Post attributes',
  617 + value: 'POST_ATTRIBUTES_REQUEST'
  618 + },
  619 + 'POST_TELEMETRY_REQUEST': {
  620 + name: 'Post telemetry',
  621 + value: 'POST_TELEMETRY_REQUEST'
  622 + },
  623 + 'TO_SERVER_RPC_REQUEST': {
  624 + name: 'RPC Request from Device',
  625 + value: 'TO_SERVER_RPC_REQUEST'
  626 + },
  627 + 'RPC_CALL_FROM_SERVER_TO_DEVICE': {
  628 + name: 'RPC Request to Device',
  629 + value: 'RPC_CALL_FROM_SERVER_TO_DEVICE'
  630 + },
  631 + 'ACTIVITY_EVENT': {
  632 + name: 'Activity Event',
  633 + value: 'ACTIVITY_EVENT'
  634 + },
  635 + 'INACTIVITY_EVENT': {
  636 + name: 'Inactivity Event',
  637 + value: 'INACTIVITY_EVENT'
  638 + },
  639 + 'CONNECT_EVENT': {
  640 + name: 'Connect Event',
  641 + value: 'CONNECT_EVENT'
  642 + },
  643 + 'DISCONNECT_EVENT': {
  644 + name: 'Disconnect Event',
  645 + value: 'DISCONNECT_EVENT'
  646 + },
  647 + 'ENTITY_CREATED': {
  648 + name: 'Entity Created',
  649 + value: 'ENTITY_CREATED'
  650 + },
  651 + 'ENTITY_UPDATED': {
  652 + name: 'Entity Updated',
  653 + value: 'ENTITY_UPDATED'
  654 + },
  655 + 'ENTITY_DELETED': {
  656 + name: 'Entity Deleted',
  657 + value: 'ENTITY_DELETED'
  658 + },
  659 + 'ENTITY_ASSIGNED': {
  660 + name: 'Entity Assigned',
  661 + value: 'ENTITY_ASSIGNED'
  662 + },
  663 + 'ENTITY_UNASSIGNED': {
  664 + name: 'Entity Unassigned',
  665 + value: 'ENTITY_UNASSIGNED'
  666 + },
  667 + 'ATTRIBUTES_UPDATED': {
  668 + name: 'Attributes Updated',
  669 + value: 'ATTRIBUTES_UPDATED'
  670 + },
  671 + 'ATTRIBUTES_DELETED': {
  672 + name: 'Attributes Deleted',
  673 + value: 'ATTRIBUTES_DELETED'
568 } 674 }
569 }, 675 },
570 valueType: { 676 valueType: {
@@ -40,12 +40,12 @@ div.tb-widget { @@ -40,12 +40,12 @@ div.tb-widget {
40 position: absolute; 40 position: absolute;
41 top: 8px; 41 top: 8px;
42 right: 8px; 42 right: 8px;
43 - z-index: 1;  
44 - margin: 0px; 43 + z-index: 19;
  44 + margin: 0;
45 45
46 .md-button.md-icon-button { 46 .md-button.md-icon-button {
47 - margin: 0px !important;  
48 - padding: 0px !important; 47 + margin: 0 !important;
  48 + padding: 0 !important;
49 line-height: 20px; 49 line-height: 20px;
50 width: 32px; 50 width: 32px;
51 height: 32px; 51 height: 32px;
@@ -60,7 +60,7 @@ @@ -60,7 +60,7 @@
60 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;"> 60 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
61 <div class="tb-color-result" ng-style="{background: $chip.color}"></div> 61 <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
62 </div> 62 </div>
63 - <div layout="row" flex> 63 + <div layout="row">
64 <div class="tb-chip-label"> 64 <div class="tb-chip-label">
65 {{$chip.label}} 65 {{$chip.label}}
66 </div> 66 </div>
@@ -112,7 +112,7 @@ @@ -112,7 +112,7 @@
112 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;"> 112 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
113 <div class="tb-color-result" ng-style="{background: $chip.color}"></div> 113 <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
114 </div> 114 </div>
115 - <div layout="row" flex> 115 + <div layout="row">
116 <div class="tb-chip-label"> 116 <div class="tb-chip-label">
117 {{$chip.label}} 117 {{$chip.label}}
118 </div> 118 </div>
@@ -164,7 +164,7 @@ @@ -164,7 +164,7 @@
164 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;"> 164 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
165 <div class="tb-color-result" ng-style="{background: $chip.color}"></div> 165 <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
166 </div> 166 </div>
167 - <div layout="row" flex> 167 + <div layout="row">
168 <div class="tb-chip-label"> 168 <div class="tb-chip-label">
169 {{$chip.label}} 169 {{$chip.label}}
170 </div> 170 </div>
@@ -61,7 +61,7 @@ @@ -61,7 +61,7 @@
61 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;"> 61 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
62 <div class="tb-color-result" ng-style="{background: $chip.color}"></div> 62 <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
63 </div> 63 </div>
64 - <div layout="row" flex> 64 + <div layout="row">
65 <div class="tb-chip-label"> 65 <div class="tb-chip-label">
66 {{$chip.label}} 66 {{$chip.label}}
67 </div> 67 </div>
@@ -112,7 +112,7 @@ @@ -112,7 +112,7 @@
112 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;"> 112 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
113 <div class="tb-color-result" ng-style="{background: $chip.color}"></div> 113 <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
114 </div> 114 </div>
115 - <div layout="row" flex> 115 + <div layout="row">
116 <div class="tb-chip-label"> 116 <div class="tb-chip-label">
117 {{$chip.label}} 117 {{$chip.label}}
118 </div> 118 </div>
@@ -59,14 +59,4 @@ md-sidenav.tb-sidenav-details { @@ -59,14 +59,4 @@ md-sidenav.tb-sidenav-details {
59 background-color: $primary-hue-3; 59 background-color: $primary-hue-3;
60 } 60 }
61 } 61 }
62 -  
63 - md-tab-content.md-active > div {  
64 - height: 100%;  
65 - & > *:first-child {  
66 - height: 100%;  
67 - }  
68 - md-content {  
69 - height: 100%;  
70 - }  
71 - }  
72 } 62 }
@@ -18,8 +18,8 @@ import './json-content.scss'; @@ -18,8 +18,8 @@ import './json-content.scss';
18 import 'brace/ext/language_tools'; 18 import 'brace/ext/language_tools';
19 import 'brace/mode/json'; 19 import 'brace/mode/json';
20 import 'brace/mode/text'; 20 import 'brace/mode/text';
21 -import 'ace-builds/src-min-noconflict/snippets/json';  
22 -import 'ace-builds/src-min-noconflict/snippets/text'; 21 +import 'brace/snippets/json';
  22 +import 'brace/snippets/text';
23 23
24 import fixAceEditor from './ace-editor-fix'; 24 import fixAceEditor from './ace-editor-fix';
25 25
@@ -17,7 +17,7 @@ import './json-object-edit.scss'; @@ -17,7 +17,7 @@ import './json-object-edit.scss';
17 17
18 import 'brace/ext/language_tools'; 18 import 'brace/ext/language_tools';
19 import 'brace/mode/json'; 19 import 'brace/mode/json';
20 -import 'ace-builds/src-min-noconflict/snippets/json'; 20 +import 'brace/snippets/json';
21 21
22 import fixAceEditor from './ace-editor-fix'; 22 import fixAceEditor from './ace-editor-fix';
23 23
@@ -111,8 +111,15 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog, @@ -111,8 +111,15 @@ function ManageWidgetActionsController($rootScope, $scope, $document, $mdDialog,
111 } 111 }
112 }); 112 });
113 113
114 - function enterFilterMode () { 114 + function enterFilterMode (event) {
  115 + let $button = angular.element(event.currentTarget);
  116 + let $toolbarsContainer = $button.closest('.toolbarsContainer');
  117 +
115 vm.query.search = ''; 118 vm.query.search = '';
  119 +
  120 + $timeout(()=>{
  121 + $toolbarsContainer.find('.searchInput').focus();
  122 + })
116 } 123 }
117 124
118 function exitFilterMode () { 125 function exitFilterMode () {
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 limitations under the License. 15 limitations under the License.
16 16
17 --> 17 -->
18 -<div ng-form="manageWidgetActionsForm" class="tb-manage-widget-actions md-whiteframe-z1" layout="column"> 18 +<div ng-form="manageWidgetActionsForm" class="tb-manage-widget-actions md-whiteframe-z1 toolbarsContainer" layout="column">
19 <md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search === null"> 19 <md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search === null">
20 <div class="md-toolbar-tools"> 20 <div class="md-toolbar-tools">
21 <span translate>widget-config.actions</span> 21 <span translate>widget-config.actions</span>
@@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
26 {{ 'widget-config.add-action' | translate }} 26 {{ 'widget-config.add-action' | translate }}
27 </md-tooltip> 27 </md-tooltip>
28 </md-button> 28 </md-button>
29 - <md-button class="md-icon-button" ng-click="vm.enterFilterMode()"> 29 + <md-button class="md-icon-button" ng-click="vm.enterFilterMode($event)">
30 <md-icon>search</md-icon> 30 <md-icon>search</md-icon>
31 <md-tooltip md-direction="top"> 31 <md-tooltip md-direction="top">
32 {{ 'action.search' | translate }} 32 {{ 'action.search' | translate }}
@@ -44,7 +44,7 @@ @@ -44,7 +44,7 @@
44 </md-button> 44 </md-button>
45 <md-input-container flex> 45 <md-input-container flex>
46 <label>&nbsp;</label> 46 <label>&nbsp;</label>
47 - <input ng-model="vm.query.search" name="querySearchInput" placeholder="{{ 'widget-config.search-actions' | translate }}"/> 47 + <input ng-model="vm.query.search" class="searchInput" name="querySearchInput" placeholder="{{ 'widget-config.search-actions' | translate }}"/>
48 </md-input-container> 48 </md-input-container>
49 <md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()"> 49 <md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()">
50 <md-icon aria-label="Close" class="material-icons">close</md-icon> 50 <md-icon aria-label="Close" class="material-icons">close</md-icon>
@@ -187,17 +187,17 @@ @@ -187,17 +187,17 @@
187 </div> 187 </div>
188 <div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center"> 188 <div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
189 <div layout="row" layout-padding> 189 <div layout="row" layout-padding>
190 - <md-checkbox flex aria-label="{{ 'widget-config.display-title' | translate }}" 190 + <md-checkbox aria-label="{{ 'widget-config.display-title' | translate }}"
191 ng-model="showTitle">{{ 'widget-config.display-title' | translate }} 191 ng-model="showTitle">{{ 'widget-config.display-title' | translate }}
192 </md-checkbox> 192 </md-checkbox>
193 </div> 193 </div>
194 <div layout="row" layout-padding> 194 <div layout="row" layout-padding>
195 - <md-checkbox flex aria-label="{{ 'widget-config.drop-shadow' | translate }}" 195 + <md-checkbox aria-label="{{ 'widget-config.drop-shadow' | translate }}"
196 ng-model="dropShadow">{{ 'widget-config.drop-shadow' | translate }} 196 ng-model="dropShadow">{{ 'widget-config.drop-shadow' | translate }}
197 </md-checkbox> 197 </md-checkbox>
198 </div> 198 </div>
199 <div layout="row" layout-padding> 199 <div layout="row" layout-padding>
200 - <md-checkbox flex aria-label="{{ 'widget-config.enable-fullscreen' | translate }}" 200 + <md-checkbox aria-label="{{ 'widget-config.enable-fullscreen' | translate }}"
201 ng-model="enableFullscreen">{{ 'widget-config.enable-fullscreen' | translate }} 201 ng-model="enableFullscreen">{{ 'widget-config.enable-fullscreen' | translate }}
202 </md-checkbox> 202 </md-checkbox>
203 </div> 203 </div>
@@ -479,7 +479,11 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele @@ -479,7 +479,11 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
479 dashboardId: targetDashboardId, 479 dashboardId: targetDashboardId,
480 state: utils.objToBase64([ stateObject ]) 480 state: utils.objToBase64([ stateObject ])
481 } 481 }
482 - $state.go('home.dashboards.dashboard', stateParams); 482 + if ($state.current.name === 'dashboard') {
  483 + $state.go('dashboard', stateParams);
  484 + } else {
  485 + $state.go('home.dashboards.dashboard', stateParams);
  486 + }
483 break; 487 break;
484 case types.widgetActionTypes.custom.value: 488 case types.widgetActionTypes.custom.value:
485 var customFunction = descriptor.customFunction; 489 var customFunction = descriptor.customFunction;
@@ -196,6 +196,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget @@ -196,6 +196,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget
196 vm.displayDashboardTimewindow = displayDashboardTimewindow; 196 vm.displayDashboardTimewindow = displayDashboardTimewindow;
197 vm.displayDashboardsSelect = displayDashboardsSelect; 197 vm.displayDashboardsSelect = displayDashboardsSelect;
198 vm.displayEntitiesSelect = displayEntitiesSelect; 198 vm.displayEntitiesSelect = displayEntitiesSelect;
  199 + vm.hideFullscreenButton = hideFullscreenButton;
199 200
200 vm.widgetsBundle; 201 vm.widgetsBundle;
201 202
@@ -258,7 +259,11 @@ export default function DashboardController(types, utils, dashboardUtils, widget @@ -258,7 +259,11 @@ export default function DashboardController(types, utils, dashboardUtils, widget
258 dashboardId: vm.currentDashboardId 259 dashboardId: vm.currentDashboardId
259 }); 260 });
260 } else { 261 } else {
261 - $state.go('home.dashboards.dashboard', {dashboardId: vm.currentDashboardId}); 262 + if ($state.current.name === 'dashboard') {
  263 + $state.go('dashboard', {dashboardId: vm.currentDashboardId});
  264 + } else {
  265 + $state.go('home.dashboards.dashboard', {dashboardId: vm.currentDashboardId});
  266 + }
262 } 267 }
263 } 268 }
264 }); 269 });
@@ -805,6 +810,10 @@ export default function DashboardController(types, utils, dashboardUtils, widget @@ -805,6 +810,10 @@ export default function DashboardController(types, utils, dashboardUtils, widget
805 } 810 }
806 } 811 }
807 812
  813 + function hideFullscreenButton() {
  814 + return vm.widgetEditMode || vm.iframeMode || $rootScope.forceFullscreen || $state.current.name === 'dashboard';
  815 + }
  816 +
808 function onRevertWidgetEdit(widgetForm) { 817 function onRevertWidgetEdit(widgetForm) {
809 if (widgetForm.$dirty) { 818 if (widgetForm.$dirty) {
810 widgetForm.$setPristine(); 819 widgetForm.$setPristine();
@@ -86,6 +86,24 @@ export default function DashboardRoutes($stateProvider) { @@ -86,6 +86,24 @@ export default function DashboardRoutes($stateProvider) {
86 label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}' 86 label: '{"icon": "dashboard", "label": "{{ vm.dashboard.title }}", "translate": "false"}'
87 } 87 }
88 }) 88 })
  89 + .state('dashboard', {
  90 + url: '/dashboard/:dashboardId?state',
  91 + reloadOnSearch: false,
  92 + module: 'private',
  93 + auth: ['TENANT_ADMIN', 'CUSTOMER_USER'],
  94 + views: {
  95 + "@": {
  96 + templateUrl: dashboardTemplate,
  97 + controller: 'DashboardController',
  98 + controllerAs: 'vm'
  99 + }
  100 + },
  101 + data: {
  102 + widgetEditMode: false,
  103 + searchEnabled: false,
  104 + pageTitle: 'dashboard.dashboard'
  105 + }
  106 + })
89 .state('home.customers.dashboards.dashboard', { 107 .state('home.customers.dashboards.dashboard', {
90 url: '/:dashboardId?state', 108 url: '/:dashboardId?state',
91 reloadOnSearch: false, 109 reloadOnSearch: false,
@@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
16 16
17 --> 17 -->
18 <md-content style="padding-top: 150px;" flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-button-id="dashboard-expand-button" 18 <md-content style="padding-top: 150px;" flex tb-expand-fullscreen="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-button-id="dashboard-expand-button"
19 - hide-expand-button="vm.widgetEditMode || vm.iframeMode || forceFullscreen" expand-tooltip-direction="bottom" ng-if="vm.dashboard"> 19 + hide-expand-button="vm.hideFullscreenButton()" expand-tooltip-direction="bottom" ng-if="vm.dashboard">
20 <section class="tb-dashboard-toolbar" ng-show="vm.showDashboardToolbar()" 20 <section class="tb-dashboard-toolbar" ng-show="vm.showDashboardToolbar()"
21 ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }"> 21 ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }">
22 <tb-dashboard-toolbar ng-show="!vm.widgetEditMode" force-fullscreen="forceFullscreen" 22 <tb-dashboard-toolbar ng-show="!vm.widgetEditMode" force-fullscreen="forceFullscreen"
@@ -41,8 +41,8 @@ @@ -41,8 +41,8 @@
41 </md-dialog-content> 41 </md-dialog-content>
42 <md-dialog-actions layout="row"> 42 <md-dialog-actions layout="row">
43 <span flex></span> 43 <span flex></span>
44 - <md-button ng-disabled="$root.loading || !theForm.$dirty || !theForm.$valid" type="submit" class="md-raised md-primary">  
45 - {{ 'action.save' | translate }} 44 + <md-button ng-disabled="$root.loading || !theForm.$valid" type="submit" class="md-raised md-primary">
  45 + {{ 'action.select' | translate }}
46 </md-button> 46 </md-button>
47 <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button> 47 <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
48 </md-dialog-actions> 48 </md-dialog-actions>
@@ -16,7 +16,8 @@ @@ -16,7 +16,8 @@
16 16
17 --> 17 -->
18 <div flex layout="column" style="margin-top: -10px;"> 18 <div flex layout="column" style="margin-top: -10px;">
19 - <div style="text-transform: uppercase; padding-bottom: 10px;">{{vm.item.type}}</div>  
20 - <div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'device.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div>  
21 - <div class="tb-small" ng-show="vm.isPublic()">{{'device.public' | translate}}</div> 19 + <div style="text-transform: uppercase; padding-bottom: 5px;">{{vm.item.type}}</div>
  20 + <div class="tb-card-description">{{vm.item.additionalInfo.description}}</div>
  21 + <div style="padding-top: 5px;" class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'device.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div>
  22 + <div style="padding-top: 5px;" class="tb-small" ng-show="vm.isPublic()">{{'device.public' | translate}}</div>
22 </div> 23 </div>
@@ -43,7 +43,7 @@ @@ -43,7 +43,7 @@
43 <fieldset ng-disabled="$root.loading"> 43 <fieldset ng-disabled="$root.loading">
44 <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index"> 44 <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
45 <span flex="5">{{$index + 1}}.</span> 45 <span flex="5">{{$index + 1}}.</span>
46 - <di class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center"> 46 + <div class="md-whiteframe-4dp tb-alias" flex layout="row" layout-align="start center">
47 <md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block"> 47 <md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block">
48 <input required name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias"> 48 <input required name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias">
49 <div ng-messages="aliasForm.alias.$error"> 49 <div ng-messages="aliasForm.alias.$error">
@@ -81,7 +81,7 @@ @@ -81,7 +81,7 @@
81 close 81 close
82 </md-icon> 82 </md-icon>
83 </md-button> 83 </md-button>
84 - </di> 84 + </div>
85 </div> 85 </div>
86 </fieldset> 86 </fieldset>
87 </div> 87 </div>
@@ -30,7 +30,7 @@ import AliasController from '../../api/alias-controller'; @@ -30,7 +30,7 @@ import AliasController from '../../api/alias-controller';
30 30
31 /*@ngInject*/ 31 /*@ngInject*/
32 export default function AttributeTableDirective($compile, $templateCache, $rootScope, $q, $mdEditDialog, $mdDialog, 32 export default function AttributeTableDirective($compile, $templateCache, $rootScope, $q, $mdEditDialog, $mdDialog,
33 - $mdUtil, $document, $translate, $filter, utils, types, dashboardUtils, 33 + $mdUtil, $document, $translate, $filter, $timeout, utils, types, dashboardUtils,
34 entityService, attributeService, widgetService) { 34 entityService, attributeService, widgetService) {
35 35
36 var linker = function (scope, element, attrs) { 36 var linker = function (scope, element, attrs) {
@@ -110,8 +110,15 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS @@ -110,8 +110,15 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
110 scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope); 110 scope.attributeScope = getAttributeScopeByValue(attrs.defaultAttributeScope);
111 } 111 }
112 112
113 - scope.enterFilterMode = function() { 113 + scope.enterFilterMode = function(event) {
  114 + let $button = angular.element(event.currentTarget);
  115 + let $toolbarsContainer = $button.closest('.toolbarsContainer');
  116 +
114 scope.query.search = ''; 117 scope.query.search = '';
  118 +
  119 + $timeout(()=>{
  120 + $toolbarsContainer.find('.searchInput').focus();
  121 + })
115 } 122 }
116 123
117 scope.exitFilterMode = function() { 124 scope.exitFilterMode = function() {
@@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
26 </md-select> 26 </md-select>
27 </md-input-container> 27 </md-input-container>
28 </section> 28 </section>
29 - <div class="md-whiteframe-z1" ng-class="{flex: mode==='widget'}"> 29 + <div class="md-whiteframe-z1 toolbarsContainer" ng-class="{flex: mode==='widget'}">
30 <md-toolbar class="md-table-toolbar md-default" ng-show="mode==='default' 30 <md-toolbar class="md-table-toolbar md-default" ng-show="mode==='default'
31 && !selectedAttributes.length 31 && !selectedAttributes.length
32 && query.search === null"> 32 && query.search === null">
@@ -39,7 +39,7 @@ @@ -39,7 +39,7 @@
39 {{ 'action.add' | translate }} 39 {{ 'action.add' | translate }}
40 </md-tooltip> 40 </md-tooltip>
41 </md-button> 41 </md-button>
42 - <md-button class="md-icon-button" ng-click="enterFilterMode()"> 42 + <md-button class="md-icon-button" ng-click="enterFilterMode($event)">
43 <md-icon>search</md-icon> 43 <md-icon>search</md-icon>
44 <md-tooltip md-direction="top"> 44 <md-tooltip md-direction="top">
45 {{ 'action.search' | translate }} 45 {{ 'action.search' | translate }}
@@ -65,7 +65,7 @@ @@ -65,7 +65,7 @@
65 </md-button> 65 </md-button>
66 <md-input-container flex> 66 <md-input-container flex>
67 <label>&nbsp;</label> 67 <label>&nbsp;</label>
68 - <input ng-model="query.search" placeholder="{{ 'common.enter-search' | translate }}"/> 68 + <input ng-model="query.search" class="searchInput" placeholder="{{ 'common.enter-search' | translate }}"/>
69 </md-input-container> 69 </md-input-container>
70 <md-button class="md-icon-button" aria-label="{{ 'action.back' | translate }}" ng-click="exitFilterMode()"> 70 <md-button class="md-icon-button" aria-label="{{ 'action.back' | translate }}" ng-click="exitFilterMode()">
71 <md-icon aria-label="{{ 'action.close' | translate }}" class="material-icons">close</md-icon> 71 <md-icon aria-label="{{ 'action.close' | translate }}" class="material-icons">close</md-icon>
@@ -22,14 +22,28 @@ import entitySelectTemplate from './entity-select.tpl.html'; @@ -22,14 +22,28 @@ import entitySelectTemplate from './entity-select.tpl.html';
22 /* eslint-enable import/no-unresolved, import/default */ 22 /* eslint-enable import/no-unresolved, import/default */
23 23
24 /*@ngInject*/ 24 /*@ngInject*/
25 -export default function EntitySelect($compile, $templateCache) { 25 +export default function EntitySelect($compile, $templateCache, entityService) {
26 26
27 var linker = function (scope, element, attrs, ngModelCtrl) { 27 var linker = function (scope, element, attrs, ngModelCtrl) {
28 var template = $templateCache.get(entitySelectTemplate); 28 var template = $templateCache.get(entitySelectTemplate);
29 element.html(template); 29 element.html(template);
30 30
31 scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false; 31 scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
32 - scope.model = {}; 32 +
  33 + var entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes, scope.useAliasEntityTypes);
  34 +
  35 + var entityTypeKeys = Object.keys(entityTypes);
  36 +
  37 + if (entityTypeKeys.length === 1) {
  38 + scope.displayEntityTypeSelect = false;
  39 + scope.defaultEntityType = entityTypes[entityTypeKeys[0]];
  40 + } else {
  41 + scope.displayEntityTypeSelect = true;
  42 + }
  43 +
  44 + scope.model = {
  45 + entityType: scope.defaultEntityType
  46 + };
33 47
34 scope.updateView = function () { 48 scope.updateView = function () {
35 if (!scope.disabled) { 49 if (!scope.disabled) {
@@ -54,7 +68,7 @@ export default function EntitySelect($compile, $templateCache) { @@ -54,7 +68,7 @@ export default function EntitySelect($compile, $templateCache) {
54 scope.model.entityType = value.entityType; 68 scope.model.entityType = value.entityType;
55 scope.model.entityId = value.id; 69 scope.model.entityId = value.id;
56 } else { 70 } else {
57 - scope.model.entityType = null; 71 + scope.model.entityType = scope.defaultEntityType;
58 scope.model.entityId = null; 72 scope.model.entityId = null;
59 } 73 }
60 initWatchers(); 74 initWatchers();
@@ -106,6 +120,7 @@ export default function EntitySelect($compile, $templateCache) { @@ -106,6 +120,7 @@ export default function EntitySelect($compile, $templateCache) {
106 theForm: '=?', 120 theForm: '=?',
107 tbRequired: '=?', 121 tbRequired: '=?',
108 disabled:'=ngDisabled', 122 disabled:'=ngDisabled',
  123 + allowedEntityTypes: "=?",
109 useAliasEntityTypes: "=?" 124 useAliasEntityTypes: "=?"
110 } 125 }
111 }; 126 };
@@ -17,10 +17,12 @@ @@ -17,10 +17,12 @@
17 --> 17 -->
18 <div layout='row' class="tb-entity-select"> 18 <div layout='row' class="tb-entity-select">
19 <tb-entity-type-select style="min-width: 100px;" 19 <tb-entity-type-select style="min-width: 100px;"
  20 + ng-if="displayEntityTypeSelect"
20 the-form="theForm" 21 the-form="theForm"
21 ng-disabled="disabled" 22 ng-disabled="disabled"
22 tb-required="tbRequired" 23 tb-required="tbRequired"
23 use-alias-entity-types="useAliasEntityTypes" 24 use-alias-entity-types="useAliasEntityTypes"
  25 + allowed-entity-types="allowedEntityTypes"
24 ng-model="model.entityType"> 26 ng-model="model.entityType">
25 </tb-entity-type-select> 27 </tb-entity-type-select>
26 <tb-entity-autocomplete flex ng-if="model.entityType" 28 <tb-entity-autocomplete flex ng-if="model.entityType"
@@ -35,7 +35,30 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m @@ -35,7 +35,30 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m
35 : $translate.instant('entity.any-entity'); 35 : $translate.instant('entity.any-entity');
36 scope.secondaryPlaceholder = '+' + $translate.instant('entity.entity-type'); 36 scope.secondaryPlaceholder = '+' + $translate.instant('entity.entity-type');
37 37
38 - var entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes); 38 + var entityTypes;
  39 +
  40 + if (scope.ignoreAuthorityFilter && scope.allowedEntityTypes
  41 + && scope.allowedEntityTypes.length) {
  42 + entityTypes = {};
  43 + scope.allowedEntityTypes.forEach((entityTypeValue) => {
  44 + var entityType = entityTypeFromValue(entityTypeValue);
  45 + if (entityType) {
  46 + entityTypes[entityType] = entityTypeValue;
  47 + }
  48 + });
  49 + } else {
  50 + entityTypes = entityService.prepareAllowedEntityTypesList(scope.allowedEntityTypes);
  51 + }
  52 +
  53 + function entityTypeFromValue(entityTypeValue) {
  54 + for (var entityType in types.entityType) {
  55 + if (types.entityType[entityType] === entityTypeValue) {
  56 + return entityType;
  57 + }
  58 + }
  59 + return null;
  60 + }
  61 +
39 scope.entityTypesList = []; 62 scope.entityTypesList = [];
40 for (var type in entityTypes) { 63 for (var type in entityTypes) {
41 var entityTypeInfo = {}; 64 var entityTypeInfo = {};
@@ -62,28 +85,43 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m @@ -62,28 +85,43 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m
62 } 85 }
63 86
64 ngModelCtrl.$render = function () { 87 ngModelCtrl.$render = function () {
65 - scope.entityTypeList = []; 88 + if (scope.entityTypeListWatch) {
  89 + scope.entityTypeListWatch();
  90 + scope.entityTypeListWatch = null;
  91 + }
  92 + var entityTypeList = [];
66 var value = ngModelCtrl.$viewValue; 93 var value = ngModelCtrl.$viewValue;
67 if (value && value.length) { 94 if (value && value.length) {
68 value.forEach(function(type) { 95 value.forEach(function(type) {
69 var entityTypeInfo = {}; 96 var entityTypeInfo = {};
70 entityTypeInfo.value = type; 97 entityTypeInfo.value = type;
71 entityTypeInfo.name = $translate.instant(types.entityTypeTranslations[entityTypeInfo.value].type) + ''; 98 entityTypeInfo.name = $translate.instant(types.entityTypeTranslations[entityTypeInfo.value].type) + '';
72 - scope.entityTypeList.push(entityTypeInfo); 99 + entityTypeList.push(entityTypeInfo);
73 }); 100 });
74 } 101 }
  102 + scope.entityTypeList = entityTypeList;
  103 + scope.entityTypeListWatch = scope.$watch('entityTypeList', function (newVal, prevVal) {
  104 + if (!angular.equals(newVal, prevVal)) {
  105 + updateEntityTypeList();
  106 + }
  107 + }, true);
75 } 108 }
76 109
77 - scope.$watch('entityTypeList', function () {  
78 - var values = []; 110 + function updateEntityTypeList() {
  111 + var values = ngModelCtrl.$viewValue;
  112 + if (!values) {
  113 + values = [];
  114 + ngModelCtrl.$setViewValue(values);
  115 + } else {
  116 + values.length = 0;
  117 + }
79 if (scope.entityTypeList && scope.entityTypeList.length) { 118 if (scope.entityTypeList && scope.entityTypeList.length) {
80 - scope.entityTypeList.forEach(function(entityType) { 119 + scope.entityTypeList.forEach(function (entityType) {
81 values.push(entityType.value); 120 values.push(entityType.value);
82 }); 121 });
83 } 122 }
84 - ngModelCtrl.$setViewValue(values);  
85 scope.updateValidity(); 123 scope.updateValidity();
86 - }, true); 124 + }
87 125
88 $compile(element.contents())(scope); 126 $compile(element.contents())(scope);
89 127
@@ -103,7 +141,8 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m @@ -103,7 +141,8 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m
103 scope: { 141 scope: {
104 disabled:'=ngDisabled', 142 disabled:'=ngDisabled',
105 tbRequired: '=?', 143 tbRequired: '=?',
106 - allowedEntityTypes: '=?' 144 + allowedEntityTypes: '=?',
  145 + ignoreAuthorityFilter: '=?'
107 } 146 }
108 }; 147 };
109 148
@@ -44,6 +44,10 @@ export default function RelationFilters($compile, $templateCache) { @@ -44,6 +44,10 @@ export default function RelationFilters($compile, $templateCache) {
44 scope.removeFilter = removeFilter; 44 scope.removeFilter = removeFilter;
45 45
46 ngModelCtrl.$render = function () { 46 ngModelCtrl.$render = function () {
  47 + if (scope.relationFiltersWatch) {
  48 + scope.relationFiltersWatch();
  49 + scope.relationFiltersWatch = null;
  50 + }
47 if (ngModelCtrl.$viewValue) { 51 if (ngModelCtrl.$viewValue) {
48 var value = ngModelCtrl.$viewValue; 52 var value = ngModelCtrl.$viewValue;
49 scope.relationFilters.length = 0; 53 scope.relationFilters.length = 0;
@@ -51,7 +55,7 @@ export default function RelationFilters($compile, $templateCache) { @@ -51,7 +55,7 @@ export default function RelationFilters($compile, $templateCache) {
51 scope.relationFilters.push(filter); 55 scope.relationFilters.push(filter);
52 }); 56 });
53 } 57 }
54 - scope.$watch('relationFilters', function (newVal, prevVal) { 58 + scope.relationFiltersWatch = scope.$watch('relationFilters', function (newVal, prevVal) {
55 if (!angular.equals(newVal, prevVal)) { 59 if (!angular.equals(newVal, prevVal)) {
56 updateValue(); 60 updateValue();
57 } 61 }
@@ -74,11 +78,16 @@ export default function RelationFilters($compile, $templateCache) { @@ -74,11 +78,16 @@ export default function RelationFilters($compile, $templateCache) {
74 } 78 }
75 79
76 function updateValue() { 80 function updateValue() {
77 - var value = []; 81 + var value = ngModelCtrl.$viewValue;
  82 + if (!value) {
  83 + value = [];
  84 + ngModelCtrl.$setViewValue(value);
  85 + } else {
  86 + value.length = 0;
  87 + }
78 scope.relationFilters.forEach(function (filter) { 88 scope.relationFilters.forEach(function (filter) {
79 value.push(filter); 89 value.push(filter);
80 }); 90 });
81 - ngModelCtrl.$setViewValue(value);  
82 } 91 }
83 $compile(element.contents())(scope); 92 $compile(element.contents())(scope);
84 } 93 }
@@ -41,7 +41,7 @@ export default function RelationTable() { @@ -41,7 +41,7 @@ export default function RelationTable() {
41 } 41 }
42 42
43 /*@ngInject*/ 43 /*@ngInject*/
44 -function RelationTableController($scope, $q, $mdDialog, $document, $translate, $filter, utils, types, entityRelationService) { 44 +function RelationTableController($scope, $q, $mdDialog, $document, $translate, $filter, $timeout, utils, types, entityRelationService) {
45 45
46 let vm = this; 46 let vm = this;
47 47
@@ -90,8 +90,15 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $ @@ -90,8 +90,15 @@ function RelationTableController($scope, $q, $mdDialog, $document, $translate, $
90 } 90 }
91 }); 91 });
92 92
93 - function enterFilterMode () { 93 + function enterFilterMode (event) {
  94 + let $button = angular.element(event.currentTarget);
  95 + let $toolbarsContainer = $button.closest('.toolbarsContainer');
  96 +
94 vm.query.search = ''; 97 vm.query.search = '';
  98 +
  99 + $timeout(()=>{
  100 + $toolbarsContainer.find('.searchInput').focus();
  101 + })
95 } 102 }
96 103
97 function exitFilterMode () { 104 function exitFilterMode () {
@@ -26,7 +26,7 @@ @@ -26,7 +26,7 @@
26 </md-select> 26 </md-select>
27 </md-input-container> 27 </md-input-container>
28 </section> 28 </section>
29 - <div layout="column" class="md-whiteframe-z1"> 29 + <div layout="column" class="md-whiteframe-z1 toolbarsContainer">
30 <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length 30 <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length
31 && vm.query.search === null"> 31 && vm.query.search === null">
32 <div class="md-toolbar-tools"> 32 <div class="md-toolbar-tools">
@@ -39,7 +39,7 @@ @@ -39,7 +39,7 @@
39 {{ 'action.add' | translate }} 39 {{ 'action.add' | translate }}
40 </md-tooltip> 40 </md-tooltip>
41 </md-button> 41 </md-button>
42 - <md-button class="md-icon-button" ng-click="vm.enterFilterMode()"> 42 + <md-button class="md-icon-button" ng-click="vm.enterFilterMode($event)">
43 <md-icon>search</md-icon> 43 <md-icon>search</md-icon>
44 <md-tooltip md-direction="top"> 44 <md-tooltip md-direction="top">
45 {{ 'action.search' | translate }} 45 {{ 'action.search' | translate }}
@@ -64,7 +64,7 @@ @@ -64,7 +64,7 @@
64 </md-button> 64 </md-button>
65 <md-input-container flex> 65 <md-input-container flex>
66 <label>&nbsp;</label> 66 <label>&nbsp;</label>
67 - <input ng-model="vm.query.search" placeholder="{{ 'common.enter-search' | translate }}"/> 67 + <input ng-model="vm.query.search" class="searchInput" placeholder="{{ 'common.enter-search' | translate }}"/>
68 </md-input-container> 68 </md-input-container>
69 <md-button class="md-icon-button" aria-label="{{ 'action.back' | translate }}" ng-click="vm.exitFilterMode()"> 69 <md-button class="md-icon-button" aria-label="{{ 'action.back' | translate }}" ng-click="vm.exitFilterMode()">
70 <md-icon aria-label="{{ 'action.close' | translate }}" class="material-icons">close</md-icon> 70 <md-icon aria-label="{{ 'action.close' | translate }}" class="material-icons">close</md-icon>