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 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <artifactId>application</artifactId>
... ...
... ... @@ -15,7 +15,7 @@
15 15 #
16 16
17 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 19 export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10"
20 20 export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark"
21 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 111 case DEVICE_NAME_OR_TYPE_UPDATE_TO_DEVICE_ACTOR_MSG:
112 112 case DEVICE_RPC_REQUEST_TO_DEVICE_ACTOR_MSG:
113 113 case SERVER_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG:
  114 + case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
114 115 onToDeviceActorMsg((TenantAwareMsg) msg);
115 116 break;
116 117 case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG:
... ...
... ... @@ -29,11 +29,7 @@ import org.thingsboard.server.common.msg.cluster.ServerAddress;
29 29 import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
30 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 35 * @author Andrew Shvayka
... ... @@ -88,7 +84,17 @@ public class RpcManagerActor extends ContextAwareActor {
88 84
89 85 private void onMsg(RpcBroadcastMsg msg) {
90 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 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 49 processor.onDeviceActorToRuleEngineMsg((DeviceActorToRuleEngineMsg) msg);
50 50 break;
51 51 case RULE_TO_RULE_CHAIN_TELL_NEXT_MSG:
  52 + case REMOTE_TO_RULE_CHAIN_TELL_NEXT_MSG:
52 53 processor.onTellNext((RuleNodeToRuleChainTellNextMsg) msg);
53 54 break;
54 55 case RULE_CHAIN_TO_RULE_CHAIN_MSG:
... ...
... ... @@ -20,6 +20,9 @@ import akka.actor.ActorRef;
20 20 import akka.actor.Props;
21 21 import akka.event.LoggingAdapter;
22 22 import com.datastax.driver.core.utils.UUIDs;
  23 +
  24 +import java.util.Optional;
  25 +
23 26 import org.thingsboard.server.actors.ActorSystemContext;
24 27 import org.thingsboard.server.actors.device.DeviceActorToRuleEngineMsg;
25 28 import org.thingsboard.server.actors.device.RuleEngineQueuePutAckMsg;
... ... @@ -37,6 +40,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
37 40 import org.thingsboard.server.common.data.rule.RuleNode;
38 41 import org.thingsboard.server.common.msg.TbMsg;
39 42 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
  43 +import org.thingsboard.server.common.msg.cluster.ServerAddress;
40 44 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
41 45 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
42 46 import org.thingsboard.server.dao.rule.RuleChainService;
... ... @@ -217,16 +221,36 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
217 221
218 222 void onTellNext(RuleNodeToRuleChainTellNextMsg envelope) {
219 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 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 248 int relationsCount = relations.size();
227 249 EntityId ackId = msg.getRuleNodeId() != null ? msg.getRuleNodeId() : msg.getRuleChainId();
228 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 254 } else if (relationsCount == 1) {
231 255 for (RuleNodeRelation relation : relations) {
232 256 pushToTarget(msg, relation.getOut(), relation.getType());
... ... @@ -244,7 +268,9 @@ public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleCh
244 268 }
245 269 }
246 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 20 import org.thingsboard.server.common.msg.MsgType;
21 21 import org.thingsboard.server.common.msg.TbActorMsg;
22 22 import org.thingsboard.server.common.msg.TbMsg;
  23 +import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
23 24
24 25 /**
25 26 * Created by ashvayka on 19.03.18.
26 27 */
27 28 @Data
28   -public final class RuleChainToRuleChainMsg implements TbActorMsg {
  29 +public final class RuleChainToRuleChainMsg implements TbActorMsg, RuleChainAwareMsg {
29 30
30 31 private final RuleChainId target;
31 32 private final RuleChainId source;
... ... @@ -34,6 +35,11 @@ public final class RuleChainToRuleChainMsg implements TbActorMsg {
34 35 private final boolean enqueue;
35 36
36 37 @Override
  38 + public RuleChainId getRuleChainId() {
  39 + return target;
  40 + }
  41 +
  42 + @Override
37 43 public MsgType getMsgType() {
38 44 return MsgType.RULE_CHAIN_TO_RULE_CHAIN_MSG;
39 45 }
... ...
... ... @@ -27,7 +27,7 @@ import java.util.Set;
27 27 * Created by ashvayka on 19.03.18.
28 28 */
29 29 @Data
30   -final class RuleNodeToRuleChainTellNextMsg implements TbActorMsg {
  30 +class RuleNodeToRuleChainTellNextMsg implements TbActorMsg {
31 31
32 32 private final RuleNodeId originator;
33 33 private final Set<String> relationTypes;
... ...
... ... @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.id.TenantId;
35 35 import org.thingsboard.server.common.data.rule.RuleChain;
36 36 import org.thingsboard.server.common.msg.TbActorMsg;
37 37 import org.thingsboard.server.common.msg.aware.DeviceAwareMsg;
  38 +import org.thingsboard.server.common.msg.aware.RuleChainAwareMsg;
38 39 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
39 40 import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg;
40 41 import scala.concurrent.duration.Duration;
... ... @@ -94,7 +95,8 @@ public class TenantActor extends RuleChainManagerActor {
94 95 onToDeviceActorMsg((DeviceAwareMsg) msg);
95 96 break;
96 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 100 break;
99 101 default:
100 102 return false;
... ... @@ -109,15 +111,19 @@ public class TenantActor extends RuleChainManagerActor {
109 111 }
110 112
111 113 private void onServiceToRuleEngineMsg(ServiceToRuleEngineMsg msg) {
  114 + if (ruleChainManager.getRootChainActor()!=null)
112 115 ruleChainManager.getRootChainActor().tell(msg, self());
  116 + else logger.info("[{}] No Root Chain", msg);
113 117 }
114 118
115 119 private void onDeviceActorToRuleEngineMsg(DeviceActorToRuleEngineMsg msg) {
  120 + if (ruleChainManager.getRootChainActor()!=null)
116 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 26 import org.springframework.web.bind.annotation.ResponseBody;
27 27 import org.springframework.web.bind.annotation.ResponseStatus;
28 28 import org.springframework.web.bind.annotation.RestController;
  29 +import org.thingsboard.server.common.data.EntityType;
29 30 import org.thingsboard.server.common.data.alarm.Alarm;
30 31 import org.thingsboard.server.common.data.alarm.AlarmId;
31 32 import org.thingsboard.server.common.data.alarm.AlarmInfo;
... ... @@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery;
33 34 import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
34 35 import org.thingsboard.server.common.data.alarm.AlarmSeverity;
35 36 import org.thingsboard.server.common.data.alarm.AlarmStatus;
  37 +import org.thingsboard.server.common.data.audit.ActionType;
36 38 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
37 39 import org.thingsboard.server.common.data.exception.ThingsboardException;
38 40 import org.thingsboard.server.common.data.id.EntityId;
... ... @@ -53,7 +55,6 @@ public class AlarmController extends BaseController {
53 55 checkParameter(ALARM_ID, strAlarmId);
54 56 try {
55 57 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
56   -
57 58 return checkAlarmId(alarmId);
58 59 } catch (Exception e) {
59 60 throw handleException(e);
... ... @@ -79,8 +80,14 @@ public class AlarmController extends BaseController {
79 80 public Alarm saveAlarm(@RequestBody Alarm alarm) throws ThingsboardException {
80 81 try {
81 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 88 } catch (Exception e) {
  89 + logEntityAction(emptyId(EntityType.ASSET), alarm,
  90 + null, alarm.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
84 91 throw handleException(e);
85 92 }
86 93 }
... ... @@ -92,8 +99,9 @@ public class AlarmController extends BaseController {
92 99 checkParameter(ALARM_ID, strAlarmId);
93 100 try {
94 101 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
95   - checkAlarmId(alarmId);
  102 + Alarm alarm = checkAlarmId(alarmId);
96 103 alarmService.ackAlarm(alarmId, System.currentTimeMillis()).get();
  104 + logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_ACK, null);
97 105 } catch (Exception e) {
98 106 throw handleException(e);
99 107 }
... ... @@ -106,8 +114,9 @@ public class AlarmController extends BaseController {
106 114 checkParameter(ALARM_ID, strAlarmId);
107 115 try {
108 116 AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
109   - checkAlarmId(alarmId);
  117 + Alarm alarm = checkAlarmId(alarmId);
110 118 alarmService.clearAlarm(alarmId, null, System.currentTimeMillis()).get();
  119 + logEntityAction(alarmId, alarm, getCurrentUser().getCustomerId(), ActionType.ALARM_CLEAR, null);
111 120 } catch (Exception e) {
112 121 throw handleException(e);
113 122 }
... ...
... ... @@ -529,18 +529,16 @@ public abstract class BaseController {
529 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 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 537 ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
539 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 542 ActionType actionType, Exception e, Object... additionalInfo) throws ThingsboardException {
545 543 if (customerId == null || customerId.isNullUid()) {
546 544 customerId = user.getCustomerId();
... ... @@ -556,8 +554,7 @@ public abstract class BaseController {
556 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 558 ActionType actionType, Object... additionalInfo) {
562 559 String msgType = null;
563 560 switch (actionType) {
... ...
... ... @@ -24,10 +24,13 @@ import org.springframework.web.bind.annotation.RequestParam;
24 24 import org.springframework.web.bind.annotation.ResponseBody;
25 25 import org.springframework.web.bind.annotation.ResponseStatus;
26 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 29 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
28 30 import org.thingsboard.server.common.data.exception.ThingsboardException;
29 31 import org.thingsboard.server.common.data.id.EntityId;
30 32 import org.thingsboard.server.common.data.id.EntityIdFactory;
  33 +import org.thingsboard.server.common.data.id.UUIDBased;
31 34 import org.thingsboard.server.common.data.relation.EntityRelation;
32 35 import org.thingsboard.server.common.data.relation.EntityRelationInfo;
33 36 import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
... ... @@ -58,7 +61,15 @@ public class EntityRelationController extends BaseController {
58 61 relation.setTypeGroup(RelationTypeGroup.COMMON);
59 62 }
60 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 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 73 throw handleException(e);
63 74 }
64 75 }
... ... @@ -81,12 +92,21 @@ public class EntityRelationController extends BaseController {
81 92 checkEntityId(fromId);
82 93 checkEntityId(toId);
83 94 RelationTypeGroup relationTypeGroup = parseRelationTypeGroup(strRelationTypeGroup, RelationTypeGroup.COMMON);
  95 + EntityRelation relation = new EntityRelation(fromId, toId, strRelationType, relationTypeGroup);
84 96 try {
85 97 Boolean found = relationService.deleteRelation(fromId, toId, strRelationType, relationTypeGroup);
86 98 if (!found) {
87 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 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 110 throw handleException(e);
91 111 }
92 112 }
... ... @@ -102,7 +122,9 @@ public class EntityRelationController extends BaseController {
102 122 checkEntityId(entityId);
103 123 try {
104 124 relationService.deleteEntityRelations(entityId);
  125 + logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, null);
105 126 } catch (Exception e) {
  127 + logEntityAction(entityId, null, getCurrentUser().getCustomerId(), ActionType.RELATIONS_DELETED, e);
106 128 throw handleException(e);
107 129 }
108 130 }
... ... @@ -210,8 +232,8 @@ public class EntityRelationController extends BaseController {
210 232 @RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {TO_ID, TO_TYPE})
211 233 @ResponseBody
212 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 237 checkParameter(TO_ID, strToId);
216 238 checkParameter(TO_TYPE, strToType);
217 239 EntityId entityId = EntityIdFactory.getByTypeAndId(strToType, strToId);
... ... @@ -276,10 +298,11 @@ public class EntityRelationController extends BaseController {
276 298
277 299 private RelationTypeGroup parseRelationTypeGroup(String strRelationTypeGroup, RelationTypeGroup defaultValue) {
278 300 RelationTypeGroup result = defaultValue;
279   - if (strRelationTypeGroup != null && strRelationTypeGroup.trim().length()>0) {
  301 + if (strRelationTypeGroup != null && strRelationTypeGroup.trim().length() > 0) {
280 302 try {
281 303 result = RelationTypeGroup.valueOf(strRelationTypeGroup);
282   - } catch (IllegalArgumentException e) { }
  304 + } catch (IllegalArgumentException e) {
  305 + }
283 306 }
284 307 return result;
285 308 }
... ...
... ... @@ -46,7 +46,6 @@ public class TbWebSocketHandler extends TextWebSocketHandler implements Telemetr
46 46 private static final ConcurrentMap<String, String> externalSessionMap = new ConcurrentHashMap<>();
47 47
48 48 @Autowired
49   - @Lazy
50 49 private TelemetryWebSocketService webSocketService;
51 50
52 51 @Override
... ...
... ... @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
22 22 import com.google.protobuf.InvalidProtocolBufferException;
23 23 import lombok.extern.slf4j.Slf4j;
24 24 import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.springframework.context.annotation.Lazy;
25 26 import org.springframework.stereotype.Service;
26 27 import org.thingsboard.rule.engine.api.RpcError;
27 28 import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
... ... @@ -67,6 +68,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
67 68 private ClusterRpcService rpcService;
68 69
69 70 @Autowired
  71 + @Lazy
70 72 private ActorService actorService;
71 73
72 74 private ScheduledExecutorService rpcCallBackExecutor;
... ...
... ... @@ -28,6 +28,7 @@ import lombok.Getter;
28 28 import lombok.extern.slf4j.Slf4j;
29 29 import org.springframework.beans.factory.annotation.Autowired;
30 30 import org.springframework.beans.factory.annotation.Value;
  31 +import org.springframework.context.annotation.Lazy;
31 32 import org.springframework.stereotype.Service;
32 33 import org.thingsboard.rule.engine.api.RpcError;
33 34 import org.thingsboard.server.actors.service.ActorService;
... ... @@ -102,6 +103,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
102 103 private AttributesService attributesService;
103 104
104 105 @Autowired
  106 + @Lazy
105 107 private ActorService actorService;
106 108
107 109 @Autowired
... ...
... ... @@ -432,7 +432,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
432 432 deviceSubscriptions.stream().filter(filter).forEach(s -> {
433 433 String sessionId = s.getWsSessionId();
434 434 List<TsKvEntry> subscriptionUpdate = f.apply(s);
435   - if (subscriptionUpdate == null || !subscriptionUpdate.isEmpty()) {
  435 + if (subscriptionUpdate != null && !subscriptionUpdate.isEmpty()) {
436 436 SubscriptionUpdate update = new SubscriptionUpdate(s.getSubscriptionId(), subscriptionUpdate);
437 437 if (s.isLocal()) {
438 438 updateSubscriptionState(sessionId, s, update);
... ...
... ... @@ -225,7 +225,7 @@ sql:
225 225 # Actor system parameters
226 226 actors:
227 227 tenant:
228   - create_components_on_init: true
  228 + create_components_on_init: "${ACTORS_TENANT_CREATE_COMPONENTS_ON_INIT:true}"
229 229 session:
230 230 max_concurrent_sessions_per_device: "${ACTORS_MAX_CONCURRENT_SESSION_PER_DEVICE:1}"
231 231 sync:
... ... @@ -328,6 +328,13 @@ spring.mvc.cors:
328 328 max-age: "1800"
329 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 338 # HSQLDB DAO Configuration
332 339 spring:
333 340 data:
... ... @@ -378,6 +385,7 @@ audit_log:
378 385 "customer": "${AUDIT_LOG_MASK_CUSTOMER:W}"
379 386 "user": "${AUDIT_LOG_MASK_USER:W}"
380 387 "rule_chain": "${AUDIT_LOG_MASK_RULE_CHAIN:W}"
  388 + "alarm": "${AUDIT_LOG_MASK_ALARM:W}"
381 389 sink:
382 390 # Type of external sink. possible options: none, elasticsearch
383 391 type: "${AUDIT_LOG_SINK_TYPE:none}"
... ...
... ... @@ -30,13 +30,13 @@ export SQL_DATA_FOLDER=${SQL_DATA_FOLDER:-/tmp}
30 30
31 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 34 -Dinstall.data_dir=${installDir} \
35 35 -Dinstall.load_demo=${loadDemo} \
36 36 -Dspring.jpa.hibernate.ddl-auto=none \
37 37 -Dinstall.upgrade=false \
38 38 -Dlogging.config=logback.xml \
39   - org.springframework.boot.loader.PropertiesLauncher" "$run_user"
  39 + org.springframework.boot.loader.PropertiesLauncher"
40 40
41 41 if [ $? -ne 0 ]; then
42 42 echo "ThingsBoard DB installation failed!"
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -31,7 +31,12 @@ public enum ActionType {
31 31 ACTIVATED(false), // log string id
32 32 SUSPENDED(false), // log string id
33 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 41 private final boolean isRead;
37 42
... ...
... ... @@ -60,4 +60,5 @@ public class EntityIdFactory {
60 60 }
61 61 throw new IllegalArgumentException("EntityType " + type + " is not supported!");
62 62 }
  63 +
63 64 }
... ...
... ... @@ -33,13 +33,19 @@ public class RelationsSearchParameters {
33 33 private UUID rootId;
34 34 private EntityType rootType;
35 35 private EntitySearchDirection direction;
  36 + private RelationTypeGroup relationTypeGroup;
36 37 private int maxLevel = 1;
37 38
38 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 44 this.rootId = entityId.getId();
40 45 this.rootType = entityId.getEntityType();
41 46 this.direction = direction;
42 47 this.maxLevel = maxLevel;
  48 + this.relationTypeGroup = relationTypeGroup;
43 49 }
44 50
45 51 public EntityId getEntityId() {
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -63,6 +63,11 @@ public enum MsgType {
63 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 71 * Message that is sent by RuleActor implementation to RuleActor itself to log the error.
67 72 */
68 73 RULE_TO_SELF_ERROR_MSG,
... ... @@ -101,6 +106,10 @@ public enum MsgType {
101 106 /**
102 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 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>common</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.common</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <artifactId>dao</artifactId>
... ...
... ... @@ -40,15 +40,14 @@ public interface AuditLogService {
40 40
41 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 43 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
44 44 import org.thingsboard.server.common.data.page.TimePageData;
45 45 import org.thingsboard.server.common.data.page.TimePageLink;
  46 +import org.thingsboard.server.common.data.relation.EntityRelation;
46 47 import org.thingsboard.server.common.data.rule.RuleChainMetaData;
47 48 import org.thingsboard.server.common.data.security.DeviceCredentials;
48 49 import org.thingsboard.server.dao.audit.sink.AuditLogSink;
... ... @@ -115,7 +116,7 @@ public class AuditLogServiceImpl implements AuditLogService {
115 116 }
116 117
117 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 120 logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity,
120 121 ActionType actionType, Exception e, Object... additionalInfo) {
121 122 if (canLog(entityId.getEntityType(), actionType)) {
... ... @@ -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 161 ActionType actionType,
162 162 Object... additionalInfo) {
163 163 ObjectNode actionData = objectMapper.createObjectNode();
164 164 switch(actionType) {
165 165 case ADDED:
166 166 case UPDATED:
  167 + case ALARM_ACK:
  168 + case ALARM_CLEAR:
  169 + case RELATIONS_DELETED:
167 170 if (entity != null) {
168 171 ObjectNode entityNode = objectMapper.valueToTree(entity);
169 172 if (entityId.getEntityType() == EntityType.DASHBOARD) {
... ... @@ -240,6 +243,11 @@ public class AuditLogServiceImpl implements AuditLogService {
240 243 actionData.put("unassignedCustomerId", strCustomerId);
241 244 actionData.put("unassignedCustomerName", strCustomerName);
242 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 252 return actionData;
245 253 }
... ...
... ... @@ -57,7 +57,7 @@ public class DummyAuditLogServiceImpl implements AuditLogService {
57 57 }
58 58
59 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 61 return null;
62 62 }
63 63
... ...
... ... @@ -16,8 +16,10 @@
16 16 package org.thingsboard.server.dao.cache;
17 17
18 18 import com.github.benmanes.caffeine.cache.Caffeine;
  19 +import com.github.benmanes.caffeine.cache.RemovalCause;
19 20 import com.github.benmanes.caffeine.cache.Ticker;
20 21 import lombok.Data;
  22 +import lombok.extern.slf4j.Slf4j;
21 23 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
22 24 import org.springframework.boot.context.properties.ConfigurationProperties;
23 25 import org.springframework.cache.CacheManager;
... ... @@ -28,6 +30,7 @@ import org.springframework.cache.support.SimpleCacheManager;
28 30 import org.springframework.context.annotation.Bean;
29 31 import org.springframework.context.annotation.Configuration;
30 32
  33 +import java.util.Arrays;
31 34 import java.util.List;
32 35 import java.util.Map;
33 36 import java.util.concurrent.TimeUnit;
... ... @@ -38,12 +41,14 @@ import java.util.stream.Collectors;
38 41 @ConfigurationProperties(prefix = "caffeine")
39 42 @EnableCaching
40 43 @Data
  44 +@Slf4j
41 45 public class CaffeineCacheConfiguration {
42 46
43 47 private Map<String, CacheSpecs> specs;
44 48
45 49 @Bean
46 50 public CacheManager cacheManager() {
  51 + log.trace("Initializing cache: {}", Arrays.toString(RemovalCause.values()));
47 52 SimpleCacheManager manager = new SimpleCacheManager();
48 53 if (specs != null) {
49 54 List<CaffeineCache> caches =
... ...
... ... @@ -402,7 +402,7 @@ public class BaseRelationService implements RelationService {
402 402 int maxLvl = params.getMaxLevel() > 0 ? params.getMaxLevel() : Integer.MAX_VALUE;
403 403
404 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 406 return Futures.transform(relationSet, input -> {
407 407 List<EntityRelation> relations = new ArrayList<>();
408 408 if (filters == null || filters.isEmpty()) {
... ... @@ -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 523 final ConcurrentHashMap<EntityId, Boolean> uniqueMap) throws Exception {
523 524 if (lvl == 0) {
524 525 return Futures.immediateFuture(Collections.emptySet());
525 526 }
526 527 lvl--;
527 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 530 Set<EntityId> childrenIds = new HashSet<>();
530 531 for (EntityRelation childRelation : children) {
531 532 log.trace("Found Relation: {}", childRelation);
... ... @@ -544,7 +545,7 @@ public class BaseRelationService implements RelationService {
544 545 }
545 546 List<ListenableFuture<Set<EntityRelation>>> futures = new ArrayList<>();
546 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 550 //TODO: try to remove this blocking operation
550 551 List<Set<EntityRelation>> relations = Futures.successfulAsList(futures).get();
... ... @@ -552,12 +553,15 @@ public class BaseRelationService implements RelationService {
552 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 557 ListenableFuture<List<EntityRelation>> relations;
  558 + if (relationTypeGroup == null) {
  559 + relationTypeGroup = RelationTypeGroup.COMMON;
  560 + }
557 561 if (direction == EntitySearchDirection.FROM) {
558   - relations = findByFromAsync(rootId, RelationTypeGroup.COMMON);
  562 + relations = findByFromAsync(rootId, relationTypeGroup);
559 563 } else {
560   - relations = findByToAsync(rootId, RelationTypeGroup.COMMON);
  564 + relations = findByToAsync(rootId, relationTypeGroup);
561 565 }
562 566 return relations;
563 567 }
... ...
1   -VERSION=2.0.3
  1 +VERSION=2.1.0
2 2 PROJECT=thingsboard
3 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 2 PROJECT=thingsboard
3 3 APP=cassandra
4 4
... ...
... ... @@ -18,7 +18,7 @@ version: '2'
18 18
19 19 services:
20 20 tb:
21   - image: "thingsboard/application:2.0.3"
  21 + image: "thingsboard/application:2.1.0"
22 22 ports:
23 23 - "8080:8080"
24 24 - "1883:1883"
... ...
... ... @@ -22,7 +22,7 @@ spec:
22 22 containers:
23 23 - name: cassandra-setup
24 24 imagePullPolicy: Always
25   - image: thingsboard/cassandra-setup:2.0.3
  25 + image: thingsboard/cassandra-setup:2.1.0
26 26 env:
27 27 - name: ADD_DEMO_DATA
28 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 54 topologyKey: "kubernetes.io/hostname"
55 55 containers:
56 56 - name: cassandra
57   - image: thingsboard/cassandra:2.0.3
  57 + image: thingsboard/cassandra:2.1.0
58 58 imagePullPolicy: Always
59 59 ports:
60 60 - containerPort: 7000
... ...
... ... @@ -84,7 +84,7 @@ spec:
84 84 containers:
85 85 - name: tb
86 86 imagePullPolicy: Always
87   - image: thingsboard/application:2.0.3
  87 + image: thingsboard/application:2.1.0
88 88 ports:
89 89 - containerPort: 8080
90 90 name: ui
... ...
... ... @@ -87,7 +87,7 @@ spec:
87 87 containers:
88 88 - name: zk
89 89 imagePullPolicy: Always
90   - image: thingsboard/zk:2.0.3
  90 + image: thingsboard/zk:2.1.0
91 91 ports:
92 92 - containerPort: 2181
93 93 name: client
... ...
1   -VERSION=2.0.3
  1 +VERSION=2.1.0
2 2 PROJECT=thingsboard
3 3 APP=application
4 4
... ...
1   -VERSION=2.0.3
  1 +VERSION=2.1.0
2 2 PROJECT=thingsboard
3 3 APP=zk
4 4
... ...
... ... @@ -19,12 +19,12 @@
19 19 <modelVersion>4.0.0</modelVersion>
20 20 <parent>
21 21 <groupId>org.thingsboard</groupId>
22   - <version>2.0.3</version>
  22 + <version>2.1.0-SNAPSHOT</version>
23 23 <artifactId>thingsboard</artifactId>
24 24 </parent>
25 25 <groupId>org.thingsboard</groupId>
26 26 <artifactId>netty-mqtt</artifactId>
27   - <version>2.0.3</version>
  27 + <version>2.1.0-SNAPSHOT</version>
28 28 <packaging>jar</packaging>
29 29
30 30 <name>Netty MQTT Client</name>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <groupId>org.thingsboard</groupId>
22 22 <artifactId>thingsboard</artifactId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <packaging>pom</packaging>
25 25
26 26 <name>Thingsboard</name>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <artifactId>rule-engine</artifactId>
... ...
... ... @@ -22,7 +22,7 @@
22 22 <modelVersion>4.0.0</modelVersion>
23 23 <parent>
24 24 <groupId>org.thingsboard</groupId>
25   - <version>2.0.3</version>
  25 + <version>2.1.0-SNAPSHOT</version>
26 26 <artifactId>rule-engine</artifactId>
27 27 </parent>
28 28 <groupId>org.thingsboard.rule-engine</groupId>
... ...
... ... @@ -22,7 +22,7 @@
22 22 <modelVersion>4.0.0</modelVersion>
23 23 <parent>
24 24 <groupId>org.thingsboard</groupId>
25   - <version>2.0.3</version>
  25 + <version>2.1.0-SNAPSHOT</version>
26 26 <artifactId>rule-engine</artifactId>
27 27 </parent>
28 28 <groupId>org.thingsboard.rule-engine</groupId>
... ...
... ... @@ -47,11 +47,12 @@ import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS;
47 47
48 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 52 private TbMsgGeneratorNodeConfiguration config;
53 53 private ScriptEngine jsEngine;
54 54 private long delay;
  55 + private long lastScheduledTs;
55 56 private EntityId originatorId;
56 57 private UUID nextTickId;
57 58 private TbMsg prevMsg;
... ... @@ -66,28 +67,40 @@ public class TbMsgGeneratorNode implements TbNode {
66 67 originatorId = ctx.getSelfId();
67 68 }
68 69 this.jsEngine = ctx.createJsScriptEngine(config.getJsScript(), "prevMsg", "prevMetadata", "prevMsgType");
69   - sentTickMsg(ctx);
  70 + scheduleTickMsg(ctx);
70 71 }
71 72
72 73 @Override
73 74 public void onMsg(TbContext ctx, TbMsg msg) {
74 75 if (msg.getType().equals(TB_MSG_GENERATOR_NODE_MSG) && msg.getId().equals(nextTickId)) {
75 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 95 TbMsg tickMsg = ctx.newMsg(TB_MSG_GENERATOR_NODE_MSG, ctx.getSelfId(), new TbMsgMetaData(), "");
83 96 nextTickId = tickMsg.getId();
84   - ctx.tellSelf(tickMsg, delay);
  97 + ctx.tellSelf(tickMsg, curDelay);
85 98 }
86 99
87 100 private ListenableFuture<TbMsg> generate(TbContext ctx) {
88 101 return ctx.getJsExecutor().executeAsync(() -> {
89 102 if (prevMsg == null) {
90   - prevMsg = ctx.newMsg( "", originatorId, new TbMsgMetaData(), "{}");
  103 + prevMsg = ctx.newMsg("", originatorId, new TbMsgMetaData(), "{}");
91 104 }
92 105 TbMsg generated = jsEngine.executeGenerate(prevMsg);
93 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 4 //# sourceMappingURL=rulenode-core-config.js.map
\ No newline at end of file
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.transport</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.transport</groupId>
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>transport</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard.transport</groupId>
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.mqtt.session;
17 17
18 18 import com.google.gson.JsonArray;
19 19 import com.google.gson.JsonElement;
  20 +import com.google.gson.JsonNull;
20 21 import com.google.gson.JsonObject;
21 22 import com.google.gson.JsonSyntaxException;
22 23 import io.netty.channel.ChannelHandlerContext;
... ... @@ -240,7 +241,7 @@ public class GatewaySessionCtx {
240 241
241 242 private String getDeviceType(JsonElement json) throws AdaptorException {
242 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 247 private JsonElement getJson(MqttPublishMessage mqttMsg) throws AdaptorException {
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard</groupId>
... ...
1 1 {
2 2 "name": "thingsboard",
3 3 "private": true,
4   - "version": "2.0.3",
  4 + "version": "2.1.0",
5 5 "description": "Thingsboard UI",
6 6 "licenses": [
7 7 {
... ... @@ -15,7 +15,6 @@
15 15 },
16 16 "dependencies": {
17 17 "@flowjs/ng-flow": "^2.7.1",
18   - "ace-builds": "1.3.1",
19 18 "angular": "1.5.8",
20 19 "angular-animate": "1.5.8",
21 20 "angular-aria": "1.5.8",
... ... @@ -37,17 +36,17 @@
37 36 "angular-socialshare": "^2.3.8",
38 37 "angular-storage": "0.0.15",
39 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 45 "angular-ui-ace": "^0.2.3",
47 46 "angular-ui-router": "^0.3.1",
48 47 "angular-websocket": "^2.0.1",
49 48 "base64-js": "^1.2.1",
50   - "brace": "^0.8.0",
  49 + "brace": "^0.10.0",
51 50 "canvas-gauges": "^2.0.9",
52 51 "clipboard": "^1.5.15",
53 52 "compass-sass-mixins": "^0.12.7",
... ... @@ -96,6 +95,7 @@
96 95 "babel-loader": "^6.2.5",
97 96 "babel-preset-es2015": "^6.14.0",
98 97 "babel-preset-react": "^6.16.0",
  98 + "compression-webpack-plugin": "^1.1.11",
99 99 "connect-history-api-fallback": "^1.3.0",
100 100 "copy-webpack-plugin": "^3.0.1",
101 101 "cross-env": "^3.2.4",
... ... @@ -127,7 +127,9 @@
127 127 "webpack-dev-middleware": "^1.6.1",
128 128 "webpack-dev-server": "^1.15.1",
129 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 134 "engine": "node >= 5.9.0",
133 135 "nyc": {
... ...
... ... @@ -20,7 +20,7 @@
20 20 <modelVersion>4.0.0</modelVersion>
21 21 <parent>
22 22 <groupId>org.thingsboard</groupId>
23   - <version>2.0.3</version>
  23 + <version>2.1.0-SNAPSHOT</version>
24 24 <artifactId>thingsboard</artifactId>
25 25 </parent>
26 26 <groupId>org.thingsboard</groupId>
... ...
... ... @@ -146,6 +146,7 @@ export default class AliasController {
146 146 newDatasource.entityId = resolvedEntity.id;
147 147 newDatasource.entityType = resolvedEntity.entityType;
148 148 newDatasource.entityName = resolvedEntity.name;
  149 + newDatasource.entityDescription = resolvedEntity.entityDescription
149 150 newDatasource.name = resolvedEntity.name;
150 151 newDatasource.generated = i > 0 ? true : false;
151 152 datasources.push(newDatasource);
... ... @@ -167,6 +168,7 @@ export default class AliasController {
167 168 datasource.entityType = entity.entityType;
168 169 datasource.entityName = entity.name;
169 170 datasource.name = entity.name;
  171 + datasource.entityDescription = entity.entityDescription;
170 172 deferred.resolve([datasource]);
171 173 } else {
172 174 if (aliasInfo.stateEntity) {
... ...
... ... @@ -252,7 +252,7 @@ function DashboardService($rootScope, $http, $q, $location, $filter) {
252 252 if (port != 80 && port != 443) {
253 253 url += ":" + port;
254 254 }
255   - url += "/dashboards/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId;
  255 + url += "/dashboard/" + dashboard.id.id + "?publicId=" + dashboard.publicCustomerId;
256 256 return url;
257 257 }
258 258
... ...
... ... @@ -329,7 +329,7 @@ function EntityService($http, $q, $filter, $translate, $log, userService, device
329 329 }
330 330
331 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 335 function entityRelationInfoToEntityInfo(entityRelationInfo, direction) {
... ...
... ... @@ -32,6 +32,7 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
32 32 getRuleNodeComponents: getRuleNodeComponents,
33 33 getRuleNodeComponentByClazz: getRuleNodeComponentByClazz,
34 34 getRuleNodeSupportedLinks: getRuleNodeSupportedLinks,
  35 + ruleNodeAllowCustomLinks: ruleNodeAllowCustomLinks,
35 36 resolveTargetRuleChains: resolveTargetRuleChains,
36 37 testScript: testScript,
37 38 getLatestRuleNodeDebugInput: getLatestRuleNodeDebugInput
... ... @@ -127,21 +128,21 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
127 128
128 129 function getRuleNodeSupportedLinks(component) {
129 130 var relationTypes = component.configurationDescriptor.nodeDefinition.relationTypes;
130   - var customRelations = component.configurationDescriptor.nodeDefinition.customRelations;
131   - var linkLabels = [];
  131 + var linkLabels = {};
132 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 139 return linkLabels;
143 140 }
144 141
  142 + function ruleNodeAllowCustomLinks(component) {
  143 + return component.configurationDescriptor.nodeDefinition.customRelations;
  144 + }
  145 +
145 146 function getRuleNodeComponents() {
146 147 var deferred = $q.defer();
147 148 if (ruleNodeComponents) {
... ... @@ -226,7 +227,10 @@ function RuleChainService($http, $q, $filter, $ocLazyLoad, $translate, types, co
226 227 if (res && res.length) {
227 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 236 function resolveTargetRuleChains(ruleChainConnections) {
... ...
... ... @@ -32,84 +32,7 @@ const MAX_LIMIT = 500;
32 32 /*@ngInject*/
33 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 37 var service = {
115 38 minIntervalLimit: minIntervalLimit,
... ... @@ -166,6 +89,7 @@ function TimeService($translate, types) {
166 89 min = boundMinInterval(min);
167 90 max = boundMaxInterval(max);
168 91 var intervals = [];
  92 + initPredefIntervals();
169 93 for (var i in predefIntervals) {
170 94 var interval = predefIntervals[i];
171 95 if (interval.value >= min && interval.value <= max) {
... ... @@ -175,6 +99,89 @@ function TimeService($translate, types) {
175 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 185 function matchesExistingInterval(min, max, intervalMs) {
179 186 var intervals = getIntervals(min, max);
180 187 for (var i in intervals) {
... ...
... ... @@ -488,7 +488,8 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
488 488 } else {
489 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 493 return false;
493 494 } else {
494 495 return true;
... ... @@ -504,10 +505,10 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, logi
504 505 var place = 'home.links';
505 506 if (currentUser.authority === 'TENANT_ADMIN' || currentUser.authority === 'CUSTOMER_USER') {
506 507 if (userHasDefaultDashboard()) {
507   - place = 'home.dashboards.dashboard';
  508 + place = $rootScope.forceFullscreen ? 'dashboard' : 'home.dashboards.dashboard';
508 509 params = {dashboardId: currentUserDetails.additionalInfo.defaultDashboardId};
509 510 } else if (isPublic()) {
510   - place = 'home.dashboards.dashboard';
  511 + place = 'dashboard';
511 512 params = {dashboardId: lastPublicDashboardId};
512 513 }
513 514 } else if (currentUser.authority === 'SYS_ADMIN') {
... ...
... ... @@ -15,10 +15,6 @@
15 15 */
16 16 import injectTapEventPlugin from 'react-tap-event-plugin';
17 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 19 /* eslint-disable import/no-unresolved, import/default */
24 20
... ... @@ -38,46 +34,28 @@ export default function AppConfig($provide,
38 34 $mdThemingProvider,
39 35 $httpProvider,
40 36 $translateProvider,
41   - storeProvider,
42   - locales) {
  37 + storeProvider) {
43 38
44 39 injectTapEventPlugin();
45 40 $locationProvider.html5Mode(true);
46 41 $urlRouterProvider.otherwise(UrlHandler);
47 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 60 $httpProvider.interceptors.push('globalInterceptor');
83 61
... ... @@ -168,4 +146,24 @@ export default function AppConfig($provide,
168 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 }
\ No newline at end of file
... ...
... ... @@ -51,7 +51,7 @@ import react from 'ngreact';
51 51 import '@flowjs/ng-flow/dist/ng-flow-standalone.min';
52 52 import 'ngFlowchart/dist/ngFlowchart';
53 53
54   -import thingsboardLocales from './locale/locale.constant';
  54 +import thingsboardTranslateHandler from './locale/translate-handler';
55 55 import thingsboardLogin from './login';
56 56 import thingsboardDialogs from './components/datakey-config-dialog.controller';
57 57 import thingsboardMenu from './services/menu.service';
... ... @@ -117,7 +117,7 @@ angular.module('thingsboard', [
117 117 react.name,
118 118 'flow',
119 119 'flowchart',
120   - thingsboardLocales,
  120 + thingsboardTranslateHandler,
121 121 thingsboardLogin,
122 122 thingsboardDialogs,
123 123 thingsboardMenu,
... ...
... ... @@ -113,7 +113,10 @@ export default function AppRun($rootScope, $window, $injector, $location, $log,
113 113 showForbiddenDialog();
114 114 } else if (to.redirectTo) {
115 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 122 } else {
... ... @@ -138,7 +141,7 @@ export default function AppRun($rootScope, $window, $injector, $location, $log,
138 141 $rootScope.pageTitle = 'ThingsBoard';
139 142
140 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 145 $location.search('publicId', userService.getPublicId());
143 146 userService.updateLastPublicDashboardId(params.dashboardId);
144 147 }
... ...
... ... @@ -195,6 +195,21 @@ export default angular.module('thingsboard.types', [])
195 195 },
196 196 "ATTRIBUTES_READ": {
197 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 215 auditLogActionStatus: {
... ... @@ -366,6 +381,12 @@ export default angular.module('thingsboard.types', [])
366 381 list: 'entity.list-of-rulechains',
367 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 390 "CURRENT_CUSTOMER": {
370 391 type: 'entity.type-current-customer',
371 392 list: 'entity.type-current-customer'
... ... @@ -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 550 inputNodeComponent: {
514 551 type: 'INPUT',
515 552 name: 'Input',
... ... @@ -565,6 +602,75 @@ export default angular.module('thingsboard.types', [])
565 602 nodeClass: "tb-input-type",
566 603 icon: "input",
567 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 676 valueType: {
... ...
... ... @@ -40,12 +40,12 @@ div.tb-widget {
40 40 position: absolute;
41 41 top: 8px;
42 42 right: 8px;
43   - z-index: 1;
44   - margin: 0px;
  43 + z-index: 19;
  44 + margin: 0;
45 45
46 46 .md-button.md-icon-button {
47   - margin: 0px !important;
48   - padding: 0px !important;
  47 + margin: 0 !important;
  48 + padding: 0 !important;
49 49 line-height: 20px;
50 50 width: 32px;
51 51 height: 32px;
... ...
... ... @@ -60,7 +60,7 @@
60 60 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
61 61 <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
62 62 </div>
63   - <div layout="row" flex>
  63 + <div layout="row">
64 64 <div class="tb-chip-label">
65 65 {{$chip.label}}
66 66 </div>
... ... @@ -112,7 +112,7 @@
112 112 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
113 113 <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
114 114 </div>
115   - <div layout="row" flex>
  115 + <div layout="row">
116 116 <div class="tb-chip-label">
117 117 {{$chip.label}}
118 118 </div>
... ... @@ -164,7 +164,7 @@
164 164 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
165 165 <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
166 166 </div>
167   - <div layout="row" flex>
  167 + <div layout="row">
168 168 <div class="tb-chip-label">
169 169 {{$chip.label}}
170 170 </div>
... ...
... ... @@ -61,7 +61,7 @@
61 61 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
62 62 <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
63 63 </div>
64   - <div layout="row" flex>
  64 + <div layout="row">
65 65 <div class="tb-chip-label">
66 66 {{$chip.label}}
67 67 </div>
... ... @@ -112,7 +112,7 @@
112 112 <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
113 113 <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
114 114 </div>
115   - <div layout="row" flex>
  115 + <div layout="row">
116 116 <div class="tb-chip-label">
117 117 {{$chip.label}}
118 118 </div>
... ...
... ... @@ -59,14 +59,4 @@ md-sidenav.tb-sidenav-details {
59 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 18 import 'brace/ext/language_tools';
19 19 import 'brace/mode/json';
20 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 24 import fixAceEditor from './ace-editor-fix';
25 25
... ...
... ... @@ -17,7 +17,7 @@ import './json-object-edit.scss';
17 17
18 18 import 'brace/ext/language_tools';
19 19 import 'brace/mode/json';
20   -import 'ace-builds/src-min-noconflict/snippets/json';
  20 +import 'brace/snippets/json';
21 21
22 22 import fixAceEditor from './ace-editor-fix';
23 23
... ...
... ... @@ -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 118 vm.query.search = '';
  119 +
  120 + $timeout(()=>{
  121 + $toolbarsContainer.find('.searchInput').focus();
  122 + })
116 123 }
117 124
118 125 function exitFilterMode () {
... ...
... ... @@ -15,7 +15,7 @@
15 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 19 <md-toolbar class="md-table-toolbar md-default" ng-show="vm.query.search === null">
20 20 <div class="md-toolbar-tools">
21 21 <span translate>widget-config.actions</span>
... ... @@ -26,7 +26,7 @@
26 26 {{ 'widget-config.add-action' | translate }}
27 27 </md-tooltip>
28 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 30 <md-icon>search</md-icon>
31 31 <md-tooltip md-direction="top">
32 32 {{ 'action.search' | translate }}
... ... @@ -44,7 +44,7 @@
44 44 </md-button>
45 45 <md-input-container flex>
46 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 48 </md-input-container>
49 49 <md-button class="md-icon-button" aria-label="Close" ng-click="vm.exitFilterMode()">
50 50 <md-icon aria-label="Close" class="material-icons">close</md-icon>
... ...
... ... @@ -187,17 +187,17 @@
187 187 </div>
188 188 <div layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
189 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 191 ng-model="showTitle">{{ 'widget-config.display-title' | translate }}
192 192 </md-checkbox>
193 193 </div>
194 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 196 ng-model="dropShadow">{{ 'widget-config.drop-shadow' | translate }}
197 197 </md-checkbox>
198 198 </div>
199 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 201 ng-model="enableFullscreen">{{ 'widget-config.enable-fullscreen' | translate }}
202 202 </md-checkbox>
203 203 </div>
... ...
... ... @@ -479,7 +479,11 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele
479 479 dashboardId: targetDashboardId,
480 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 487 break;
484 488 case types.widgetActionTypes.custom.value:
485 489 var customFunction = descriptor.customFunction;
... ...
... ... @@ -196,6 +196,7 @@ export default function DashboardController(types, utils, dashboardUtils, widget
196 196 vm.displayDashboardTimewindow = displayDashboardTimewindow;
197 197 vm.displayDashboardsSelect = displayDashboardsSelect;
198 198 vm.displayEntitiesSelect = displayEntitiesSelect;
  199 + vm.hideFullscreenButton = hideFullscreenButton;
199 200
200 201 vm.widgetsBundle;
201 202
... ... @@ -258,7 +259,11 @@ export default function DashboardController(types, utils, dashboardUtils, widget
258 259 dashboardId: vm.currentDashboardId
259 260 });
260 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 810 }
806 811 }
807 812
  813 + function hideFullscreenButton() {
  814 + return vm.widgetEditMode || vm.iframeMode || $rootScope.forceFullscreen || $state.current.name === 'dashboard';
  815 + }
  816 +
808 817 function onRevertWidgetEdit(widgetForm) {
809 818 if (widgetForm.$dirty) {
810 819 widgetForm.$setPristine();
... ...
... ... @@ -86,6 +86,24 @@ export default function DashboardRoutes($stateProvider) {
86 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 107 .state('home.customers.dashboards.dashboard', {
90 108 url: '/:dashboardId?state',
91 109 reloadOnSearch: false,
... ...
... ... @@ -16,7 +16,7 @@
16 16
17 17 -->
18 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 20 <section class="tb-dashboard-toolbar" ng-show="vm.showDashboardToolbar()"
21 21 ng-class="{ 'tb-dashboard-toolbar-opened': vm.toolbarOpened, 'tb-dashboard-toolbar-closed': !vm.toolbarOpened }">
22 22 <tb-dashboard-toolbar ng-show="!vm.widgetEditMode" force-fullscreen="forceFullscreen"
... ...
... ... @@ -41,8 +41,8 @@
41 41 </md-dialog-content>
42 42 <md-dialog-actions layout="row">
43 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 46 </md-button>
47 47 <md-button ng-disabled="$root.loading" ng-click="vm.cancel()" style="margin-right:20px;">{{ 'action.cancel' | translate }}</md-button>
48 48 </md-dialog-actions>
... ...
... ... @@ -16,7 +16,8 @@
16 16
17 17 -->
18 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 23 </div>
... ...
... ... @@ -43,7 +43,7 @@
43 43 <fieldset ng-disabled="$root.loading">
44 44 <div ng-form name="aliasForm" flex layout="row" layout-align="start center" ng-repeat="entityAlias in vm.entityAliases track by $index">
45 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 47 <md-input-container flex="20" style="min-width: 150px;" md-no-float class="md-block">
48 48 <input required name="alias" placeholder="{{ 'entity.alias' | translate }}" ng-model="entityAlias.alias">
49 49 <div ng-messages="aliasForm.alias.$error">
... ... @@ -81,7 +81,7 @@
81 81 close
82 82 </md-icon>
83 83 </md-button>
84   - </di>
  84 + </div>
85 85 </div>
86 86 </fieldset>
87 87 </div>
... ...
... ... @@ -30,7 +30,7 @@ import AliasController from '../../api/alias-controller';
30 30
31 31 /*@ngInject*/
32 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 34 entityService, attributeService, widgetService) {
35 35
36 36 var linker = function (scope, element, attrs) {
... ... @@ -110,8 +110,15 @@ export default function AttributeTableDirective($compile, $templateCache, $rootS
110 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 117 scope.query.search = '';
  118 +
  119 + $timeout(()=>{
  120 + $toolbarsContainer.find('.searchInput').focus();
  121 + })
115 122 }
116 123
117 124 scope.exitFilterMode = function() {
... ...
... ... @@ -26,7 +26,7 @@
26 26 </md-select>
27 27 </md-input-container>
28 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 30 <md-toolbar class="md-table-toolbar md-default" ng-show="mode==='default'
31 31 && !selectedAttributes.length
32 32 && query.search === null">
... ... @@ -39,7 +39,7 @@
39 39 {{ 'action.add' | translate }}
40 40 </md-tooltip>
41 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 43 <md-icon>search</md-icon>
44 44 <md-tooltip md-direction="top">
45 45 {{ 'action.search' | translate }}
... ... @@ -65,7 +65,7 @@
65 65 </md-button>
66 66 <md-input-container flex>
67 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 69 </md-input-container>
70 70 <md-button class="md-icon-button" aria-label="{{ 'action.back' | translate }}" ng-click="exitFilterMode()">
71 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 22 /* eslint-enable import/no-unresolved, import/default */
23 23
24 24 /*@ngInject*/
25   -export default function EntitySelect($compile, $templateCache) {
  25 +export default function EntitySelect($compile, $templateCache, entityService) {
26 26
27 27 var linker = function (scope, element, attrs, ngModelCtrl) {
28 28 var template = $templateCache.get(entitySelectTemplate);
29 29 element.html(template);
30 30
31 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 48 scope.updateView = function () {
35 49 if (!scope.disabled) {
... ... @@ -54,7 +68,7 @@ export default function EntitySelect($compile, $templateCache) {
54 68 scope.model.entityType = value.entityType;
55 69 scope.model.entityId = value.id;
56 70 } else {
57   - scope.model.entityType = null;
  71 + scope.model.entityType = scope.defaultEntityType;
58 72 scope.model.entityId = null;
59 73 }
60 74 initWatchers();
... ... @@ -106,6 +120,7 @@ export default function EntitySelect($compile, $templateCache) {
106 120 theForm: '=?',
107 121 tbRequired: '=?',
108 122 disabled:'=ngDisabled',
  123 + allowedEntityTypes: "=?",
109 124 useAliasEntityTypes: "=?"
110 125 }
111 126 };
... ...
... ... @@ -17,10 +17,12 @@
17 17 -->
18 18 <div layout='row' class="tb-entity-select">
19 19 <tb-entity-type-select style="min-width: 100px;"
  20 + ng-if="displayEntityTypeSelect"
20 21 the-form="theForm"
21 22 ng-disabled="disabled"
22 23 tb-required="tbRequired"
23 24 use-alias-entity-types="useAliasEntityTypes"
  25 + allowed-entity-types="allowedEntityTypes"
24 26 ng-model="model.entityType">
25 27 </tb-entity-type-select>
26 28 <tb-entity-autocomplete flex ng-if="model.entityType"
... ...
... ... @@ -35,7 +35,30 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m
35 35 : $translate.instant('entity.any-entity');
36 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 62 scope.entityTypesList = [];
40 63 for (var type in entityTypes) {
41 64 var entityTypeInfo = {};
... ... @@ -62,28 +85,43 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m
62 85 }
63 86
64 87 ngModelCtrl.$render = function () {
65   - scope.entityTypeList = [];
  88 + if (scope.entityTypeListWatch) {
  89 + scope.entityTypeListWatch();
  90 + scope.entityTypeListWatch = null;
  91 + }
  92 + var entityTypeList = [];
66 93 var value = ngModelCtrl.$viewValue;
67 94 if (value && value.length) {
68 95 value.forEach(function(type) {
69 96 var entityTypeInfo = {};
70 97 entityTypeInfo.value = type;
71 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 118 if (scope.entityTypeList && scope.entityTypeList.length) {
80   - scope.entityTypeList.forEach(function(entityType) {
  119 + scope.entityTypeList.forEach(function (entityType) {
81 120 values.push(entityType.value);
82 121 });
83 122 }
84   - ngModelCtrl.$setViewValue(values);
85 123 scope.updateValidity();
86   - }, true);
  124 + }
87 125
88 126 $compile(element.contents())(scope);
89 127
... ... @@ -103,7 +141,8 @@ export default function EntityTypeListDirective($compile, $templateCache, $q, $m
103 141 scope: {
104 142 disabled:'=ngDisabled',
105 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 44 scope.removeFilter = removeFilter;
45 45
46 46 ngModelCtrl.$render = function () {
  47 + if (scope.relationFiltersWatch) {
  48 + scope.relationFiltersWatch();
  49 + scope.relationFiltersWatch = null;
  50 + }
47 51 if (ngModelCtrl.$viewValue) {
48 52 var value = ngModelCtrl.$viewValue;
49 53 scope.relationFilters.length = 0;
... ... @@ -51,7 +55,7 @@ export default function RelationFilters($compile, $templateCache) {
51 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 59 if (!angular.equals(newVal, prevVal)) {
56 60 updateValue();
57 61 }
... ... @@ -74,11 +78,16 @@ export default function RelationFilters($compile, $templateCache) {
74 78 }
75 79
76 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 88 scope.relationFilters.forEach(function (filter) {
79 89 value.push(filter);
80 90 });
81   - ngModelCtrl.$setViewValue(value);
82 91 }
83 92 $compile(element.contents())(scope);
84 93 }
... ...
... ... @@ -41,7 +41,7 @@ export default function RelationTable() {
41 41 }
42 42
43 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 46 let vm = this;
47 47
... ... @@ -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 97 vm.query.search = '';
  98 +
  99 + $timeout(()=>{
  100 + $toolbarsContainer.find('.searchInput').focus();
  101 + })
95 102 }
96 103
97 104 function exitFilterMode () {
... ...
... ... @@ -26,7 +26,7 @@
26 26 </md-select>
27 27 </md-input-container>
28 28 </section>
29   - <div layout="column" class="md-whiteframe-z1">
  29 + <div layout="column" class="md-whiteframe-z1 toolbarsContainer">
30 30 <md-toolbar class="md-table-toolbar md-default" ng-show="!vm.selectedRelations.length
31 31 && vm.query.search === null">
32 32 <div class="md-toolbar-tools">
... ... @@ -39,7 +39,7 @@
39 39 {{ 'action.add' | translate }}
40 40 </md-tooltip>
41 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 43 <md-icon>search</md-icon>
44 44 <md-tooltip md-direction="top">
45 45 {{ 'action.search' | translate }}
... ... @@ -64,7 +64,7 @@
64 64 </md-button>
65 65 <md-input-container flex>
66 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 68 </md-input-container>
69 69 <md-button class="md-icon-button" aria-label="{{ 'action.back' | translate }}" ng-click="vm.exitFilterMode()">
70 70 <md-icon aria-label="{{ 'action.close' | translate }}" class="material-icons">close</md-icon>
... ...