Showing
81 changed files
with
2064 additions
and
687 deletions
Too many changes to show.
To preserve performance only 81 of 129 files are displayed.
@@ -116,6 +116,7 @@ public class AppActor extends RuleChainManagerActor { | @@ -116,6 +116,7 @@ public class AppActor extends RuleChainManagerActor { | ||
116 | break; | 116 | break; |
117 | case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG: | 117 | case ACTOR_SYSTEM_TO_DEVICE_SESSION_ACTOR_MSG: |
118 | onToDeviceSessionMsg((BasicActorSystemToDeviceSessionActorMsg) msg); | 118 | onToDeviceSessionMsg((BasicActorSystemToDeviceSessionActorMsg) msg); |
119 | + break; | ||
119 | default: | 120 | default: |
120 | return false; | 121 | return false; |
121 | } | 122 | } |
@@ -76,7 +76,7 @@ public class DeviceController extends BaseController { | @@ -76,7 +76,7 @@ public class DeviceController extends BaseController { | ||
76 | device.setTenantId(getCurrentUser().getTenantId()); | 76 | device.setTenantId(getCurrentUser().getTenantId()); |
77 | if (getCurrentUser().getAuthority() == Authority.CUSTOMER_USER) { | 77 | if (getCurrentUser().getAuthority() == Authority.CUSTOMER_USER) { |
78 | if (device.getId() == null || device.getId().isNullUid() || | 78 | if (device.getId() == null || device.getId().isNullUid() || |
79 | - device.getCustomerId() == null || device.getCustomerId().isNullUid()) { | 79 | + device.getCustomerId() == null || device.getCustomerId().isNullUid()) { |
80 | throw new ThingsboardException("You don't have permission to perform this operation!", | 80 | throw new ThingsboardException("You don't have permission to perform this operation!", |
81 | ThingsboardErrorCode.PERMISSION_DENIED); | 81 | ThingsboardErrorCode.PERMISSION_DENIED); |
82 | } else { | 82 | } else { |
@@ -49,15 +49,15 @@ import org.thingsboard.server.common.data.kv.Aggregation; | @@ -49,15 +49,15 @@ import org.thingsboard.server.common.data.kv.Aggregation; | ||
49 | import org.thingsboard.server.common.data.kv.AttributeKey; | 49 | import org.thingsboard.server.common.data.kv.AttributeKey; |
50 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 50 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
51 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | 51 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
52 | -import org.thingsboard.server.common.data.kv.BaseTsKvQuery; | 52 | +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; |
53 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 53 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
54 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 54 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
55 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 55 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
56 | import org.thingsboard.server.common.data.kv.KvEntry; | 56 | import org.thingsboard.server.common.data.kv.KvEntry; |
57 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 57 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
58 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | ||
58 | import org.thingsboard.server.common.data.kv.StringDataEntry; | 59 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
59 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 60 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
60 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | ||
61 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | 61 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; |
62 | import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; | 62 | import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; |
63 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; | 63 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
@@ -81,7 +81,6 @@ import java.util.LinkedHashMap; | @@ -81,7 +81,6 @@ import java.util.LinkedHashMap; | ||
81 | import java.util.List; | 81 | import java.util.List; |
82 | import java.util.Map; | 82 | import java.util.Map; |
83 | import java.util.Set; | 83 | import java.util.Set; |
84 | -import java.util.UUID; | ||
85 | import java.util.concurrent.ExecutorService; | 84 | import java.util.concurrent.ExecutorService; |
86 | import java.util.concurrent.Executors; | 85 | import java.util.concurrent.Executors; |
87 | import java.util.stream.Collectors; | 86 | import java.util.stream.Collectors; |
@@ -201,7 +200,7 @@ public class TelemetryController extends BaseController { | @@ -201,7 +200,7 @@ public class TelemetryController extends BaseController { | ||
201 | (result, entityId) -> { | 200 | (result, entityId) -> { |
202 | // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted | 201 | // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted |
203 | Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); | 202 | Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); |
204 | - List<TsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseTsKvQuery(key, startTs, endTs, interval, limit, agg)) | 203 | + List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg)) |
205 | .collect(Collectors.toList()); | 204 | .collect(Collectors.toList()); |
206 | 205 | ||
207 | Futures.addCallback(tsService.findAll(entityId, queries), getTsKvListCallback(result)); | 206 | Futures.addCallback(tsService.findAll(entityId, queries), getTsKvListCallback(result)); |
application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsSandboxService.java
@@ -22,6 +22,7 @@ import delight.nashornsandbox.NashornSandbox; | @@ -22,6 +22,7 @@ import delight.nashornsandbox.NashornSandbox; | ||
22 | import delight.nashornsandbox.NashornSandboxes; | 22 | import delight.nashornsandbox.NashornSandboxes; |
23 | import jdk.nashorn.api.scripting.NashornScriptEngineFactory; | 23 | import jdk.nashorn.api.scripting.NashornScriptEngineFactory; |
24 | import lombok.extern.slf4j.Slf4j; | 24 | import lombok.extern.slf4j.Slf4j; |
25 | +import org.apache.commons.lang3.tuple.Pair; | ||
25 | 26 | ||
26 | import javax.annotation.PostConstruct; | 27 | import javax.annotation.PostConstruct; |
27 | import javax.annotation.PreDestroy; | 28 | import javax.annotation.PreDestroy; |
@@ -42,9 +43,10 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic | @@ -42,9 +43,10 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic | ||
42 | private ScriptEngine engine; | 43 | private ScriptEngine engine; |
43 | private ExecutorService monitorExecutorService; | 44 | private ExecutorService monitorExecutorService; |
44 | 45 | ||
45 | - private Map<UUID, String> functionsMap = new ConcurrentHashMap<>(); | ||
46 | - | ||
47 | - private Map<UUID,AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>(); | 46 | + private final Map<UUID, String> functionsMap = new ConcurrentHashMap<>(); |
47 | + private final Map<UUID,AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>(); | ||
48 | + private final Map<String, Pair<UUID, AtomicInteger>> scriptToId = new ConcurrentHashMap<>(); | ||
49 | + private final Map<UUID, AtomicInteger> scriptIdToCount = new ConcurrentHashMap<>(); | ||
48 | 50 | ||
49 | @PostConstruct | 51 | @PostConstruct |
50 | public void init() { | 52 | public void init() { |
@@ -78,19 +80,27 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic | @@ -78,19 +80,27 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic | ||
78 | 80 | ||
79 | @Override | 81 | @Override |
80 | public ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames) { | 82 | public ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames) { |
81 | - UUID scriptId = UUID.randomUUID(); | ||
82 | - String functionName = "invokeInternal_" + scriptId.toString().replace('-','_'); | ||
83 | - String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames); | ||
84 | - try { | ||
85 | - if (useJsSandbox()) { | ||
86 | - sandbox.eval(jsScript); | ||
87 | - } else { | ||
88 | - engine.eval(jsScript); | 83 | + Pair<UUID, AtomicInteger> deduplicated = deduplicate(scriptType, scriptBody); |
84 | + UUID scriptId = deduplicated.getLeft(); | ||
85 | + AtomicInteger duplicateCount = deduplicated.getRight(); | ||
86 | + | ||
87 | + if(duplicateCount.compareAndSet(0, 1)) { | ||
88 | + String functionName = "invokeInternal_" + scriptId.toString().replace('-', '_'); | ||
89 | + String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames); | ||
90 | + try { | ||
91 | + if (useJsSandbox()) { | ||
92 | + sandbox.eval(jsScript); | ||
93 | + } else { | ||
94 | + engine.eval(jsScript); | ||
95 | + } | ||
96 | + functionsMap.put(scriptId, functionName); | ||
97 | + } catch (Exception e) { | ||
98 | + duplicateCount.decrementAndGet(); | ||
99 | + log.warn("Failed to compile JS script: {}", e.getMessage(), e); | ||
100 | + return Futures.immediateFailedFuture(e); | ||
89 | } | 101 | } |
90 | - functionsMap.put(scriptId, functionName); | ||
91 | - } catch (Exception e) { | ||
92 | - log.warn("Failed to compile JS script: {}", e.getMessage(), e); | ||
93 | - return Futures.immediateFailedFuture(e); | 102 | + } else { |
103 | + duplicateCount.incrementAndGet(); | ||
94 | } | 104 | } |
95 | return Futures.immediateFuture(scriptId); | 105 | return Futures.immediateFuture(scriptId); |
96 | } | 106 | } |
@@ -122,6 +132,13 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic | @@ -122,6 +132,13 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic | ||
122 | 132 | ||
123 | @Override | 133 | @Override |
124 | public ListenableFuture<Void> release(UUID scriptId) { | 134 | public ListenableFuture<Void> release(UUID scriptId) { |
135 | + AtomicInteger count = scriptIdToCount.get(scriptId); | ||
136 | + if(count != null) { | ||
137 | + if(count.decrementAndGet() > 0) { | ||
138 | + return Futures.immediateFuture(null); | ||
139 | + } | ||
140 | + } | ||
141 | + | ||
125 | String functionName = functionsMap.get(scriptId); | 142 | String functionName = functionsMap.get(scriptId); |
126 | if (functionName != null) { | 143 | if (functionName != null) { |
127 | try { | 144 | try { |
@@ -156,4 +173,16 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic | @@ -156,4 +173,16 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic | ||
156 | throw new RuntimeException("No script factory implemented for scriptType: " + scriptType); | 173 | throw new RuntimeException("No script factory implemented for scriptType: " + scriptType); |
157 | } | 174 | } |
158 | } | 175 | } |
176 | + | ||
177 | + private Pair<UUID, AtomicInteger> deduplicate(JsScriptType scriptType, String scriptBody) { | ||
178 | + Pair<UUID, AtomicInteger> precomputed = Pair.of(UUID.randomUUID(), new AtomicInteger()); | ||
179 | + | ||
180 | + Pair<UUID, AtomicInteger> pair = scriptToId.computeIfAbsent(deduplicateKey(scriptType, scriptBody), i -> precomputed); | ||
181 | + AtomicInteger duplicateCount = scriptIdToCount.computeIfAbsent(pair.getLeft(), i -> pair.getRight()); | ||
182 | + return Pair.of(pair.getLeft(), duplicateCount); | ||
183 | + } | ||
184 | + | ||
185 | + private String deduplicateKey(JsScriptType scriptType, String scriptBody) { | ||
186 | + return scriptType + "_" + scriptBody; | ||
187 | + } | ||
159 | } | 188 | } |
@@ -45,7 +45,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S | @@ -45,7 +45,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S | ||
45 | try { | 45 | try { |
46 | this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get(); | 46 | this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get(); |
47 | } catch (Exception e) { | 47 | } catch (Exception e) { |
48 | - throw new IllegalArgumentException("Can't compile script: " + e.getMessage()); | 48 | + throw new IllegalArgumentException("Can't compile script: " + e.getMessage(), e); |
49 | } | 49 | } |
50 | } | 50 | } |
51 | 51 |
@@ -24,26 +24,33 @@ import org.springframework.beans.factory.annotation.Autowired; | @@ -24,26 +24,33 @@ import org.springframework.beans.factory.annotation.Autowired; | ||
24 | import org.springframework.context.annotation.Lazy; | 24 | import org.springframework.context.annotation.Lazy; |
25 | import org.springframework.stereotype.Service; | 25 | import org.springframework.stereotype.Service; |
26 | import org.springframework.util.StringUtils; | 26 | import org.springframework.util.StringUtils; |
27 | +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; | ||
27 | import org.thingsboard.rule.engine.api.util.DonAsynchron; | 28 | import org.thingsboard.rule.engine.api.util.DonAsynchron; |
29 | +import org.thingsboard.server.actors.service.ActorService; | ||
28 | import org.thingsboard.server.common.data.DataConstants; | 30 | import org.thingsboard.server.common.data.DataConstants; |
29 | import org.thingsboard.server.common.data.EntityType; | 31 | import org.thingsboard.server.common.data.EntityType; |
30 | import org.thingsboard.server.common.data.EntityView; | 32 | import org.thingsboard.server.common.data.EntityView; |
31 | import org.thingsboard.server.common.data.id.DeviceId; | 33 | import org.thingsboard.server.common.data.id.DeviceId; |
32 | import org.thingsboard.server.common.data.id.EntityId; | 34 | import org.thingsboard.server.common.data.id.EntityId; |
33 | import org.thingsboard.server.common.data.id.EntityIdFactory; | 35 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
36 | +//<<<<<<< HEAD | ||
34 | import org.thingsboard.server.common.data.id.EntityViewId; | 37 | import org.thingsboard.server.common.data.id.EntityViewId; |
38 | +//======= | ||
39 | +import org.thingsboard.server.common.data.id.TenantId; | ||
40 | +//>>>>>>> d909192071880b7af2137333142bc62ece369ec1 | ||
35 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 41 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
36 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | 42 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
37 | -import org.thingsboard.server.common.data.kv.BaseTsKvQuery; | 43 | +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; |
38 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 44 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
39 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 45 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
40 | import org.thingsboard.server.common.data.kv.DataType; | 46 | import org.thingsboard.server.common.data.kv.DataType; |
41 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 47 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
42 | import org.thingsboard.server.common.data.kv.KvEntry; | 48 | import org.thingsboard.server.common.data.kv.KvEntry; |
43 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 49 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
50 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | ||
44 | import org.thingsboard.server.common.data.kv.StringDataEntry; | 51 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
45 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 52 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
46 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | 53 | +import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; |
47 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 54 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
48 | import org.thingsboard.server.dao.attributes.AttributesService; | 55 | import org.thingsboard.server.dao.attributes.AttributesService; |
49 | import org.thingsboard.server.dao.entityview.EntityViewService; | 56 | import org.thingsboard.server.dao.entityview.EntityViewService; |
@@ -108,6 +115,10 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | @@ -108,6 +115,10 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | ||
108 | @Lazy | 115 | @Lazy |
109 | private DeviceStateService stateService; | 116 | private DeviceStateService stateService; |
110 | 117 | ||
118 | + @Autowired | ||
119 | + @Lazy | ||
120 | + private ActorService actorService; | ||
121 | + | ||
111 | private ExecutorService tsCallBackExecutor; | 122 | private ExecutorService tsCallBackExecutor; |
112 | private ExecutorService wsCallBackExecutor; | 123 | private ExecutorService wsCallBackExecutor; |
113 | 124 | ||
@@ -213,6 +224,13 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | @@ -213,6 +224,13 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | ||
213 | } | 224 | } |
214 | 225 | ||
215 | @Override | 226 | @Override |
227 | + public void onSharedAttributesUpdate(TenantId tenantId, DeviceId deviceId, Set<AttributeKvEntry> attributes) { | ||
228 | + DeviceAttributesEventNotificationMsg notificationMsg = DeviceAttributesEventNotificationMsg.onUpdate(tenantId, | ||
229 | + deviceId, DataConstants.SHARED_SCOPE, new ArrayList<>(attributes)); | ||
230 | + actorService.onMsg(new SendToClusterMsg(deviceId, notificationMsg)); | ||
231 | + } | ||
232 | + | ||
233 | + @Override | ||
216 | public void onNewRemoteSubscription(ServerAddress serverAddress, byte[] data) { | 234 | public void onNewRemoteSubscription(ServerAddress serverAddress, byte[] data) { |
217 | ClusterAPIProtos.SubscriptionProto proto; | 235 | ClusterAPIProtos.SubscriptionProto proto; |
218 | try { | 236 | try { |
@@ -364,9 +382,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | @@ -364,9 +382,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio | ||
364 | e -> log.error("Failed to fetch missed updates.", e), tsCallBackExecutor); | 382 | e -> log.error("Failed to fetch missed updates.", e), tsCallBackExecutor); |
365 | } else if (subscription.getType() == TelemetryFeature.TIMESERIES) { | 383 | } else if (subscription.getType() == TelemetryFeature.TIMESERIES) { |
366 | long curTs = System.currentTimeMillis(); | 384 | long curTs = System.currentTimeMillis(); |
367 | - List<TsKvQuery> queries = new ArrayList<>(); | 385 | + List<ReadTsKvQuery> queries = new ArrayList<>(); |
368 | subscription.getKeyStates().entrySet().forEach(e -> { | 386 | subscription.getKeyStates().entrySet().forEach(e -> { |
369 | - queries.add(new BaseTsKvQuery(e.getKey(), e.getValue() + 1L, curTs)); | 387 | + queries.add(new BaseReadTsKvQuery(e.getKey(), e.getValue() + 1L, curTs)); |
370 | }); | 388 | }); |
371 | 389 | ||
372 | DonAsynchron.withCallback(tsService.findAll(entityId, queries), | 390 | DonAsynchron.withCallback(tsService.findAll(entityId, queries), |
@@ -30,10 +30,10 @@ import org.thingsboard.server.common.data.id.EntityId; | @@ -30,10 +30,10 @@ import org.thingsboard.server.common.data.id.EntityId; | ||
30 | import org.thingsboard.server.common.data.id.EntityIdFactory; | 30 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
31 | import org.thingsboard.server.common.data.kv.Aggregation; | 31 | import org.thingsboard.server.common.data.kv.Aggregation; |
32 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 32 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
33 | -import org.thingsboard.server.common.data.kv.BaseTsKvQuery; | 33 | +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; |
34 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 34 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
35 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | ||
35 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 36 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
36 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | ||
37 | import org.thingsboard.server.dao.attributes.AttributesService; | 37 | import org.thingsboard.server.dao.attributes.AttributesService; |
38 | import org.thingsboard.server.dao.timeseries.TimeseriesService; | 38 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
39 | import org.thingsboard.server.service.security.AccessValidator; | 39 | import org.thingsboard.server.service.security.AccessValidator; |
@@ -251,7 +251,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | @@ -251,7 +251,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | ||
251 | } | 251 | } |
252 | EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); | 252 | EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); |
253 | List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); | 253 | List<String> keys = new ArrayList<>(getKeys(cmd).orElse(Collections.emptySet())); |
254 | - List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))) | 254 | + List<ReadTsKvQuery> queries = keys.stream().map(key -> new BaseReadTsKvQuery(key, cmd.getStartTs(), cmd.getEndTs(), cmd.getInterval(), getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))) |
255 | .collect(Collectors.toList()); | 255 | .collect(Collectors.toList()); |
256 | 256 | ||
257 | FutureCallback<List<TsKvEntry>> callback = new FutureCallback<List<TsKvEntry>>() { | 257 | FutureCallback<List<TsKvEntry>> callback = new FutureCallback<List<TsKvEntry>>() { |
@@ -337,7 +337,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | @@ -337,7 +337,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi | ||
337 | log.debug("[{}] fetching timeseries data for last {} ms for keys: ({}) for device : {}", sessionId, cmd.getTimeWindow(), cmd.getKeys(), entityId); | 337 | log.debug("[{}] fetching timeseries data for last {} ms for keys: ({}) for device : {}", sessionId, cmd.getTimeWindow(), cmd.getKeys(), entityId); |
338 | startTs = cmd.getStartTs(); | 338 | startTs = cmd.getStartTs(); |
339 | long endTs = cmd.getStartTs() + cmd.getTimeWindow(); | 339 | long endTs = cmd.getStartTs() + cmd.getTimeWindow(); |
340 | - List<TsKvQuery> queries = keys.stream().map(key -> new BaseTsKvQuery(key, startTs, endTs, cmd.getInterval(), | 340 | + List<ReadTsKvQuery> queries = keys.stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, cmd.getInterval(), |
341 | getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))).collect(Collectors.toList()); | 341 | getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))).collect(Collectors.toList()); |
342 | 342 | ||
343 | final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); | 343 | final FutureCallback<List<TsKvEntry>> callback = getSubscriptionCallback(sessionRef, cmd, sessionId, entityId, startTs, keys); |
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.data.kv; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | + | ||
20 | +@Data | ||
21 | +public class BaseDeleteTsKvQuery extends BaseTsKvQuery implements DeleteTsKvQuery { | ||
22 | + | ||
23 | + private final Boolean rewriteLatestIfDeleted; | ||
24 | + | ||
25 | + public BaseDeleteTsKvQuery(String key, long startTs, long endTs, boolean rewriteLatestIfDeleted) { | ||
26 | + super(key, startTs, endTs); | ||
27 | + this.rewriteLatestIfDeleted = rewriteLatestIfDeleted; | ||
28 | + } | ||
29 | + | ||
30 | + public BaseDeleteTsKvQuery(String key, long startTs, long endTs) { | ||
31 | + this(key, startTs, endTs, false); | ||
32 | + } | ||
33 | + | ||
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.server.common.data.kv; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | + | ||
20 | +@Data | ||
21 | +public class BaseReadTsKvQuery extends BaseTsKvQuery implements ReadTsKvQuery { | ||
22 | + | ||
23 | + private final long interval; | ||
24 | + private final int limit; | ||
25 | + private final Aggregation aggregation; | ||
26 | + private final String orderBy; | ||
27 | + | ||
28 | + public BaseReadTsKvQuery(String key, long startTs, long endTs, long interval, int limit, Aggregation aggregation) { | ||
29 | + this(key, startTs, endTs, interval, limit, aggregation, "DESC"); | ||
30 | + } | ||
31 | + | ||
32 | + public BaseReadTsKvQuery(String key, long startTs, long endTs, long interval, int limit, Aggregation aggregation, | ||
33 | + String orderBy) { | ||
34 | + super(key, startTs, endTs); | ||
35 | + this.interval = interval; | ||
36 | + this.limit = limit; | ||
37 | + this.aggregation = aggregation; | ||
38 | + this.orderBy = orderBy; | ||
39 | + } | ||
40 | + | ||
41 | + public BaseReadTsKvQuery(String key, long startTs, long endTs) { | ||
42 | + this(key, startTs, endTs, endTs - startTs, 1, Aggregation.AVG, "DESC"); | ||
43 | + } | ||
44 | + | ||
45 | +} |
@@ -23,21 +23,11 @@ public class BaseTsKvQuery implements TsKvQuery { | @@ -23,21 +23,11 @@ public class BaseTsKvQuery implements TsKvQuery { | ||
23 | private final String key; | 23 | private final String key; |
24 | private final long startTs; | 24 | private final long startTs; |
25 | private final long endTs; | 25 | private final long endTs; |
26 | - private final long interval; | ||
27 | - private final int limit; | ||
28 | - private final Aggregation aggregation; | ||
29 | 26 | ||
30 | - public BaseTsKvQuery(String key, long startTs, long endTs, long interval, int limit, Aggregation aggregation) { | 27 | + public BaseTsKvQuery(String key, long startTs, long endTs) { |
31 | this.key = key; | 28 | this.key = key; |
32 | this.startTs = startTs; | 29 | this.startTs = startTs; |
33 | this.endTs = endTs; | 30 | this.endTs = endTs; |
34 | - this.interval = interval; | ||
35 | - this.limit = limit; | ||
36 | - this.aggregation = aggregation; | ||
37 | - } | ||
38 | - | ||
39 | - public BaseTsKvQuery(String key, long startTs, long endTs) { | ||
40 | - this(key, startTs, endTs, endTs-startTs, 1, Aggregation.AVG); | ||
41 | } | 31 | } |
42 | 32 | ||
43 | } | 33 | } |
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.data.kv; | ||
17 | + | ||
18 | +public interface DeleteTsKvQuery extends TsKvQuery { | ||
19 | + | ||
20 | + Boolean getRewriteLatestIfDeleted(); | ||
21 | + | ||
22 | +} |
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.data.kv; | ||
17 | + | ||
18 | +public interface ReadTsKvQuery extends TsKvQuery { | ||
19 | + | ||
20 | + long getInterval(); | ||
21 | + | ||
22 | + int getLimit(); | ||
23 | + | ||
24 | + Aggregation getAggregation(); | ||
25 | + | ||
26 | + String getOrderBy(); | ||
27 | + | ||
28 | +} |
@@ -16,9 +16,7 @@ | @@ -16,9 +16,7 @@ | ||
16 | package org.thingsboard.server.dao.relation; | 16 | package org.thingsboard.server.dao.relation; |
17 | 17 | ||
18 | import com.google.common.base.Function; | 18 | import com.google.common.base.Function; |
19 | -import com.google.common.util.concurrent.AsyncFunction; | ||
20 | -import com.google.common.util.concurrent.Futures; | ||
21 | -import com.google.common.util.concurrent.ListenableFuture; | 19 | +import com.google.common.util.concurrent.*; |
22 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
23 | import org.springframework.beans.factory.annotation.Autowired; | 21 | import org.springframework.beans.factory.annotation.Autowired; |
24 | import org.springframework.cache.Cache; | 22 | import org.springframework.cache.Cache; |
@@ -41,7 +39,6 @@ import org.thingsboard.server.dao.exception.DataValidationException; | @@ -41,7 +39,6 @@ import org.thingsboard.server.dao.exception.DataValidationException; | ||
41 | 39 | ||
42 | import javax.annotation.Nullable; | 40 | import javax.annotation.Nullable; |
43 | import java.util.ArrayList; | 41 | import java.util.ArrayList; |
44 | -import java.util.Arrays; | ||
45 | import java.util.Collections; | 42 | import java.util.Collections; |
46 | import java.util.HashSet; | 43 | import java.util.HashSet; |
47 | import java.util.List; | 44 | import java.util.List; |
@@ -94,10 +91,10 @@ public class BaseRelationService implements RelationService { | @@ -94,10 +91,10 @@ public class BaseRelationService implements RelationService { | ||
94 | 91 | ||
95 | @Caching(evict = { | 92 | @Caching(evict = { |
96 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"), | 93 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"), |
97 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"), | ||
98 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"), | ||
99 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"), | ||
100 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}") | 94 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"), |
95 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"), | ||
96 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"), | ||
97 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}") | ||
101 | }) | 98 | }) |
102 | @Override | 99 | @Override |
103 | public boolean saveRelation(EntityRelation relation) { | 100 | public boolean saveRelation(EntityRelation relation) { |
@@ -108,10 +105,10 @@ public class BaseRelationService implements RelationService { | @@ -108,10 +105,10 @@ public class BaseRelationService implements RelationService { | ||
108 | 105 | ||
109 | @Caching(evict = { | 106 | @Caching(evict = { |
110 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"), | 107 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"), |
111 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"), | ||
112 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"), | ||
113 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"), | ||
114 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}") | 108 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"), |
109 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"), | ||
110 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"), | ||
111 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}") | ||
115 | }) | 112 | }) |
116 | @Override | 113 | @Override |
117 | public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) { | 114 | public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) { |
@@ -122,10 +119,10 @@ public class BaseRelationService implements RelationService { | @@ -122,10 +119,10 @@ public class BaseRelationService implements RelationService { | ||
122 | 119 | ||
123 | @Caching(evict = { | 120 | @Caching(evict = { |
124 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"), | 121 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"), |
125 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"), | ||
126 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"), | ||
127 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"), | ||
128 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}") | 122 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"), |
123 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"), | ||
124 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"), | ||
125 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}") | ||
129 | }) | 126 | }) |
130 | @Override | 127 | @Override |
131 | public boolean deleteRelation(EntityRelation relation) { | 128 | public boolean deleteRelation(EntityRelation relation) { |
@@ -136,10 +133,10 @@ public class BaseRelationService implements RelationService { | @@ -136,10 +133,10 @@ public class BaseRelationService implements RelationService { | ||
136 | 133 | ||
137 | @Caching(evict = { | 134 | @Caching(evict = { |
138 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"), | 135 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.to, #relation.type, #relation.typeGroup}"), |
139 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup}"), | ||
140 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup}"), | ||
141 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup}"), | ||
142 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup}") | 136 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.type, #relation.typeGroup, 'FROM'}"), |
137 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.from, #relation.typeGroup, 'FROM'}"), | ||
138 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.typeGroup, 'TO'}"), | ||
139 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#relation.to, #relation.type, #relation.typeGroup, 'TO'}") | ||
143 | }) | 140 | }) |
144 | @Override | 141 | @Override |
145 | public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) { | 142 | public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) { |
@@ -150,10 +147,10 @@ public class BaseRelationService implements RelationService { | @@ -150,10 +147,10 @@ public class BaseRelationService implements RelationService { | ||
150 | 147 | ||
151 | @Caching(evict = { | 148 | @Caching(evict = { |
152 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}"), | 149 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}"), |
153 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}"), | ||
154 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}"), | ||
155 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup}"), | ||
156 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}") | 150 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}"), |
151 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}"), | ||
152 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}"), | ||
153 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}") | ||
157 | }) | 154 | }) |
158 | @Override | 155 | @Override |
159 | public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { | 156 | public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
@@ -164,10 +161,10 @@ public class BaseRelationService implements RelationService { | @@ -164,10 +161,10 @@ public class BaseRelationService implements RelationService { | ||
164 | 161 | ||
165 | @Caching(evict = { | 162 | @Caching(evict = { |
166 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}"), | 163 | @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #to, #relationType, #typeGroup}"), |
167 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}"), | ||
168 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}"), | ||
169 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup}"), | ||
170 | - @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}") | 164 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}"), |
165 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}"), | ||
166 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}"), | ||
167 | + @CacheEvict(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}") | ||
171 | }) | 168 | }) |
172 | @Override | 169 | @Override |
173 | public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { | 170 | public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
@@ -250,30 +247,36 @@ public class BaseRelationService implements RelationService { | @@ -250,30 +247,36 @@ public class BaseRelationService implements RelationService { | ||
250 | fromTypeAndTypeGroup.add(relation.getFrom()); | 247 | fromTypeAndTypeGroup.add(relation.getFrom()); |
251 | fromTypeAndTypeGroup.add(relation.getType()); | 248 | fromTypeAndTypeGroup.add(relation.getType()); |
252 | fromTypeAndTypeGroup.add(relation.getTypeGroup()); | 249 | fromTypeAndTypeGroup.add(relation.getTypeGroup()); |
250 | + fromTypeAndTypeGroup.add(EntitySearchDirection.FROM.name()); | ||
253 | cache.evict(fromTypeAndTypeGroup); | 251 | cache.evict(fromTypeAndTypeGroup); |
254 | 252 | ||
255 | List<Object> fromAndTypeGroup = new ArrayList<>(); | 253 | List<Object> fromAndTypeGroup = new ArrayList<>(); |
256 | fromAndTypeGroup.add(relation.getFrom()); | 254 | fromAndTypeGroup.add(relation.getFrom()); |
257 | fromAndTypeGroup.add(relation.getTypeGroup()); | 255 | fromAndTypeGroup.add(relation.getTypeGroup()); |
256 | + fromAndTypeGroup.add(EntitySearchDirection.FROM.name()); | ||
258 | cache.evict(fromAndTypeGroup); | 257 | cache.evict(fromAndTypeGroup); |
259 | 258 | ||
260 | List<Object> toAndTypeGroup = new ArrayList<>(); | 259 | List<Object> toAndTypeGroup = new ArrayList<>(); |
261 | toAndTypeGroup.add(relation.getTo()); | 260 | toAndTypeGroup.add(relation.getTo()); |
262 | toAndTypeGroup.add(relation.getTypeGroup()); | 261 | toAndTypeGroup.add(relation.getTypeGroup()); |
262 | + toAndTypeGroup.add(EntitySearchDirection.TO.name()); | ||
263 | cache.evict(toAndTypeGroup); | 263 | cache.evict(toAndTypeGroup); |
264 | 264 | ||
265 | List<Object> toTypeAndTypeGroup = new ArrayList<>(); | 265 | List<Object> toTypeAndTypeGroup = new ArrayList<>(); |
266 | - fromTypeAndTypeGroup.add(relation.getTo()); | ||
267 | - fromTypeAndTypeGroup.add(relation.getType()); | ||
268 | - fromTypeAndTypeGroup.add(relation.getTypeGroup()); | 266 | + toTypeAndTypeGroup.add(relation.getTo()); |
267 | + toTypeAndTypeGroup.add(relation.getType()); | ||
268 | + toTypeAndTypeGroup.add(relation.getTypeGroup()); | ||
269 | + toTypeAndTypeGroup.add(EntitySearchDirection.TO.name()); | ||
269 | cache.evict(toTypeAndTypeGroup); | 270 | cache.evict(toTypeAndTypeGroup); |
270 | } | 271 | } |
271 | 272 | ||
272 | - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup}") | 273 | + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #typeGroup, 'FROM'}") |
273 | @Override | 274 | @Override |
274 | public List<EntityRelation> findByFrom(EntityId from, RelationTypeGroup typeGroup) { | 275 | public List<EntityRelation> findByFrom(EntityId from, RelationTypeGroup typeGroup) { |
276 | + validate(from); | ||
277 | + validateTypeGroup(typeGroup); | ||
275 | try { | 278 | try { |
276 | - return findByFromAsync(from, typeGroup).get(); | 279 | + return relationDao.findAllByFrom(from, typeGroup).get(); |
277 | } catch (InterruptedException | ExecutionException e) { | 280 | } catch (InterruptedException | ExecutionException e) { |
278 | throw new RuntimeException(e); | 281 | throw new RuntimeException(e); |
279 | } | 282 | } |
@@ -284,7 +287,29 @@ public class BaseRelationService implements RelationService { | @@ -284,7 +287,29 @@ public class BaseRelationService implements RelationService { | ||
284 | log.trace("Executing findByFrom [{}][{}]", from, typeGroup); | 287 | log.trace("Executing findByFrom [{}][{}]", from, typeGroup); |
285 | validate(from); | 288 | validate(from); |
286 | validateTypeGroup(typeGroup); | 289 | validateTypeGroup(typeGroup); |
287 | - return relationDao.findAllByFrom(from, typeGroup); | 290 | + |
291 | + List<Object> fromAndTypeGroup = new ArrayList<>(); | ||
292 | + fromAndTypeGroup.add(from); | ||
293 | + fromAndTypeGroup.add(typeGroup); | ||
294 | + fromAndTypeGroup.add(EntitySearchDirection.FROM.name()); | ||
295 | + | ||
296 | + Cache cache = cacheManager.getCache(RELATIONS_CACHE); | ||
297 | + List<EntityRelation> fromCache = cache.get(fromAndTypeGroup, List.class); | ||
298 | + if (fromCache != null) { | ||
299 | + return Futures.immediateFuture(fromCache); | ||
300 | + } else { | ||
301 | + ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByFrom(from, typeGroup); | ||
302 | + Futures.addCallback(relationsFuture, | ||
303 | + new FutureCallback<List<EntityRelation>>() { | ||
304 | + @Override | ||
305 | + public void onSuccess(@Nullable List<EntityRelation> result) { | ||
306 | + cache.putIfAbsent(fromAndTypeGroup, result); | ||
307 | + } | ||
308 | + @Override | ||
309 | + public void onFailure(Throwable t) {} | ||
310 | + }); | ||
311 | + return relationsFuture; | ||
312 | + } | ||
288 | } | 313 | } |
289 | 314 | ||
290 | @Override | 315 | @Override |
@@ -305,7 +330,7 @@ public class BaseRelationService implements RelationService { | @@ -305,7 +330,7 @@ public class BaseRelationService implements RelationService { | ||
305 | }); | 330 | }); |
306 | } | 331 | } |
307 | 332 | ||
308 | - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup}") | 333 | + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#from, #relationType, #typeGroup, 'FROM'}") |
309 | @Override | 334 | @Override |
310 | public List<EntityRelation> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { | 335 | public List<EntityRelation> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { |
311 | try { | 336 | try { |
@@ -324,11 +349,13 @@ public class BaseRelationService implements RelationService { | @@ -324,11 +349,13 @@ public class BaseRelationService implements RelationService { | ||
324 | return relationDao.findAllByFromAndType(from, relationType, typeGroup); | 349 | return relationDao.findAllByFromAndType(from, relationType, typeGroup); |
325 | } | 350 | } |
326 | 351 | ||
327 | - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup}") | 352 | + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #typeGroup, 'TO'}") |
328 | @Override | 353 | @Override |
329 | public List<EntityRelation> findByTo(EntityId to, RelationTypeGroup typeGroup) { | 354 | public List<EntityRelation> findByTo(EntityId to, RelationTypeGroup typeGroup) { |
355 | + validate(to); | ||
356 | + validateTypeGroup(typeGroup); | ||
330 | try { | 357 | try { |
331 | - return findByToAsync(to, typeGroup).get(); | 358 | + return relationDao.findAllByTo(to, typeGroup).get(); |
332 | } catch (InterruptedException | ExecutionException e) { | 359 | } catch (InterruptedException | ExecutionException e) { |
333 | throw new RuntimeException(e); | 360 | throw new RuntimeException(e); |
334 | } | 361 | } |
@@ -339,7 +366,29 @@ public class BaseRelationService implements RelationService { | @@ -339,7 +366,29 @@ public class BaseRelationService implements RelationService { | ||
339 | log.trace("Executing findByTo [{}][{}]", to, typeGroup); | 366 | log.trace("Executing findByTo [{}][{}]", to, typeGroup); |
340 | validate(to); | 367 | validate(to); |
341 | validateTypeGroup(typeGroup); | 368 | validateTypeGroup(typeGroup); |
342 | - return relationDao.findAllByTo(to, typeGroup); | 369 | + |
370 | + List<Object> toAndTypeGroup = new ArrayList<>(); | ||
371 | + toAndTypeGroup.add(to); | ||
372 | + toAndTypeGroup.add(typeGroup); | ||
373 | + toAndTypeGroup.add(EntitySearchDirection.TO.name()); | ||
374 | + | ||
375 | + Cache cache = cacheManager.getCache(RELATIONS_CACHE); | ||
376 | + List<EntityRelation> fromCache = cache.get(toAndTypeGroup, List.class); | ||
377 | + if (fromCache != null) { | ||
378 | + return Futures.immediateFuture(fromCache); | ||
379 | + } else { | ||
380 | + ListenableFuture<List<EntityRelation>> relationsFuture = relationDao.findAllByTo(to, typeGroup); | ||
381 | + Futures.addCallback(relationsFuture, | ||
382 | + new FutureCallback<List<EntityRelation>>() { | ||
383 | + @Override | ||
384 | + public void onSuccess(@Nullable List<EntityRelation> result) { | ||
385 | + cache.putIfAbsent(toAndTypeGroup, result); | ||
386 | + } | ||
387 | + @Override | ||
388 | + public void onFailure(Throwable t) {} | ||
389 | + }); | ||
390 | + return relationsFuture; | ||
391 | + } | ||
343 | } | 392 | } |
344 | 393 | ||
345 | @Override | 394 | @Override |
@@ -371,7 +420,7 @@ public class BaseRelationService implements RelationService { | @@ -371,7 +420,7 @@ public class BaseRelationService implements RelationService { | ||
371 | }); | 420 | }); |
372 | } | 421 | } |
373 | 422 | ||
374 | - @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup}") | 423 | + @Cacheable(cacheNames = RELATIONS_CACHE, key = "{#to, #relationType, #typeGroup, 'TO'}") |
375 | @Override | 424 | @Override |
376 | public List<EntityRelation> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { | 425 | public List<EntityRelation> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { |
377 | try { | 426 | try { |
@@ -31,9 +31,10 @@ import org.thingsboard.server.common.data.UUIDConverter; | @@ -31,9 +31,10 @@ import org.thingsboard.server.common.data.UUIDConverter; | ||
31 | import org.thingsboard.server.common.data.id.EntityId; | 31 | import org.thingsboard.server.common.data.id.EntityId; |
32 | import org.thingsboard.server.common.data.kv.Aggregation; | 32 | import org.thingsboard.server.common.data.kv.Aggregation; |
33 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 33 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
34 | +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | ||
35 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | ||
34 | import org.thingsboard.server.common.data.kv.StringDataEntry; | 36 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
35 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 37 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
36 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | ||
37 | import org.thingsboard.server.dao.DaoUtil; | 38 | import org.thingsboard.server.dao.DaoUtil; |
38 | import org.thingsboard.server.dao.model.sql.TsKvEntity; | 39 | import org.thingsboard.server.dao.model.sql.TsKvEntity; |
39 | import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey; | 40 | import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey; |
@@ -102,7 +103,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | @@ -102,7 +103,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | ||
102 | } | 103 | } |
103 | 104 | ||
104 | @Override | 105 | @Override |
105 | - public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<TsKvQuery> queries) { | 106 | + public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries) { |
106 | List<ListenableFuture<List<TsKvEntry>>> futures = queries | 107 | List<ListenableFuture<List<TsKvEntry>>> futures = queries |
107 | .stream() | 108 | .stream() |
108 | .map(query -> findAllAsync(entityId, query)) | 109 | .map(query -> findAllAsync(entityId, query)) |
@@ -121,7 +122,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | @@ -121,7 +122,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | ||
121 | }, service); | 122 | }, service); |
122 | } | 123 | } |
123 | 124 | ||
124 | - private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, TsKvQuery query) { | 125 | + private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) { |
125 | if (query.getAggregation() == Aggregation.NONE) { | 126 | if (query.getAggregation() == Aggregation.NONE) { |
126 | return findAllAsyncWithLimit(entityId, query); | 127 | return findAllAsyncWithLimit(entityId, query); |
127 | } else { | 128 | } else { |
@@ -228,7 +229,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | @@ -228,7 +229,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | ||
228 | }); | 229 | }); |
229 | } | 230 | } |
230 | 231 | ||
231 | - private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, TsKvQuery query) { | 232 | + private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) { |
232 | return Futures.immediateFuture( | 233 | return Futures.immediateFuture( |
233 | DaoUtil.convertDataList( | 234 | DaoUtil.convertDataList( |
234 | tsKvRepository.findAllWithLimit( | 235 | tsKvRepository.findAllWithLimit( |
@@ -306,6 +307,36 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | @@ -306,6 +307,36 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp | ||
306 | }); | 307 | }); |
307 | } | 308 | } |
308 | 309 | ||
310 | + @Override | ||
311 | + public ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query) { | ||
312 | + return service.submit(() -> { | ||
313 | + tsKvRepository.delete( | ||
314 | + fromTimeUUID(entityId.getId()), | ||
315 | + entityId.getEntityType(), | ||
316 | + query.getKey(), | ||
317 | + query.getStartTs(), | ||
318 | + query.getEndTs()); | ||
319 | + return null; | ||
320 | + }); | ||
321 | + } | ||
322 | + | ||
323 | + @Override | ||
324 | + public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { | ||
325 | + TsKvLatestEntity latestEntity = new TsKvLatestEntity(); | ||
326 | + latestEntity.setEntityType(entityId.getEntityType()); | ||
327 | + latestEntity.setEntityId(fromTimeUUID(entityId.getId())); | ||
328 | + latestEntity.setKey(query.getKey()); | ||
329 | + return service.submit(() -> { | ||
330 | + tsKvLatestRepository.delete(latestEntity); | ||
331 | + return null; | ||
332 | + }); | ||
333 | + } | ||
334 | + | ||
335 | + @Override | ||
336 | + public ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query) { | ||
337 | + return service.submit(() -> null); | ||
338 | + } | ||
339 | + | ||
309 | @PreDestroy | 340 | @PreDestroy |
310 | void onDestroy() { | 341 | void onDestroy() { |
311 | if (insertService != null) { | 342 | if (insertService != null) { |
@@ -16,10 +16,12 @@ | @@ -16,10 +16,12 @@ | ||
16 | package org.thingsboard.server.dao.sql.timeseries; | 16 | package org.thingsboard.server.dao.sql.timeseries; |
17 | 17 | ||
18 | import org.springframework.data.domain.Pageable; | 18 | import org.springframework.data.domain.Pageable; |
19 | +import org.springframework.data.jpa.repository.Modifying; | ||
19 | import org.springframework.data.jpa.repository.Query; | 20 | import org.springframework.data.jpa.repository.Query; |
20 | import org.springframework.data.repository.CrudRepository; | 21 | import org.springframework.data.repository.CrudRepository; |
21 | import org.springframework.data.repository.query.Param; | 22 | import org.springframework.data.repository.query.Param; |
22 | import org.springframework.scheduling.annotation.Async; | 23 | import org.springframework.scheduling.annotation.Async; |
24 | +import org.springframework.transaction.annotation.Transactional; | ||
23 | import org.thingsboard.server.common.data.EntityType; | 25 | import org.thingsboard.server.common.data.EntityType; |
24 | import org.thingsboard.server.dao.model.sql.TsKvCompositeKey; | 26 | import org.thingsboard.server.dao.model.sql.TsKvCompositeKey; |
25 | import org.thingsboard.server.dao.model.sql.TsKvEntity; | 27 | import org.thingsboard.server.dao.model.sql.TsKvEntity; |
@@ -41,6 +43,17 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite | @@ -41,6 +43,17 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite | ||
41 | @Param("endTs") long endTs, | 43 | @Param("endTs") long endTs, |
42 | Pageable pageable); | 44 | Pageable pageable); |
43 | 45 | ||
46 | + @Transactional | ||
47 | + @Modifying | ||
48 | + @Query("DELETE FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " + | ||
49 | + "AND tskv.entityType = :entityType AND tskv.key = :entityKey " + | ||
50 | + "AND tskv.ts > :startTs AND tskv.ts < :endTs") | ||
51 | + void delete(@Param("entityId") String entityId, | ||
52 | + @Param("entityType") EntityType entityType, | ||
53 | + @Param("entityKey") String key, | ||
54 | + @Param("startTs") long startTs, | ||
55 | + @Param("endTs") long endTs); | ||
56 | + | ||
44 | @Async | 57 | @Async |
45 | @Query("SELECT new TsKvEntity(MAX(tskv.strValue), MAX(tskv.longValue), MAX(tskv.doubleValue)) FROM TsKvEntity tskv " + | 58 | @Query("SELECT new TsKvEntity(MAX(tskv.strValue), MAX(tskv.longValue), MAX(tskv.doubleValue)) FROM TsKvEntity tskv " + |
46 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + | 59 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + |
@@ -56,30 +69,30 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite | @@ -56,30 +69,30 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite | ||
56 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + | 69 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + |
57 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") | 70 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") |
58 | CompletableFuture<TsKvEntity> findMin(@Param("entityId") String entityId, | 71 | CompletableFuture<TsKvEntity> findMin(@Param("entityId") String entityId, |
59 | - @Param("entityType") EntityType entityType, | ||
60 | - @Param("entityKey") String entityKey, | ||
61 | - @Param("startTs") long startTs, | ||
62 | - @Param("endTs") long endTs); | 72 | + @Param("entityType") EntityType entityType, |
73 | + @Param("entityKey") String entityKey, | ||
74 | + @Param("startTs") long startTs, | ||
75 | + @Param("endTs") long endTs); | ||
63 | 76 | ||
64 | @Async | 77 | @Async |
65 | @Query("SELECT new TsKvEntity(COUNT(tskv.booleanValue), COUNT(tskv.strValue), COUNT(tskv.longValue), COUNT(tskv.doubleValue)) FROM TsKvEntity tskv " + | 78 | @Query("SELECT new TsKvEntity(COUNT(tskv.booleanValue), COUNT(tskv.strValue), COUNT(tskv.longValue), COUNT(tskv.doubleValue)) FROM TsKvEntity tskv " + |
66 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + | 79 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + |
67 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") | 80 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") |
68 | CompletableFuture<TsKvEntity> findCount(@Param("entityId") String entityId, | 81 | CompletableFuture<TsKvEntity> findCount(@Param("entityId") String entityId, |
69 | - @Param("entityType") EntityType entityType, | ||
70 | - @Param("entityKey") String entityKey, | ||
71 | - @Param("startTs") long startTs, | ||
72 | - @Param("endTs") long endTs); | 82 | + @Param("entityType") EntityType entityType, |
83 | + @Param("entityKey") String entityKey, | ||
84 | + @Param("startTs") long startTs, | ||
85 | + @Param("endTs") long endTs); | ||
73 | 86 | ||
74 | @Async | 87 | @Async |
75 | @Query("SELECT new TsKvEntity(AVG(tskv.longValue), AVG(tskv.doubleValue)) FROM TsKvEntity tskv " + | 88 | @Query("SELECT new TsKvEntity(AVG(tskv.longValue), AVG(tskv.doubleValue)) FROM TsKvEntity tskv " + |
76 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + | 89 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + |
77 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") | 90 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") |
78 | CompletableFuture<TsKvEntity> findAvg(@Param("entityId") String entityId, | 91 | CompletableFuture<TsKvEntity> findAvg(@Param("entityId") String entityId, |
79 | - @Param("entityType") EntityType entityType, | ||
80 | - @Param("entityKey") String entityKey, | ||
81 | - @Param("startTs") long startTs, | ||
82 | - @Param("endTs") long endTs); | 92 | + @Param("entityType") EntityType entityType, |
93 | + @Param("entityKey") String entityKey, | ||
94 | + @Param("startTs") long startTs, | ||
95 | + @Param("endTs") long endTs); | ||
83 | 96 | ||
84 | 97 | ||
85 | @Async | 98 | @Async |
@@ -87,8 +100,8 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite | @@ -87,8 +100,8 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite | ||
87 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + | 100 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + |
88 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") | 101 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") |
89 | CompletableFuture<TsKvEntity> findSum(@Param("entityId") String entityId, | 102 | CompletableFuture<TsKvEntity> findSum(@Param("entityId") String entityId, |
90 | - @Param("entityType") EntityType entityType, | ||
91 | - @Param("entityKey") String entityKey, | ||
92 | - @Param("startTs") long startTs, | ||
93 | - @Param("endTs") long endTs); | 103 | + @Param("entityType") EntityType entityType, |
104 | + @Param("entityKey") String entityKey, | ||
105 | + @Param("startTs") long startTs, | ||
106 | + @Param("endTs") long endTs); | ||
94 | } | 107 | } |
@@ -25,9 +25,10 @@ import org.thingsboard.server.common.data.EntityType; | @@ -25,9 +25,10 @@ import org.thingsboard.server.common.data.EntityType; | ||
25 | import org.thingsboard.server.common.data.EntityView; | 25 | import org.thingsboard.server.common.data.EntityView; |
26 | import org.thingsboard.server.common.data.id.EntityId; | 26 | import org.thingsboard.server.common.data.id.EntityId; |
27 | import org.thingsboard.server.common.data.id.EntityViewId; | 27 | import org.thingsboard.server.common.data.id.EntityViewId; |
28 | -import org.thingsboard.server.common.data.kv.BaseTsKvQuery; | 28 | +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; |
29 | +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | ||
30 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | ||
29 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 31 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
30 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | ||
31 | import org.thingsboard.server.dao.entityview.EntityViewService; | 32 | import org.thingsboard.server.dao.entityview.EntityViewService; |
32 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 33 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
33 | import org.thingsboard.server.dao.service.Validator; | 34 | import org.thingsboard.server.dao.service.Validator; |
@@ -46,6 +47,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank; | @@ -46,6 +47,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank; | ||
46 | public class BaseTimeseriesService implements TimeseriesService { | 47 | public class BaseTimeseriesService implements TimeseriesService { |
47 | 48 | ||
48 | public static final int INSERTS_PER_ENTRY = 3; | 49 | public static final int INSERTS_PER_ENTRY = 3; |
50 | + public static final int DELETES_PER_ENTRY = INSERTS_PER_ENTRY; | ||
49 | 51 | ||
50 | @Autowired | 52 | @Autowired |
51 | private TimeseriesDao timeseriesDao; | 53 | private TimeseriesDao timeseriesDao; |
@@ -54,9 +56,9 @@ public class BaseTimeseriesService implements TimeseriesService { | @@ -54,9 +56,9 @@ public class BaseTimeseriesService implements TimeseriesService { | ||
54 | private EntityViewService entityViewService; | 56 | private EntityViewService entityViewService; |
55 | 57 | ||
56 | @Override | 58 | @Override |
57 | - public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<TsKvQuery> queries) { | 59 | + public ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<ReadTsKvQuery> queries) { |
58 | validate(entityId); | 60 | validate(entityId); |
59 | - queries.forEach(query -> validate(query)); | 61 | + queries.forEach(BaseTimeseriesService::validate); |
60 | if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { | 62 | if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { |
61 | EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId); | 63 | EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId); |
62 | return timeseriesDao.findAllAsync(entityView.getEntityId(), updateQueriesForEntityView(entityView, queries)); | 64 | return timeseriesDao.findAllAsync(entityView.getEntityId(), updateQueriesForEntityView(entityView, queries)); |
@@ -69,13 +71,14 @@ public class BaseTimeseriesService implements TimeseriesService { | @@ -69,13 +71,14 @@ public class BaseTimeseriesService implements TimeseriesService { | ||
69 | validate(entityId); | 71 | validate(entityId); |
70 | List<ListenableFuture<TsKvEntry>> futures = Lists.newArrayListWithExpectedSize(keys.size()); | 72 | List<ListenableFuture<TsKvEntry>> futures = Lists.newArrayListWithExpectedSize(keys.size()); |
71 | keys.forEach(key -> Validator.validateString(key, "Incorrect key " + key)); | 73 | keys.forEach(key -> Validator.validateString(key, "Incorrect key " + key)); |
72 | - if (false/*entityId.getEntityType().equals(EntityType.ENTITY_VIEW)*/) { | 74 | + if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { |
73 | EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId); | 75 | EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId); |
74 | - Collection<String> newKeys = chooseKeysForEntityView(entityView, keys); | ||
75 | - newKeys.forEach(newKey -> futures.add(timeseriesDao.findLatest(entityView.getEntityId(), newKey))); | ||
76 | - } else { | ||
77 | - keys.forEach(key -> futures.add(timeseriesDao.findLatest(entityId, key))); | 76 | + Collection<String> matchingKeys = chooseKeysForEntityView(entityView, keys); |
77 | + List<ReadTsKvQuery> queries = new ArrayList<>(); | ||
78 | + matchingKeys.forEach(key -> queries.add(new BaseReadTsKvQuery(key, entityView.getStartTs(), entityView.getEndTs()))); | ||
79 | + return timeseriesDao.findAllAsync(entityView.getEntityId(), updateQueriesForEntityView(entityView, queries)); | ||
78 | } | 80 | } |
81 | + keys.forEach(key -> futures.add(timeseriesDao.findLatest(entityId, key))); | ||
79 | return Futures.allAsList(futures); | 82 | return Futures.allAsList(futures); |
80 | } | 83 | } |
81 | 84 | ||
@@ -129,8 +132,8 @@ public class BaseTimeseriesService implements TimeseriesService { | @@ -129,8 +132,8 @@ public class BaseTimeseriesService implements TimeseriesService { | ||
129 | futures.add(timeseriesDao.save(entityId, tsKvEntry, ttl)); | 132 | futures.add(timeseriesDao.save(entityId, tsKvEntry, ttl)); |
130 | } | 133 | } |
131 | 134 | ||
132 | - private List<TsKvQuery> updateQueriesForEntityView(EntityView entityView, List<TsKvQuery> queries) { | ||
133 | - List<TsKvQuery> newQueries = new ArrayList<>(); | 135 | + private List<ReadTsKvQuery> updateQueriesForEntityView(EntityView entityView, List<ReadTsKvQuery> queries) { |
136 | + List<ReadTsKvQuery> newQueries = new ArrayList<>(); | ||
134 | entityView.getKeys().getTimeseries() | 137 | entityView.getKeys().getTimeseries() |
135 | .forEach(viewKey -> queries | 138 | .forEach(viewKey -> queries |
136 | .forEach(query -> { | 139 | .forEach(query -> { |
@@ -148,7 +151,6 @@ public class BaseTimeseriesService implements TimeseriesService { | @@ -148,7 +151,6 @@ public class BaseTimeseriesService implements TimeseriesService { | ||
148 | return newQueries; | 151 | return newQueries; |
149 | } | 152 | } |
150 | 153 | ||
151 | - @Deprecated /*Will be a modified*/ | ||
152 | private Collection<String> chooseKeysForEntityView(EntityView entityView, Collection<String> keys) { | 154 | private Collection<String> chooseKeysForEntityView(EntityView entityView, Collection<String> keys) { |
153 | Collection<String> newKeys = new ArrayList<>(); | 155 | Collection<String> newKeys = new ArrayList<>(); |
154 | entityView.getKeys().getTimeseries() | 156 | entityView.getKeys().getTimeseries() |
@@ -156,27 +158,53 @@ public class BaseTimeseriesService implements TimeseriesService { | @@ -156,27 +158,53 @@ public class BaseTimeseriesService implements TimeseriesService { | ||
156 | .forEach(key -> { | 158 | .forEach(key -> { |
157 | if (key.equals(viewKey)) { | 159 | if (key.equals(viewKey)) { |
158 | newKeys.add(key); | 160 | newKeys.add(key); |
159 | - }})); | 161 | + } |
162 | + })); | ||
160 | return newKeys; | 163 | return newKeys; |
161 | } | 164 | } |
162 | 165 | ||
166 | + @Override | ||
167 | + public ListenableFuture<List<Void>> remove(EntityId entityId, List<DeleteTsKvQuery> deleteTsKvQueries) { | ||
168 | + validate(entityId); | ||
169 | + deleteTsKvQueries.forEach(BaseTimeseriesService::validate); | ||
170 | + List<ListenableFuture<Void>> futures = Lists.newArrayListWithExpectedSize(deleteTsKvQueries.size() * DELETES_PER_ENTRY); | ||
171 | + for (DeleteTsKvQuery tsKvQuery : deleteTsKvQueries) { | ||
172 | + deleteAndRegisterFutures(futures, entityId, tsKvQuery); | ||
173 | + } | ||
174 | + return Futures.allAsList(futures); | ||
175 | + } | ||
176 | + | ||
177 | + private void deleteAndRegisterFutures(List<ListenableFuture<Void>> futures, EntityId entityId, DeleteTsKvQuery query) { | ||
178 | + futures.add(timeseriesDao.remove(entityId, query)); | ||
179 | + futures.add(timeseriesDao.removeLatest(entityId, query)); | ||
180 | + futures.add(timeseriesDao.removePartition(entityId, query)); | ||
181 | + } | ||
182 | + | ||
163 | private static void validate(EntityId entityId) { | 183 | private static void validate(EntityId entityId) { |
164 | Validator.validateEntityId(entityId, "Incorrect entityId " + entityId); | 184 | Validator.validateEntityId(entityId, "Incorrect entityId " + entityId); |
165 | } | 185 | } |
166 | 186 | ||
167 | - private static void validate(TsKvQuery query) { | 187 | + private static void validate(ReadTsKvQuery query) { |
168 | if (query == null) { | 188 | if (query == null) { |
169 | - throw new IncorrectParameterException("TsKvQuery can't be null"); | 189 | + throw new IncorrectParameterException("ReadTsKvQuery can't be null"); |
170 | } else if (isBlank(query.getKey())) { | 190 | } else if (isBlank(query.getKey())) { |
171 | - throw new IncorrectParameterException("Incorrect TsKvQuery. Key can't be empty"); | 191 | + throw new IncorrectParameterException("Incorrect ReadTsKvQuery. Key can't be empty"); |
172 | } else if (query.getAggregation() == null) { | 192 | } else if (query.getAggregation() == null) { |
173 | - throw new IncorrectParameterException("Incorrect TsKvQuery. Aggregation can't be empty"); | 193 | + throw new IncorrectParameterException("Incorrect ReadTsKvQuery. Aggregation can't be empty"); |
194 | + } | ||
195 | + } | ||
196 | + | ||
197 | + private static void validate(DeleteTsKvQuery query) { | ||
198 | + if (query == null) { | ||
199 | + throw new IncorrectParameterException("DeleteTsKvQuery can't be null"); | ||
200 | + } else if (isBlank(query.getKey())) { | ||
201 | + throw new IncorrectParameterException("Incorrect DeleteTsKvQuery. Key can't be empty"); | ||
174 | } | 202 | } |
175 | } | 203 | } |
176 | 204 | ||
177 | - private static TsKvQuery updateQuery(Long startTs, Long endTs, String viewKey, TsKvQuery query) { | 205 | + private static ReadTsKvQuery updateQuery(Long startTs, Long endTs, String viewKey, ReadTsKvQuery query) { |
178 | return startTs <= query.getStartTs() && endTs >= query.getEndTs() ? query : | 206 | return startTs <= query.getStartTs() && endTs >= query.getEndTs() ? query : |
179 | - new BaseTsKvQuery(viewKey, startTs, endTs, query.getInterval(), query.getLimit(), query.getAggregation()); | 207 | + new BaseReadTsKvQuery(viewKey, startTs, endTs, query.getInterval(), query.getLimit(), query.getAggregation()); |
180 | } | 208 | } |
181 | 209 | ||
182 | private static void checkForNonEntityView(EntityId entityId) throws Exception { | 210 | private static void checkForNonEntityView(EntityId entityId) throws Exception { |
@@ -20,6 +20,7 @@ import com.datastax.driver.core.PreparedStatement; | @@ -20,6 +20,7 @@ import com.datastax.driver.core.PreparedStatement; | ||
20 | import com.datastax.driver.core.ResultSet; | 20 | import com.datastax.driver.core.ResultSet; |
21 | import com.datastax.driver.core.ResultSetFuture; | 21 | import com.datastax.driver.core.ResultSetFuture; |
22 | import com.datastax.driver.core.Row; | 22 | import com.datastax.driver.core.Row; |
23 | +import com.datastax.driver.core.Statement; | ||
23 | import com.datastax.driver.core.querybuilder.QueryBuilder; | 24 | import com.datastax.driver.core.querybuilder.QueryBuilder; |
24 | import com.datastax.driver.core.querybuilder.Select; | 25 | import com.datastax.driver.core.querybuilder.Select; |
25 | import com.google.common.base.Function; | 26 | import com.google.common.base.Function; |
@@ -34,16 +35,17 @@ import org.springframework.core.env.Environment; | @@ -34,16 +35,17 @@ import org.springframework.core.env.Environment; | ||
34 | import org.springframework.stereotype.Component; | 35 | import org.springframework.stereotype.Component; |
35 | import org.thingsboard.server.common.data.id.EntityId; | 36 | import org.thingsboard.server.common.data.id.EntityId; |
36 | import org.thingsboard.server.common.data.kv.Aggregation; | 37 | import org.thingsboard.server.common.data.kv.Aggregation; |
37 | -import org.thingsboard.server.common.data.kv.BaseTsKvQuery; | 38 | +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; |
38 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 39 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
39 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 40 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
40 | import org.thingsboard.server.common.data.kv.DataType; | 41 | import org.thingsboard.server.common.data.kv.DataType; |
42 | +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | ||
41 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 43 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
42 | import org.thingsboard.server.common.data.kv.KvEntry; | 44 | import org.thingsboard.server.common.data.kv.KvEntry; |
43 | import org.thingsboard.server.common.data.kv.LongDataEntry; | 45 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
46 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | ||
44 | import org.thingsboard.server.common.data.kv.StringDataEntry; | 47 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
45 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 48 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
46 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | ||
47 | import org.thingsboard.server.dao.model.ModelConstants; | 49 | import org.thingsboard.server.dao.model.ModelConstants; |
48 | import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao; | 50 | import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao; |
49 | import org.thingsboard.server.dao.util.NoSqlDao; | 51 | import org.thingsboard.server.dao.util.NoSqlDao; |
@@ -54,11 +56,11 @@ import javax.annotation.PreDestroy; | @@ -54,11 +56,11 @@ import javax.annotation.PreDestroy; | ||
54 | import java.time.Instant; | 56 | import java.time.Instant; |
55 | import java.time.LocalDateTime; | 57 | import java.time.LocalDateTime; |
56 | import java.time.ZoneOffset; | 58 | import java.time.ZoneOffset; |
59 | +import java.util.ArrayList; | ||
57 | import java.util.Arrays; | 60 | import java.util.Arrays; |
61 | +import java.util.Collections; | ||
58 | import java.util.List; | 62 | import java.util.List; |
59 | -import java.util.ArrayList; | ||
60 | import java.util.Optional; | 63 | import java.util.Optional; |
61 | -import java.util.Collections; | ||
62 | import java.util.stream.Collectors; | 64 | import java.util.stream.Collectors; |
63 | 65 | ||
64 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; | 66 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; |
@@ -76,8 +78,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -76,8 +78,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
76 | public static final String GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID = "Generated query [{}] for entityType {} and entityId {}"; | 78 | public static final String GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID = "Generated query [{}] for entityType {} and entityId {}"; |
77 | public static final String SELECT_PREFIX = "SELECT "; | 79 | public static final String SELECT_PREFIX = "SELECT "; |
78 | public static final String EQUALS_PARAM = " = ? "; | 80 | public static final String EQUALS_PARAM = " = ? "; |
79 | - | ||
80 | - | 81 | + public static final String ASC_ORDER = "ASC"; |
82 | + public static final String DESC_ORDER = "DESC"; | ||
81 | private static List<Long> FIXED_PARTITION = Arrays.asList(new Long[]{0L}); | 83 | private static List<Long> FIXED_PARTITION = Arrays.asList(new Long[]{0L}); |
82 | 84 | ||
83 | @Autowired | 85 | @Autowired |
@@ -96,9 +98,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -96,9 +98,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
96 | private PreparedStatement latestInsertStmt; | 98 | private PreparedStatement latestInsertStmt; |
97 | private PreparedStatement[] saveStmts; | 99 | private PreparedStatement[] saveStmts; |
98 | private PreparedStatement[] saveTtlStmts; | 100 | private PreparedStatement[] saveTtlStmts; |
99 | - private PreparedStatement[] fetchStmts; | 101 | + private PreparedStatement[] fetchStmtsAsc; |
102 | + private PreparedStatement[] fetchStmtsDesc; | ||
100 | private PreparedStatement findLatestStmt; | 103 | private PreparedStatement findLatestStmt; |
101 | private PreparedStatement findAllLatestStmt; | 104 | private PreparedStatement findAllLatestStmt; |
105 | + private PreparedStatement deleteStmt; | ||
106 | + private PreparedStatement deletePartitionStmt; | ||
102 | 107 | ||
103 | private boolean isInstall() { | 108 | private boolean isInstall() { |
104 | return environment.acceptsProfiles("install"); | 109 | return environment.acceptsProfiles("install"); |
@@ -108,7 +113,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -108,7 +113,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
108 | public void init() { | 113 | public void init() { |
109 | super.startExecutor(); | 114 | super.startExecutor(); |
110 | if (!isInstall()) { | 115 | if (!isInstall()) { |
111 | - getFetchStmt(Aggregation.NONE); | 116 | + getFetchStmt(Aggregation.NONE, DESC_ORDER); |
112 | Optional<TsPartitionDate> partition = TsPartitionDate.parse(partitioning); | 117 | Optional<TsPartitionDate> partition = TsPartitionDate.parse(partitioning); |
113 | if (partition.isPresent()) { | 118 | if (partition.isPresent()) { |
114 | tsFormat = partition.get(); | 119 | tsFormat = partition.get(); |
@@ -125,7 +130,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -125,7 +130,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
125 | } | 130 | } |
126 | 131 | ||
127 | @Override | 132 | @Override |
128 | - public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<TsKvQuery> queries) { | 133 | + public ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries) { |
129 | List<ListenableFuture<List<TsKvEntry>>> futures = queries.stream().map(query -> findAllAsync(entityId, query)).collect(Collectors.toList()); | 134 | List<ListenableFuture<List<TsKvEntry>>> futures = queries.stream().map(query -> findAllAsync(entityId, query)).collect(Collectors.toList()); |
130 | return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() { | 135 | return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() { |
131 | @Nullable | 136 | @Nullable |
@@ -142,7 +147,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -142,7 +147,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
142 | } | 147 | } |
143 | 148 | ||
144 | 149 | ||
145 | - private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, TsKvQuery query) { | 150 | + private ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) { |
146 | if (query.getAggregation() == Aggregation.NONE) { | 151 | if (query.getAggregation() == Aggregation.NONE) { |
147 | return findAllAsyncWithLimit(entityId, query); | 152 | return findAllAsyncWithLimit(entityId, query); |
148 | } else { | 153 | } else { |
@@ -152,7 +157,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -152,7 +157,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
152 | while (stepTs < query.getEndTs()) { | 157 | while (stepTs < query.getEndTs()) { |
153 | long startTs = stepTs; | 158 | long startTs = stepTs; |
154 | long endTs = stepTs + step; | 159 | long endTs = stepTs + step; |
155 | - TsKvQuery subQuery = new BaseTsKvQuery(query.getKey(), startTs, endTs, step, 1, query.getAggregation()); | 160 | + ReadTsKvQuery subQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, step, 1, query.getAggregation(), query.getOrderBy()); |
156 | futures.add(findAndAggregateAsync(entityId, subQuery, toPartitionTs(startTs), toPartitionTs(endTs))); | 161 | futures.add(findAndAggregateAsync(entityId, subQuery, toPartitionTs(startTs), toPartitionTs(endTs))); |
157 | stepTs = endTs; | 162 | stepTs = endTs; |
158 | } | 163 | } |
@@ -171,7 +176,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -171,7 +176,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
171 | return tsFormat.getTruncateUnit().equals(TsPartitionDate.EPOCH_START); | 176 | return tsFormat.getTruncateUnit().equals(TsPartitionDate.EPOCH_START); |
172 | } | 177 | } |
173 | 178 | ||
174 | - private ListenableFuture<List<Long>> getPartitionsFuture(TsKvQuery query, EntityId entityId, long minPartition, long maxPartition) { | 179 | + private ListenableFuture<List<Long>> getPartitionsFuture(ReadTsKvQuery query, EntityId entityId, long minPartition, long maxPartition) { |
175 | if (isFixedPartitioning()) { //no need to fetch partitions from DB | 180 | if (isFixedPartitioning()) { //no need to fetch partitions from DB |
176 | return Futures.immediateFuture(FIXED_PARTITION); | 181 | return Futures.immediateFuture(FIXED_PARTITION); |
177 | } | 182 | } |
@@ -179,11 +184,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -179,11 +184,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
179 | return Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor); | 184 | return Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor); |
180 | } | 185 | } |
181 | 186 | ||
182 | - private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, TsKvQuery query) { | ||
183 | - | 187 | + private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) { |
184 | long minPartition = toPartitionTs(query.getStartTs()); | 188 | long minPartition = toPartitionTs(query.getStartTs()); |
185 | long maxPartition = toPartitionTs(query.getEndTs()); | 189 | long maxPartition = toPartitionTs(query.getEndTs()); |
186 | - | ||
187 | final ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition); | 190 | final ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition); |
188 | final SimpleListenableFuture<List<TsKvEntry>> resultFuture = new SimpleListenableFuture<>(); | 191 | final SimpleListenableFuture<List<TsKvEntry>> resultFuture = new SimpleListenableFuture<>(); |
189 | 192 | ||
@@ -212,7 +215,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -212,7 +215,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
212 | if (cursor.isFull() || !cursor.hasNextPartition()) { | 215 | if (cursor.isFull() || !cursor.hasNextPartition()) { |
213 | resultFuture.set(cursor.getData()); | 216 | resultFuture.set(cursor.getData()); |
214 | } else { | 217 | } else { |
215 | - PreparedStatement proto = getFetchStmt(Aggregation.NONE); | 218 | + PreparedStatement proto = getFetchStmt(Aggregation.NONE, cursor.getOrderBy()); |
216 | BoundStatement stmt = proto.bind(); | 219 | BoundStatement stmt = proto.bind(); |
217 | stmt.setString(0, cursor.getEntityType()); | 220 | stmt.setString(0, cursor.getEntityType()); |
218 | stmt.setUUID(1, cursor.getEntityId()); | 221 | stmt.setUUID(1, cursor.getEntityId()); |
@@ -237,14 +240,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -237,14 +240,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
237 | } | 240 | } |
238 | } | 241 | } |
239 | 242 | ||
240 | - private ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, TsKvQuery query, long minPartition, long maxPartition) { | 243 | + private ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, ReadTsKvQuery query, long minPartition, long maxPartition) { |
241 | final Aggregation aggregation = query.getAggregation(); | 244 | final Aggregation aggregation = query.getAggregation(); |
242 | final String key = query.getKey(); | 245 | final String key = query.getKey(); |
243 | final long startTs = query.getStartTs(); | 246 | final long startTs = query.getStartTs(); |
244 | final long endTs = query.getEndTs(); | 247 | final long endTs = query.getEndTs(); |
245 | final long ts = startTs + (endTs - startTs) / 2; | 248 | final long ts = startTs + (endTs - startTs) / 2; |
246 | - | ||
247 | - | ||
248 | ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition); | 249 | ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition); |
249 | ListenableFuture<List<ResultSet>> aggregationChunks = Futures.transformAsync(partitionsListFuture, | 250 | ListenableFuture<List<ResultSet>> aggregationChunks = Futures.transformAsync(partitionsListFuture, |
250 | getFetchChunksAsyncFunction(entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor); | 251 | getFetchChunksAsyncFunction(entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor); |
@@ -260,7 +261,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -260,7 +261,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
260 | private AsyncFunction<List<Long>, List<ResultSet>> getFetchChunksAsyncFunction(EntityId entityId, String key, Aggregation aggregation, long startTs, long endTs) { | 261 | private AsyncFunction<List<Long>, List<ResultSet>> getFetchChunksAsyncFunction(EntityId entityId, String key, Aggregation aggregation, long startTs, long endTs) { |
261 | return partitions -> { | 262 | return partitions -> { |
262 | try { | 263 | try { |
263 | - PreparedStatement proto = getFetchStmt(aggregation); | 264 | + PreparedStatement proto = getFetchStmt(aggregation, DESC_ORDER); |
264 | List<ResultSetFuture> futures = new ArrayList<>(partitions.size()); | 265 | List<ResultSetFuture> futures = new ArrayList<>(partitions.size()); |
265 | for (Long partition : partitions) { | 266 | for (Long partition : partitions) { |
266 | log.trace("Fetching data for partition [{}] for entityType {} and entityId {}", partition, entityId.getEntityType(), entityId.getId()); | 267 | log.trace("Fetching data for partition [{}] for entityType {} and entityId {}", partition, entityId.getEntityType(), entityId.getId()); |
@@ -363,6 +364,204 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -363,6 +364,204 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
363 | return getFuture(executeAsyncWrite(stmt), rs -> null); | 364 | return getFuture(executeAsyncWrite(stmt), rs -> null); |
364 | } | 365 | } |
365 | 366 | ||
367 | + @Override | ||
368 | + public ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query) { | ||
369 | + long minPartition = toPartitionTs(query.getStartTs()); | ||
370 | + long maxPartition = toPartitionTs(query.getEndTs()); | ||
371 | + | ||
372 | + ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition); | ||
373 | + | ||
374 | + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>(); | ||
375 | + final ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor); | ||
376 | + | ||
377 | + Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() { | ||
378 | + @Override | ||
379 | + public void onSuccess(@Nullable List<Long> partitions) { | ||
380 | + QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitions); | ||
381 | + deleteAsync(cursor, resultFuture); | ||
382 | + } | ||
383 | + | ||
384 | + @Override | ||
385 | + public void onFailure(Throwable t) { | ||
386 | + log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t); | ||
387 | + } | ||
388 | + }, readResultsProcessingExecutor); | ||
389 | + return resultFuture; | ||
390 | + } | ||
391 | + | ||
392 | + private void deleteAsync(final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) { | ||
393 | + if (!cursor.hasNextPartition()) { | ||
394 | + resultFuture.set(null); | ||
395 | + } else { | ||
396 | + PreparedStatement proto = getDeleteStmt(); | ||
397 | + BoundStatement stmt = proto.bind(); | ||
398 | + stmt.setString(0, cursor.getEntityType()); | ||
399 | + stmt.setUUID(1, cursor.getEntityId()); | ||
400 | + stmt.setString(2, cursor.getKey()); | ||
401 | + stmt.setLong(3, cursor.getNextPartition()); | ||
402 | + stmt.setLong(4, cursor.getStartTs()); | ||
403 | + stmt.setLong(5, cursor.getEndTs()); | ||
404 | + | ||
405 | + Futures.addCallback(executeAsyncWrite(stmt), new FutureCallback<ResultSet>() { | ||
406 | + @Override | ||
407 | + public void onSuccess(@Nullable ResultSet result) { | ||
408 | + deleteAsync(cursor, resultFuture); | ||
409 | + } | ||
410 | + | ||
411 | + @Override | ||
412 | + public void onFailure(Throwable t) { | ||
413 | + log.error("[{}][{}] Failed to delete data for query {}-{}", stmt, t); | ||
414 | + } | ||
415 | + }, readResultsProcessingExecutor); | ||
416 | + } | ||
417 | + } | ||
418 | + | ||
419 | + private PreparedStatement getDeleteStmt() { | ||
420 | + if (deleteStmt == null) { | ||
421 | + deleteStmt = prepare("DELETE FROM " + ModelConstants.TS_KV_CF + | ||
422 | + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM | ||
423 | + + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM | ||
424 | + + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM | ||
425 | + + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM | ||
426 | + + "AND " + ModelConstants.TS_COLUMN + " > ? " | ||
427 | + + "AND " + ModelConstants.TS_COLUMN + " <= ?"); | ||
428 | + } | ||
429 | + return deleteStmt; | ||
430 | + } | ||
431 | + | ||
432 | + @Override | ||
433 | + public ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query) { | ||
434 | + ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(entityId, query.getKey()); | ||
435 | + | ||
436 | + ListenableFuture<Boolean> booleanFuture = Futures.transformAsync(latestEntryFuture, latestEntry -> { | ||
437 | + long ts = latestEntry.getTs(); | ||
438 | + if (ts >= query.getStartTs() && ts <= query.getEndTs()) { | ||
439 | + return Futures.immediateFuture(true); | ||
440 | + } else { | ||
441 | + log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey()); | ||
442 | + } | ||
443 | + return Futures.immediateFuture(false); | ||
444 | + }, readResultsProcessingExecutor); | ||
445 | + | ||
446 | + ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | ||
447 | + if (isRemove) { | ||
448 | + return deleteLatest(entityId, query.getKey()); | ||
449 | + } | ||
450 | + return Futures.immediateFuture(null); | ||
451 | + }, readResultsProcessingExecutor); | ||
452 | + | ||
453 | + if (query.getRewriteLatestIfDeleted()) { | ||
454 | + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> { | ||
455 | + if (isRemove) { | ||
456 | + return getNewLatestEntryFuture(entityId, query); | ||
457 | + } | ||
458 | + return Futures.immediateFuture(null); | ||
459 | + }, readResultsProcessingExecutor); | ||
460 | + | ||
461 | + return Futures.transformAsync(Futures.allAsList(Arrays.asList(savedLatestFuture, removedLatestFuture)), | ||
462 | + list -> Futures.immediateFuture(null), readResultsProcessingExecutor); | ||
463 | + } | ||
464 | + return removedLatestFuture; | ||
465 | + } | ||
466 | + | ||
467 | + private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) { | ||
468 | + long startTs = 0; | ||
469 | + long endTs = query.getStartTs() - 1; | ||
470 | + ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1, | ||
471 | + Aggregation.NONE, DESC_ORDER); | ||
472 | + ListenableFuture<List<TsKvEntry>> future = findAllAsync(entityId, findNewLatestQuery); | ||
473 | + | ||
474 | + return Futures.transformAsync(future, entryList -> { | ||
475 | + if (entryList.size() == 1) { | ||
476 | + return saveLatest(entityId, entryList.get(0)); | ||
477 | + } else { | ||
478 | + log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey()); | ||
479 | + } | ||
480 | + return Futures.immediateFuture(null); | ||
481 | + }, readResultsProcessingExecutor); | ||
482 | + } | ||
483 | + | ||
484 | + private ListenableFuture<Void> deleteLatest(EntityId entityId, String key) { | ||
485 | + Statement delete = QueryBuilder.delete().all().from(ModelConstants.TS_KV_LATEST_CF) | ||
486 | + .where(eq(ModelConstants.ENTITY_TYPE_COLUMN, entityId.getEntityType())) | ||
487 | + .and(eq(ModelConstants.ENTITY_ID_COLUMN, entityId.getId())) | ||
488 | + .and(eq(ModelConstants.KEY_COLUMN, key)); | ||
489 | + log.debug("Remove request: {}", delete.toString()); | ||
490 | + return getFuture(executeAsyncWrite(delete), rs -> null); | ||
491 | + } | ||
492 | + | ||
493 | + @Override | ||
494 | + public ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query) { | ||
495 | + long minPartition = toPartitionTs(query.getStartTs()); | ||
496 | + long maxPartition = toPartitionTs(query.getEndTs()); | ||
497 | + if (minPartition == maxPartition) { | ||
498 | + return Futures.immediateFuture(null); | ||
499 | + } else { | ||
500 | + ResultSetFuture partitionsFuture = fetchPartitions(entityId, query.getKey(), minPartition, maxPartition); | ||
501 | + | ||
502 | + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>(); | ||
503 | + final ListenableFuture<List<Long>> partitionsListFuture = Futures.transform(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor); | ||
504 | + | ||
505 | + Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() { | ||
506 | + @Override | ||
507 | + public void onSuccess(@Nullable List<Long> partitions) { | ||
508 | + int index = 0; | ||
509 | + if (minPartition != query.getStartTs()) { | ||
510 | + index = 1; | ||
511 | + } | ||
512 | + List<Long> partitionsToDelete = new ArrayList<>(); | ||
513 | + for (int i = index; i < partitions.size() - 1; i++) { | ||
514 | + partitionsToDelete.add(partitions.get(i)); | ||
515 | + } | ||
516 | + QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitionsToDelete); | ||
517 | + deletePartitionAsync(cursor, resultFuture); | ||
518 | + } | ||
519 | + | ||
520 | + @Override | ||
521 | + public void onFailure(Throwable t) { | ||
522 | + log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t); | ||
523 | + } | ||
524 | + }, readResultsProcessingExecutor); | ||
525 | + return resultFuture; | ||
526 | + } | ||
527 | + } | ||
528 | + | ||
529 | + private void deletePartitionAsync(final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) { | ||
530 | + if (!cursor.hasNextPartition()) { | ||
531 | + resultFuture.set(null); | ||
532 | + } else { | ||
533 | + PreparedStatement proto = getDeletePartitionStmt(); | ||
534 | + BoundStatement stmt = proto.bind(); | ||
535 | + stmt.setString(0, cursor.getEntityType()); | ||
536 | + stmt.setUUID(1, cursor.getEntityId()); | ||
537 | + stmt.setLong(2, cursor.getNextPartition()); | ||
538 | + stmt.setString(3, cursor.getKey()); | ||
539 | + | ||
540 | + Futures.addCallback(executeAsyncWrite(stmt), new FutureCallback<ResultSet>() { | ||
541 | + @Override | ||
542 | + public void onSuccess(@Nullable ResultSet result) { | ||
543 | + deletePartitionAsync(cursor, resultFuture); | ||
544 | + } | ||
545 | + | ||
546 | + @Override | ||
547 | + public void onFailure(Throwable t) { | ||
548 | + log.error("[{}][{}] Failed to delete data for query {}-{}", stmt, t); | ||
549 | + } | ||
550 | + }, readResultsProcessingExecutor); | ||
551 | + } | ||
552 | + } | ||
553 | + | ||
554 | + private PreparedStatement getDeletePartitionStmt() { | ||
555 | + if (deletePartitionStmt == null) { | ||
556 | + deletePartitionStmt = prepare("DELETE FROM " + ModelConstants.TS_KV_PARTITIONS_CF + | ||
557 | + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM | ||
558 | + + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM | ||
559 | + + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM | ||
560 | + + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM); | ||
561 | + } | ||
562 | + return deletePartitionStmt; | ||
563 | + } | ||
564 | + | ||
366 | private List<TsKvEntry> convertResultToTsKvEntryList(List<Row> rows) { | 565 | private List<TsKvEntry> convertResultToTsKvEntryList(List<Row> rows) { |
367 | List<TsKvEntry> entries = new ArrayList<>(rows.size()); | 566 | List<TsKvEntry> entries = new ArrayList<>(rows.size()); |
368 | if (!rows.isEmpty()) { | 567 | if (!rows.isEmpty()) { |
@@ -458,28 +657,43 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | @@ -458,28 +657,43 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem | ||
458 | return saveTtlStmts[dataType.ordinal()]; | 657 | return saveTtlStmts[dataType.ordinal()]; |
459 | } | 658 | } |
460 | 659 | ||
461 | - private PreparedStatement getFetchStmt(Aggregation aggType) { | ||
462 | - if (fetchStmts == null) { | ||
463 | - fetchStmts = new PreparedStatement[Aggregation.values().length]; | ||
464 | - for (Aggregation type : Aggregation.values()) { | ||
465 | - if (type == Aggregation.SUM && fetchStmts[Aggregation.AVG.ordinal()] != null) { | ||
466 | - fetchStmts[type.ordinal()] = fetchStmts[Aggregation.AVG.ordinal()]; | ||
467 | - } else if (type == Aggregation.AVG && fetchStmts[Aggregation.SUM.ordinal()] != null) { | ||
468 | - fetchStmts[type.ordinal()] = fetchStmts[Aggregation.SUM.ordinal()]; | ||
469 | - } else { | ||
470 | - fetchStmts[type.ordinal()] = prepare(SELECT_PREFIX + | ||
471 | - String.join(", ", ModelConstants.getFetchColumnNames(type)) + " FROM " + ModelConstants.TS_KV_CF | ||
472 | - + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM | ||
473 | - + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM | ||
474 | - + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM | ||
475 | - + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM | ||
476 | - + "AND " + ModelConstants.TS_COLUMN + " > ? " | ||
477 | - + "AND " + ModelConstants.TS_COLUMN + " <= ?" | ||
478 | - + (type == Aggregation.NONE ? " ORDER BY " + ModelConstants.TS_COLUMN + " DESC LIMIT ?" : "")); | 660 | + private PreparedStatement getFetchStmt(Aggregation aggType, String orderBy) { |
661 | + switch (orderBy) { | ||
662 | + case ASC_ORDER: | ||
663 | + if (fetchStmtsAsc == null) { | ||
664 | + fetchStmtsAsc = initFetchStmt(orderBy); | ||
665 | + } | ||
666 | + return fetchStmtsAsc[aggType.ordinal()]; | ||
667 | + case DESC_ORDER: | ||
668 | + if (fetchStmtsDesc == null) { | ||
669 | + fetchStmtsDesc = initFetchStmt(orderBy); | ||
479 | } | 670 | } |
671 | + return fetchStmtsDesc[aggType.ordinal()]; | ||
672 | + default: | ||
673 | + throw new RuntimeException("Not supported" + orderBy + "order!"); | ||
674 | + } | ||
675 | + } | ||
676 | + | ||
677 | + private PreparedStatement[] initFetchStmt(String orderBy) { | ||
678 | + PreparedStatement[] fetchStmts = new PreparedStatement[Aggregation.values().length]; | ||
679 | + for (Aggregation type : Aggregation.values()) { | ||
680 | + if (type == Aggregation.SUM && fetchStmts[Aggregation.AVG.ordinal()] != null) { | ||
681 | + fetchStmts[type.ordinal()] = fetchStmts[Aggregation.AVG.ordinal()]; | ||
682 | + } else if (type == Aggregation.AVG && fetchStmts[Aggregation.SUM.ordinal()] != null) { | ||
683 | + fetchStmts[type.ordinal()] = fetchStmts[Aggregation.SUM.ordinal()]; | ||
684 | + } else { | ||
685 | + fetchStmts[type.ordinal()] = prepare(SELECT_PREFIX + | ||
686 | + String.join(", ", ModelConstants.getFetchColumnNames(type)) + " FROM " + ModelConstants.TS_KV_CF | ||
687 | + + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM | ||
688 | + + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM | ||
689 | + + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM | ||
690 | + + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM | ||
691 | + + "AND " + ModelConstants.TS_COLUMN + " > ? " | ||
692 | + + "AND " + ModelConstants.TS_COLUMN + " <= ?" | ||
693 | + + (type == Aggregation.NONE ? " ORDER BY " + ModelConstants.TS_COLUMN + " " + orderBy + " LIMIT ?" : "")); | ||
480 | } | 694 | } |
481 | } | 695 | } |
482 | - return fetchStmts[aggType.ordinal()]; | 696 | + return fetchStmts; |
483 | } | 697 | } |
484 | 698 | ||
485 | private PreparedStatement getLatestStmt() { | 699 | private PreparedStatement getLatestStmt() { |
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.dao.timeseries; | ||
17 | + | ||
18 | +import lombok.Getter; | ||
19 | +import org.thingsboard.server.common.data.kv.TsKvQuery; | ||
20 | + | ||
21 | +import java.util.List; | ||
22 | +import java.util.UUID; | ||
23 | + | ||
24 | +public class QueryCursor { | ||
25 | + | ||
26 | + @Getter | ||
27 | + protected final String entityType; | ||
28 | + @Getter | ||
29 | + protected final UUID entityId; | ||
30 | + @Getter | ||
31 | + protected final String key; | ||
32 | + @Getter | ||
33 | + private final long startTs; | ||
34 | + @Getter | ||
35 | + private final long endTs; | ||
36 | + | ||
37 | + final List<Long> partitions; | ||
38 | + private int partitionIndex; | ||
39 | + | ||
40 | + public QueryCursor(String entityType, UUID entityId, TsKvQuery baseQuery, List<Long> partitions) { | ||
41 | + this.entityType = entityType; | ||
42 | + this.entityId = entityId; | ||
43 | + this.key = baseQuery.getKey(); | ||
44 | + this.startTs = baseQuery.getStartTs(); | ||
45 | + this.endTs = baseQuery.getEndTs(); | ||
46 | + this.partitions = partitions; | ||
47 | + this.partitionIndex = partitions.size() - 1; | ||
48 | + } | ||
49 | + | ||
50 | + public boolean hasNextPartition() { | ||
51 | + return partitionIndex >= 0; | ||
52 | + } | ||
53 | + | ||
54 | + public long getNextPartition() { | ||
55 | + long partition = partitions.get(partitionIndex); | ||
56 | + partitionIndex--; | ||
57 | + return partition; | ||
58 | + } | ||
59 | + | ||
60 | +} |
@@ -17,8 +17,9 @@ package org.thingsboard.server.dao.timeseries; | @@ -17,8 +17,9 @@ package org.thingsboard.server.dao.timeseries; | ||
17 | 17 | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | import org.thingsboard.server.common.data.id.EntityId; | 19 | import org.thingsboard.server.common.data.id.EntityId; |
20 | +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | ||
21 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | ||
20 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 22 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
21 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | ||
22 | 23 | ||
23 | import java.util.List; | 24 | import java.util.List; |
24 | 25 | ||
@@ -27,7 +28,7 @@ import java.util.List; | @@ -27,7 +28,7 @@ import java.util.List; | ||
27 | */ | 28 | */ |
28 | public interface TimeseriesDao { | 29 | public interface TimeseriesDao { |
29 | 30 | ||
30 | - ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<TsKvQuery> queries); | 31 | + ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, List<ReadTsKvQuery> queries); |
31 | 32 | ||
32 | ListenableFuture<TsKvEntry> findLatest(EntityId entityId, String key); | 33 | ListenableFuture<TsKvEntry> findLatest(EntityId entityId, String key); |
33 | 34 | ||
@@ -38,4 +39,10 @@ public interface TimeseriesDao { | @@ -38,4 +39,10 @@ public interface TimeseriesDao { | ||
38 | ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl); | 39 | ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl); |
39 | 40 | ||
40 | ListenableFuture<Void> saveLatest(EntityId entityId, TsKvEntry tsKvEntry); | 41 | ListenableFuture<Void> saveLatest(EntityId entityId, TsKvEntry tsKvEntry); |
42 | + | ||
43 | + ListenableFuture<Void> remove(EntityId entityId, DeleteTsKvQuery query); | ||
44 | + | ||
45 | + ListenableFuture<Void> removeLatest(EntityId entityId, DeleteTsKvQuery query); | ||
46 | + | ||
47 | + ListenableFuture<Void> removePartition(EntityId entityId, DeleteTsKvQuery query); | ||
41 | } | 48 | } |
@@ -17,8 +17,9 @@ package org.thingsboard.server.dao.timeseries; | @@ -17,8 +17,9 @@ package org.thingsboard.server.dao.timeseries; | ||
17 | 17 | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | import org.thingsboard.server.common.data.id.EntityId; | 19 | import org.thingsboard.server.common.data.id.EntityId; |
20 | +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | ||
21 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | ||
20 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 22 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
21 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | ||
22 | 23 | ||
23 | import java.util.Collection; | 24 | import java.util.Collection; |
24 | import java.util.List; | 25 | import java.util.List; |
@@ -28,7 +29,7 @@ import java.util.List; | @@ -28,7 +29,7 @@ import java.util.List; | ||
28 | */ | 29 | */ |
29 | public interface TimeseriesService { | 30 | public interface TimeseriesService { |
30 | 31 | ||
31 | - ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<TsKvQuery> queries); | 32 | + ListenableFuture<List<TsKvEntry>> findAll(EntityId entityId, List<ReadTsKvQuery> queries); |
32 | 33 | ||
33 | ListenableFuture<List<TsKvEntry>> findLatest(EntityId entityId, Collection<String> keys); | 34 | ListenableFuture<List<TsKvEntry>> findLatest(EntityId entityId, Collection<String> keys); |
34 | 35 | ||
@@ -37,4 +38,6 @@ public interface TimeseriesService { | @@ -37,4 +38,6 @@ public interface TimeseriesService { | ||
37 | ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry); | 38 | ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry); |
38 | 39 | ||
39 | ListenableFuture<List<Void>> save(EntityId entityId, List<TsKvEntry> tsKvEntry, long ttl); | 40 | ListenableFuture<List<Void>> save(EntityId entityId, List<TsKvEntry> tsKvEntry, long ttl); |
41 | + | ||
42 | + ListenableFuture<List<Void>> remove(EntityId entityId, List<DeleteTsKvQuery> queries); | ||
40 | } | 43 | } |
@@ -16,57 +16,53 @@ | @@ -16,57 +16,53 @@ | ||
16 | package org.thingsboard.server.dao.timeseries; | 16 | package org.thingsboard.server.dao.timeseries; |
17 | 17 | ||
18 | import lombok.Getter; | 18 | import lombok.Getter; |
19 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | ||
19 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 20 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
20 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | ||
21 | 21 | ||
22 | import java.util.ArrayList; | 22 | import java.util.ArrayList; |
23 | import java.util.List; | 23 | import java.util.List; |
24 | import java.util.UUID; | 24 | import java.util.UUID; |
25 | 25 | ||
26 | +import static org.thingsboard.server.dao.timeseries.CassandraBaseTimeseriesDao.DESC_ORDER; | ||
27 | + | ||
26 | /** | 28 | /** |
27 | * Created by ashvayka on 21.02.17. | 29 | * Created by ashvayka on 21.02.17. |
28 | */ | 30 | */ |
29 | -public class TsKvQueryCursor { | ||
30 | - @Getter | ||
31 | - private final String entityType; | ||
32 | - @Getter | ||
33 | - private final UUID entityId; | ||
34 | - @Getter | ||
35 | - private final String key; | ||
36 | - @Getter | ||
37 | - private final long startTs; | ||
38 | - @Getter | ||
39 | - private final long endTs; | ||
40 | - private final List<Long> partitions; | 31 | +public class TsKvQueryCursor extends QueryCursor { |
32 | + | ||
41 | @Getter | 33 | @Getter |
42 | private final List<TsKvEntry> data; | 34 | private final List<TsKvEntry> data; |
35 | + @Getter | ||
36 | + private String orderBy; | ||
43 | 37 | ||
44 | private int partitionIndex; | 38 | private int partitionIndex; |
45 | private int currentLimit; | 39 | private int currentLimit; |
46 | 40 | ||
47 | - public TsKvQueryCursor(String entityType, UUID entityId, TsKvQuery baseQuery, List<Long> partitions) { | ||
48 | - this.entityType = entityType; | ||
49 | - this.entityId = entityId; | ||
50 | - this.key = baseQuery.getKey(); | ||
51 | - this.startTs = baseQuery.getStartTs(); | ||
52 | - this.endTs = baseQuery.getEndTs(); | ||
53 | - this.partitions = partitions; | ||
54 | - this.partitionIndex = partitions.size() - 1; | 41 | + public TsKvQueryCursor(String entityType, UUID entityId, ReadTsKvQuery baseQuery, List<Long> partitions) { |
42 | + super(entityType, entityId, baseQuery, partitions); | ||
43 | + this.orderBy = baseQuery.getOrderBy(); | ||
44 | + this.partitionIndex = isDesc() ? partitions.size() - 1 : 0; | ||
55 | this.data = new ArrayList<>(); | 45 | this.data = new ArrayList<>(); |
56 | this.currentLimit = baseQuery.getLimit(); | 46 | this.currentLimit = baseQuery.getLimit(); |
57 | } | 47 | } |
58 | 48 | ||
49 | + @Override | ||
59 | public boolean hasNextPartition() { | 50 | public boolean hasNextPartition() { |
60 | - return partitionIndex >= 0; | 51 | + return isDesc() ? partitionIndex >= 0 : partitionIndex <= partitions.size() - 1; |
61 | } | 52 | } |
62 | 53 | ||
63 | public boolean isFull() { | 54 | public boolean isFull() { |
64 | return currentLimit <= 0; | 55 | return currentLimit <= 0; |
65 | } | 56 | } |
66 | 57 | ||
58 | + @Override | ||
67 | public long getNextPartition() { | 59 | public long getNextPartition() { |
68 | long partition = partitions.get(partitionIndex); | 60 | long partition = partitions.get(partitionIndex); |
69 | - partitionIndex--; | 61 | + if (isDesc()) { |
62 | + partitionIndex--; | ||
63 | + } else { | ||
64 | + partitionIndex++; | ||
65 | + } | ||
70 | return partition; | 66 | return partition; |
71 | } | 67 | } |
72 | 68 | ||
@@ -78,4 +74,8 @@ public class TsKvQueryCursor { | @@ -78,4 +74,8 @@ public class TsKvQueryCursor { | ||
78 | currentLimit -= newData.size(); | 74 | currentLimit -= newData.size(); |
79 | data.addAll(newData); | 75 | data.addAll(newData); |
80 | } | 76 | } |
77 | + | ||
78 | + private boolean isDesc() { | ||
79 | + return orderBy.equals(DESC_ORDER); | ||
80 | + } | ||
81 | } | 81 | } |
@@ -227,6 +227,13 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { | @@ -227,6 +227,13 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { | ||
227 | Assert.assertTrue(relations.contains(relationA)); | 227 | Assert.assertTrue(relations.contains(relationA)); |
228 | Assert.assertTrue(relations.contains(relationB)); | 228 | Assert.assertTrue(relations.contains(relationB)); |
229 | Assert.assertTrue(relations.contains(relationC)); | 229 | Assert.assertTrue(relations.contains(relationC)); |
230 | + | ||
231 | + //Test from cache | ||
232 | + relations = relationService.findByQuery(query).get(); | ||
233 | + Assert.assertEquals(3, relations.size()); | ||
234 | + Assert.assertTrue(relations.contains(relationA)); | ||
235 | + Assert.assertTrue(relations.contains(relationB)); | ||
236 | + Assert.assertTrue(relations.contains(relationC)); | ||
230 | } | 237 | } |
231 | 238 | ||
232 | @Test | 239 | @Test |
@@ -253,6 +260,12 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { | @@ -253,6 +260,12 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { | ||
253 | Assert.assertEquals(2, relations.size()); | 260 | Assert.assertEquals(2, relations.size()); |
254 | Assert.assertTrue(relations.contains(relationAB)); | 261 | Assert.assertTrue(relations.contains(relationAB)); |
255 | Assert.assertTrue(relations.contains(relationBC)); | 262 | Assert.assertTrue(relations.contains(relationBC)); |
263 | + | ||
264 | + //Test from cache | ||
265 | + relations = relationService.findByQuery(query).get(); | ||
266 | + Assert.assertEquals(2, relations.size()); | ||
267 | + Assert.assertTrue(relations.contains(relationAB)); | ||
268 | + Assert.assertTrue(relations.contains(relationBC)); | ||
256 | } | 269 | } |
257 | 270 | ||
258 | 271 |
@@ -21,7 +21,8 @@ import org.junit.Assert; | @@ -21,7 +21,8 @@ import org.junit.Assert; | ||
21 | import org.junit.Test; | 21 | import org.junit.Test; |
22 | import org.thingsboard.server.common.data.id.DeviceId; | 22 | import org.thingsboard.server.common.data.id.DeviceId; |
23 | import org.thingsboard.server.common.data.kv.Aggregation; | 23 | import org.thingsboard.server.common.data.kv.Aggregation; |
24 | -import org.thingsboard.server.common.data.kv.BaseTsKvQuery; | 24 | +import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery; |
25 | +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; | ||
25 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | 26 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
26 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; | 27 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
27 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; | 28 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
@@ -53,6 +54,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -53,6 +54,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
53 | private static final String BOOLEAN_KEY = "booleanKey"; | 54 | private static final String BOOLEAN_KEY = "booleanKey"; |
54 | 55 | ||
55 | private static final long TS = 42L; | 56 | private static final long TS = 42L; |
57 | + private static final String DESC_ORDER = "DESC"; | ||
56 | 58 | ||
57 | KvEntry stringKvEntry = new StringDataEntry(STRING_KEY, "value"); | 59 | KvEntry stringKvEntry = new StringDataEntry(STRING_KEY, "value"); |
58 | KvEntry longKvEntry = new LongDataEntry(LONG_KEY, Long.MAX_VALUE); | 60 | KvEntry longKvEntry = new LongDataEntry(LONG_KEY, Long.MAX_VALUE); |
@@ -101,6 +103,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -101,6 +103,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
101 | } | 103 | } |
102 | 104 | ||
103 | @Test | 105 | @Test |
106 | + public void testDeleteDeviceTsData() throws Exception { | ||
107 | + DeviceId deviceId = new DeviceId(UUIDs.timeBased()); | ||
108 | + | ||
109 | + saveEntries(deviceId, 10000); | ||
110 | + saveEntries(deviceId, 20000); | ||
111 | + saveEntries(deviceId, 30000); | ||
112 | + saveEntries(deviceId, 40000); | ||
113 | + | ||
114 | + tsService.remove(deviceId, Collections.singletonList( | ||
115 | + new BaseDeleteTsKvQuery(STRING_KEY, 15000, 45000))).get(); | ||
116 | + | ||
117 | + List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList( | ||
118 | + new BaseReadTsKvQuery(STRING_KEY, 5000, 45000, 10000, 10, Aggregation.NONE))).get(); | ||
119 | + Assert.assertEquals(1, list.size()); | ||
120 | + | ||
121 | + List<TsKvEntry> latest = tsService.findLatest(deviceId, Collections.singletonList(STRING_KEY)).get(); | ||
122 | + Assert.assertEquals(null, latest.get(0).getValueAsString()); | ||
123 | + } | ||
124 | + | ||
125 | + @Test | ||
104 | public void testFindDeviceTsData() throws Exception { | 126 | public void testFindDeviceTsData() throws Exception { |
105 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); | 127 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); |
106 | List<TsKvEntry> entries = new ArrayList<>(); | 128 | List<TsKvEntry> entries = new ArrayList<>(); |
@@ -114,7 +136,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -114,7 +136,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
114 | entries.add(save(deviceId, 45000, 500)); | 136 | entries.add(save(deviceId, 45000, 500)); |
115 | entries.add(save(deviceId, 55000, 600)); | 137 | entries.add(save(deviceId, 55000, 600)); |
116 | 138 | ||
117 | - List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0, | 139 | + List<TsKvEntry> list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0, |
118 | 60000, 20000, 3, Aggregation.NONE))).get(); | 140 | 60000, 20000, 3, Aggregation.NONE))).get(); |
119 | assertEquals(3, list.size()); | 141 | assertEquals(3, list.size()); |
120 | assertEquals(55000, list.get(0).getTs()); | 142 | assertEquals(55000, list.get(0).getTs()); |
@@ -126,7 +148,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -126,7 +148,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
126 | assertEquals(35000, list.get(2).getTs()); | 148 | assertEquals(35000, list.get(2).getTs()); |
127 | assertEquals(java.util.Optional.of(400L), list.get(2).getLongValue()); | 149 | assertEquals(java.util.Optional.of(400L), list.get(2).getLongValue()); |
128 | 150 | ||
129 | - list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0, | 151 | + list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0, |
130 | 60000, 20000, 3, Aggregation.AVG))).get(); | 152 | 60000, 20000, 3, Aggregation.AVG))).get(); |
131 | assertEquals(3, list.size()); | 153 | assertEquals(3, list.size()); |
132 | assertEquals(10000, list.get(0).getTs()); | 154 | assertEquals(10000, list.get(0).getTs()); |
@@ -138,7 +160,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -138,7 +160,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
138 | assertEquals(50000, list.get(2).getTs()); | 160 | assertEquals(50000, list.get(2).getTs()); |
139 | assertEquals(java.util.Optional.of(550L), list.get(2).getLongValue()); | 161 | assertEquals(java.util.Optional.of(550L), list.get(2).getLongValue()); |
140 | 162 | ||
141 | - list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0, | 163 | + list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0, |
142 | 60000, 20000, 3, Aggregation.SUM))).get(); | 164 | 60000, 20000, 3, Aggregation.SUM))).get(); |
143 | 165 | ||
144 | assertEquals(3, list.size()); | 166 | assertEquals(3, list.size()); |
@@ -151,7 +173,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -151,7 +173,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
151 | assertEquals(50000, list.get(2).getTs()); | 173 | assertEquals(50000, list.get(2).getTs()); |
152 | assertEquals(java.util.Optional.of(1100L), list.get(2).getLongValue()); | 174 | assertEquals(java.util.Optional.of(1100L), list.get(2).getLongValue()); |
153 | 175 | ||
154 | - list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0, | 176 | + list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0, |
155 | 60000, 20000, 3, Aggregation.MIN))).get(); | 177 | 60000, 20000, 3, Aggregation.MIN))).get(); |
156 | 178 | ||
157 | assertEquals(3, list.size()); | 179 | assertEquals(3, list.size()); |
@@ -164,7 +186,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -164,7 +186,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
164 | assertEquals(50000, list.get(2).getTs()); | 186 | assertEquals(50000, list.get(2).getTs()); |
165 | assertEquals(java.util.Optional.of(500L), list.get(2).getLongValue()); | 187 | assertEquals(java.util.Optional.of(500L), list.get(2).getLongValue()); |
166 | 188 | ||
167 | - list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0, | 189 | + list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0, |
168 | 60000, 20000, 3, Aggregation.MAX))).get(); | 190 | 60000, 20000, 3, Aggregation.MAX))).get(); |
169 | 191 | ||
170 | assertEquals(3, list.size()); | 192 | assertEquals(3, list.size()); |
@@ -177,7 +199,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | @@ -177,7 +199,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { | ||
177 | assertEquals(50000, list.get(2).getTs()); | 199 | assertEquals(50000, list.get(2).getTs()); |
178 | assertEquals(java.util.Optional.of(600L), list.get(2).getLongValue()); | 200 | assertEquals(java.util.Optional.of(600L), list.get(2).getLongValue()); |
179 | 201 | ||
180 | - list = tsService.findAll(deviceId, Collections.singletonList(new BaseTsKvQuery(LONG_KEY, 0, | 202 | + list = tsService.findAll(deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0, |
181 | 60000, 20000, 3, Aggregation.COUNT))).get(); | 203 | 60000, 20000, 3, Aggregation.COUNT))).get(); |
182 | 204 | ||
183 | assertEquals(3, list.size()); | 205 | assertEquals(3, list.size()); |
@@ -40,6 +40,8 @@ public final class MqttClientConfig { | @@ -40,6 +40,8 @@ public final class MqttClientConfig { | ||
40 | private Class<? extends Channel> channelClass = NioSocketChannel.class; | 40 | private Class<? extends Channel> channelClass = NioSocketChannel.class; |
41 | 41 | ||
42 | private boolean reconnect = true; | 42 | private boolean reconnect = true; |
43 | + private long reconnectDelay = 1L; | ||
44 | + private int maxBytesInMessage = 8092; | ||
43 | 45 | ||
44 | public MqttClientConfig() { | 46 | public MqttClientConfig() { |
45 | this(null); | 47 | this(null); |
@@ -146,4 +148,38 @@ public final class MqttClientConfig { | @@ -146,4 +148,38 @@ public final class MqttClientConfig { | ||
146 | public void setReconnect(boolean reconnect) { | 148 | public void setReconnect(boolean reconnect) { |
147 | this.reconnect = reconnect; | 149 | this.reconnect = reconnect; |
148 | } | 150 | } |
151 | + | ||
152 | + public long getReconnectDelay() { | ||
153 | + return reconnectDelay; | ||
154 | + } | ||
155 | + | ||
156 | + /** | ||
157 | + * Sets the reconnect delay in seconds. Defaults to 1 second. | ||
158 | + * @param reconnectDelay | ||
159 | + * @throws IllegalArgumentException if reconnectDelay is smaller than 1. | ||
160 | + */ | ||
161 | + public void setReconnectDelay(long reconnectDelay) { | ||
162 | + if (reconnectDelay <= 0) { | ||
163 | + throw new IllegalArgumentException("reconnectDelay must be > 0"); | ||
164 | + } | ||
165 | + this.reconnectDelay = reconnectDelay; | ||
166 | + } | ||
167 | + | ||
168 | + public int getMaxBytesInMessage() { | ||
169 | + return maxBytesInMessage; | ||
170 | + } | ||
171 | + | ||
172 | + /** | ||
173 | + * Sets the maximum number of bytes in the message for the {@link io.netty.handler.codec.mqtt.MqttDecoder}. | ||
174 | + * Default value is 8092 as specified by Netty. The absolute maximum size is 256MB as set by the MQTT spec. | ||
175 | + * | ||
176 | + * @param maxBytesInMessage | ||
177 | + * @throws IllegalArgumentException if maxBytesInMessage is smaller than 1 or greater than 256_000_000. | ||
178 | + */ | ||
179 | + public void setMaxBytesInMessage(int maxBytesInMessage) { | ||
180 | + if (maxBytesInMessage <= 0 || maxBytesInMessage > 256_000_000) { | ||
181 | + throw new IllegalArgumentException("maxBytesInMessage must be > 0 or < 256_000_000"); | ||
182 | + } | ||
183 | + this.maxBytesInMessage = maxBytesInMessage; | ||
184 | + } | ||
149 | } | 185 | } |
@@ -155,7 +155,7 @@ final class MqttClientImpl implements MqttClient { | @@ -155,7 +155,7 @@ final class MqttClientImpl implements MqttClient { | ||
155 | if (reconnect) { | 155 | if (reconnect) { |
156 | this.reconnect = true; | 156 | this.reconnect = true; |
157 | } | 157 | } |
158 | - eventLoop.schedule((Runnable) () -> connect(host, port, reconnect), 1L, TimeUnit.SECONDS); | 158 | + eventLoop.schedule((Runnable) () -> connect(host, port, reconnect), clientConfig.getReconnectDelay(), TimeUnit.SECONDS); |
159 | } | 159 | } |
160 | } | 160 | } |
161 | 161 | ||
@@ -512,7 +512,7 @@ final class MqttClientImpl implements MqttClient { | @@ -512,7 +512,7 @@ final class MqttClientImpl implements MqttClient { | ||
512 | ch.pipeline().addLast(sslContext.newHandler(ch.alloc(), host, port)); | 512 | ch.pipeline().addLast(sslContext.newHandler(ch.alloc(), host, port)); |
513 | } | 513 | } |
514 | 514 | ||
515 | - ch.pipeline().addLast("mqttDecoder", new MqttDecoder()); | 515 | + ch.pipeline().addLast("mqttDecoder", new MqttDecoder(clientConfig.getMaxBytesInMessage())); |
516 | ch.pipeline().addLast("mqttEncoder", MqttEncoder.INSTANCE); | 516 | ch.pipeline().addLast("mqttEncoder", MqttEncoder.INSTANCE); |
517 | ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(MqttClientImpl.this.clientConfig.getTimeoutSeconds(), MqttClientImpl.this.clientConfig.getTimeoutSeconds(), 0)); | 517 | ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(MqttClientImpl.this.clientConfig.getTimeoutSeconds(), MqttClientImpl.this.clientConfig.getTimeoutSeconds(), 0)); |
518 | ch.pipeline().addLast("mqttPingHandler", new MqttPingHandler(MqttClientImpl.this.clientConfig.getTimeoutSeconds())); | 518 | ch.pipeline().addLast("mqttPingHandler", new MqttPingHandler(MqttClientImpl.this.clientConfig.getTimeoutSeconds())); |
@@ -16,11 +16,14 @@ | @@ -16,11 +16,14 @@ | ||
16 | package org.thingsboard.rule.engine.api; | 16 | package org.thingsboard.rule.engine.api; |
17 | 17 | ||
18 | import com.google.common.util.concurrent.FutureCallback; | 18 | import com.google.common.util.concurrent.FutureCallback; |
19 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
19 | import org.thingsboard.server.common.data.id.EntityId; | 20 | import org.thingsboard.server.common.data.id.EntityId; |
21 | +import org.thingsboard.server.common.data.id.TenantId; | ||
20 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 22 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
21 | import org.thingsboard.server.common.data.kv.TsKvEntry; | 23 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
22 | 24 | ||
23 | import java.util.List; | 25 | import java.util.List; |
26 | +import java.util.Set; | ||
24 | 27 | ||
25 | /** | 28 | /** |
26 | * Created by ashvayka on 02.04.18. | 29 | * Created by ashvayka on 02.04.18. |
@@ -41,4 +44,6 @@ public interface RuleEngineTelemetryService { | @@ -41,4 +44,6 @@ public interface RuleEngineTelemetryService { | ||
41 | 44 | ||
42 | void saveAttrAndNotify(EntityId entityId, String scope, String key, boolean value, FutureCallback<Void> callback); | 45 | void saveAttrAndNotify(EntityId entityId, String scope, String key, boolean value, FutureCallback<Void> callback); |
43 | 46 | ||
47 | + void onSharedAttributesUpdate(TenantId tenantId, DeviceId deviceId, Set<AttributeKvEntry> attributes); | ||
48 | + | ||
44 | } | 49 | } |
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.metadata; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.core.JsonGenerator; | ||
19 | +import com.fasterxml.jackson.core.JsonParser; | ||
20 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
21 | +import com.fasterxml.jackson.databind.node.ArrayNode; | ||
22 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
23 | +import com.google.common.util.concurrent.ListenableFuture; | ||
24 | +import lombok.extern.slf4j.Slf4j; | ||
25 | +import org.thingsboard.rule.engine.api.*; | ||
26 | +import org.thingsboard.rule.engine.api.util.DonAsynchron; | ||
27 | +import org.thingsboard.rule.engine.api.util.TbNodeUtils; | ||
28 | +import org.thingsboard.server.common.data.kv.BaseTsKvQuery; | ||
29 | +import org.thingsboard.server.common.data.kv.TsKvEntry; | ||
30 | +import org.thingsboard.server.common.data.kv.TsKvQuery; | ||
31 | +import org.thingsboard.server.common.data.plugin.ComponentType; | ||
32 | +import org.thingsboard.server.common.msg.TbMsg; | ||
33 | + | ||
34 | +import java.util.List; | ||
35 | +import java.util.concurrent.ExecutionException; | ||
36 | +import java.util.concurrent.TimeUnit; | ||
37 | +import java.util.stream.Collectors; | ||
38 | + | ||
39 | +import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; | ||
40 | +import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.FETCH_MODE_ALL; | ||
41 | +import static org.thingsboard.rule.engine.metadata.TbGetTelemetryNodeConfiguration.MAX_FETCH_SIZE; | ||
42 | +import static org.thingsboard.server.common.data.kv.Aggregation.NONE; | ||
43 | + | ||
44 | +/** | ||
45 | + * Created by mshvayka on 04.09.18. | ||
46 | + */ | ||
47 | +@Slf4j | ||
48 | +@RuleNode(type = ComponentType.ENRICHMENT, | ||
49 | + name = "originator telemetry", | ||
50 | + configClazz = TbGetTelemetryNodeConfiguration.class, | ||
51 | + nodeDescription = "Add Message Originator Telemetry for selected time range into Message Metadata\n", | ||
52 | + nodeDetails = "The node allows you to select fetch mode <b>FIRST/LAST/ALL</b> to fetch telemetry of certain time range that are added into Message metadata without any prefix. " + | ||
53 | + "If selected fetch mode <b>ALL</b> Telemetry will be added like array into Message Metadata where <b>key</b> is Timestamp and <b>value</b> is value of Telemetry. " + | ||
54 | + "If selected fetch mode <b>FIRST</b> or <b>LAST</b> Telemetry will be added like string without Timestamp", | ||
55 | + uiResources = {"static/rulenode/rulenode-core-config.js"}, | ||
56 | + configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase") | ||
57 | +public class TbGetTelemetryNode implements TbNode { | ||
58 | + | ||
59 | + private TbGetTelemetryNodeConfiguration config; | ||
60 | + private List<String> tsKeyNames; | ||
61 | + private long startTsOffset; | ||
62 | + private long endTsOffset; | ||
63 | + private int limit; | ||
64 | + private ObjectMapper mapper; | ||
65 | + | ||
66 | + @Override | ||
67 | + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { | ||
68 | + this.config = TbNodeUtils.convert(configuration, TbGetTelemetryNodeConfiguration.class); | ||
69 | + tsKeyNames = config.getLatestTsKeyNames(); | ||
70 | + startTsOffset = TimeUnit.valueOf(config.getStartIntervalTimeUnit()).toMillis(config.getStartInterval()); | ||
71 | + endTsOffset = TimeUnit.valueOf(config.getEndIntervalTimeUnit()).toMillis(config.getEndInterval()); | ||
72 | + limit = config.getFetchMode().equals(FETCH_MODE_ALL) ? MAX_FETCH_SIZE : 1; | ||
73 | + mapper = new ObjectMapper(); | ||
74 | + mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false); | ||
75 | + mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); | ||
76 | + } | ||
77 | + | ||
78 | + @Override | ||
79 | + public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { | ||
80 | + if (tsKeyNames.isEmpty()) { | ||
81 | + ctx.tellFailure(msg, new IllegalStateException("Telemetry is not selected!")); | ||
82 | + } else { | ||
83 | + try { | ||
84 | + List<TsKvQuery> queries = buildQueries(); | ||
85 | + ListenableFuture<List<TsKvEntry>> list = ctx.getTimeseriesService().findAll(msg.getOriginator(), queries); | ||
86 | + DonAsynchron.withCallback(list, data -> { | ||
87 | + process(data, msg); | ||
88 | + TbMsg newMsg = ctx.newMsg(msg.getType(), msg.getOriginator(), msg.getMetaData(), msg.getData()); | ||
89 | + ctx.tellNext(newMsg, SUCCESS); | ||
90 | + }, error -> ctx.tellFailure(msg, error), ctx.getDbCallbackExecutor()); | ||
91 | + } catch (Exception e) { | ||
92 | + ctx.tellFailure(msg, e); | ||
93 | + } | ||
94 | + } | ||
95 | + } | ||
96 | + | ||
97 | + //TODO: handle direction; | ||
98 | + private List<TsKvQuery> buildQueries() { | ||
99 | + long ts = System.currentTimeMillis(); | ||
100 | + long startTs = ts - startTsOffset; | ||
101 | + long endTs = ts - endTsOffset; | ||
102 | + | ||
103 | + return tsKeyNames.stream() | ||
104 | + .map(key -> new BaseTsKvQuery(key, startTs, endTs, 1, limit, NONE)) | ||
105 | + .collect(Collectors.toList()); | ||
106 | + } | ||
107 | + | ||
108 | + private void process(List<TsKvEntry> entries, TbMsg msg) { | ||
109 | + ObjectNode resultNode = mapper.createObjectNode(); | ||
110 | + if (limit == MAX_FETCH_SIZE) { | ||
111 | + entries.forEach(entry -> processArray(resultNode, entry)); | ||
112 | + } else { | ||
113 | + entries.forEach(entry -> processSingle(resultNode, entry)); | ||
114 | + } | ||
115 | + | ||
116 | + for (String key : tsKeyNames) { | ||
117 | + if(resultNode.has(key)){ | ||
118 | + msg.getMetaData().putValue(key, resultNode.get(key).toString()); | ||
119 | + } | ||
120 | + } | ||
121 | + } | ||
122 | + | ||
123 | + private void processSingle(ObjectNode node, TsKvEntry entry) { | ||
124 | + node.put(entry.getKey(), entry.getValueAsString()); | ||
125 | + } | ||
126 | + | ||
127 | + private void processArray(ObjectNode node, TsKvEntry entry) { | ||
128 | + if(node.has(entry.getKey())){ | ||
129 | + ArrayNode arrayNode = (ArrayNode) node.get(entry.getKey()); | ||
130 | + ObjectNode obj = buildNode(entry); | ||
131 | + arrayNode.add(obj); | ||
132 | + }else { | ||
133 | + ArrayNode arrayNode = mapper.createArrayNode(); | ||
134 | + ObjectNode obj = buildNode(entry); | ||
135 | + arrayNode.add(obj); | ||
136 | + node.set(entry.getKey(), arrayNode); | ||
137 | + } | ||
138 | + } | ||
139 | + | ||
140 | + private ObjectNode buildNode(TsKvEntry entry) { | ||
141 | + ObjectNode obj = mapper.createObjectNode() | ||
142 | + .put("ts", entry.getTs()); | ||
143 | + switch (entry.getDataType()) { | ||
144 | + case STRING: | ||
145 | + obj.put("value", entry.getValueAsString()); | ||
146 | + break; | ||
147 | + case LONG: | ||
148 | + obj.put("value", entry.getLongValue().get()); | ||
149 | + break; | ||
150 | + case BOOLEAN: | ||
151 | + obj.put("value", entry.getBooleanValue().get()); | ||
152 | + break; | ||
153 | + case DOUBLE: | ||
154 | + obj.put("value", entry.getDoubleValue().get()); | ||
155 | + break; | ||
156 | + } | ||
157 | + return obj; | ||
158 | + } | ||
159 | + | ||
160 | + @Override | ||
161 | + public void destroy() { | ||
162 | + | ||
163 | + } | ||
164 | +} |
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.metadata; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import org.thingsboard.rule.engine.api.NodeConfiguration; | ||
20 | + | ||
21 | +import java.util.Collections; | ||
22 | +import java.util.List; | ||
23 | +import java.util.concurrent.TimeUnit; | ||
24 | + | ||
25 | +/** | ||
26 | + * Created by mshvayka on 04.09.18. | ||
27 | + */ | ||
28 | +@Data | ||
29 | +public class TbGetTelemetryNodeConfiguration implements NodeConfiguration<TbGetTelemetryNodeConfiguration> { | ||
30 | + | ||
31 | + public static final String FETCH_MODE_FIRST = "FIRST"; | ||
32 | + public static final String FETCH_MODE_LAST = "LAST"; | ||
33 | + public static final String FETCH_MODE_ALL = "ALL"; | ||
34 | + public static final int MAX_FETCH_SIZE = 1000; | ||
35 | + | ||
36 | + private int startInterval; | ||
37 | + private int endInterval; | ||
38 | + private String startIntervalTimeUnit; | ||
39 | + private String endIntervalTimeUnit; | ||
40 | + private String fetchMode; //FIRST, LAST, LATEST | ||
41 | + | ||
42 | + private List<String> latestTsKeyNames; | ||
43 | + | ||
44 | + | ||
45 | + | ||
46 | + @Override | ||
47 | + public TbGetTelemetryNodeConfiguration defaultConfiguration() { | ||
48 | + TbGetTelemetryNodeConfiguration configuration = new TbGetTelemetryNodeConfiguration(); | ||
49 | + configuration.setLatestTsKeyNames(Collections.emptyList()); | ||
50 | + configuration.setFetchMode("FIRST"); | ||
51 | + configuration.setStartIntervalTimeUnit(TimeUnit.MINUTES.name()); | ||
52 | + configuration.setStartInterval(2); | ||
53 | + configuration.setEndIntervalTimeUnit(TimeUnit.MINUTES.name()); | ||
54 | + configuration.setEndInterval(1); | ||
55 | + return configuration; | ||
56 | + } | ||
57 | +} |
@@ -17,12 +17,15 @@ package org.thingsboard.rule.engine.telemetry; | @@ -17,12 +17,15 @@ package org.thingsboard.rule.engine.telemetry; | ||
17 | 17 | ||
18 | import com.google.gson.JsonParser; | 18 | import com.google.gson.JsonParser; |
19 | import lombok.extern.slf4j.Slf4j; | 19 | import lombok.extern.slf4j.Slf4j; |
20 | -import org.thingsboard.rule.engine.api.util.TbNodeUtils; | ||
21 | import org.thingsboard.rule.engine.api.RuleNode; | 20 | import org.thingsboard.rule.engine.api.RuleNode; |
22 | import org.thingsboard.rule.engine.api.TbContext; | 21 | import org.thingsboard.rule.engine.api.TbContext; |
23 | import org.thingsboard.rule.engine.api.TbNode; | 22 | import org.thingsboard.rule.engine.api.TbNode; |
24 | import org.thingsboard.rule.engine.api.TbNodeConfiguration; | 23 | import org.thingsboard.rule.engine.api.TbNodeConfiguration; |
25 | import org.thingsboard.rule.engine.api.TbNodeException; | 24 | import org.thingsboard.rule.engine.api.TbNodeException; |
25 | +import org.thingsboard.rule.engine.api.util.TbNodeUtils; | ||
26 | +import org.thingsboard.server.common.data.DataConstants; | ||
27 | +import org.thingsboard.server.common.data.EntityType; | ||
28 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
26 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 29 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
27 | import org.thingsboard.server.common.data.plugin.ComponentType; | 30 | import org.thingsboard.server.common.data.plugin.ComponentType; |
28 | import org.thingsboard.server.common.msg.TbMsg; | 31 | import org.thingsboard.server.common.msg.TbMsg; |
@@ -62,6 +65,9 @@ public class TbMsgAttributesNode implements TbNode { | @@ -62,6 +65,9 @@ public class TbMsgAttributesNode implements TbNode { | ||
62 | String src = msg.getData(); | 65 | String src = msg.getData(); |
63 | Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src)).getAttributes(); | 66 | Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src)).getAttributes(); |
64 | ctx.getTelemetryService().saveAndNotify(msg.getOriginator(), config.getScope(), new ArrayList<>(attributes), new TelemetryNodeCallback(ctx, msg)); | 67 | ctx.getTelemetryService().saveAndNotify(msg.getOriginator(), config.getScope(), new ArrayList<>(attributes), new TelemetryNodeCallback(ctx, msg)); |
68 | + if (msg.getOriginator().getEntityType() == EntityType.DEVICE && DataConstants.SHARED_SCOPE.equals(config.getScope())) { | ||
69 | + ctx.getTelemetryService().onSharedAttributesUpdate(ctx.getTenantId(), new DeviceId(msg.getOriginator().getId()), attributes); | ||
70 | + } | ||
65 | } | 71 | } |
66 | 72 | ||
67 | @Override | 73 | @Override |
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
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:'...'}}" }\'>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> </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}])); | 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(79)},function(e,t){},1,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 class=tb-telemetry-from-database-config ng-form name=getTelemetryConfigForm layout=column> <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> <md-input-container style=margin-bottom:38px;margin-top:58px> <label translate class="tb-title no-padding">tb.rulenode.fetch-mode</label> <md-select required ng-model=configuration.fetchMode> <md-option ng-repeat="type in ruleNodeTypes.fetchModeType" ng-value=type> {{ type }} </md-option> </md-select> </md-input-container> <div layout=column layout-gt-sm=row> <md-input-container flex class="md-block tb-time-value"> <label translate class="tb-title no-padding">tb.rulenode.start-interval</label> <input required type=number step=1 min=1 max=2147483647 name=startInterval ng-model=configuration.startInterval> <div ng-messages=getTelemetryConfigForm.startInterval.$error> <div translate ng-message=required>tb.rulenode.start-interval-value-required</div> <div ng-message=min translate>tb.rulenode.time-value-range</div> <div ng-message=max translate>tb.rulenode.time-value-range</div> </div> </md-input-container> <md-input-container flex class="md-block tb-time-unit"> <label translate class="tb-title no-padding">tb.rulenode.start-interval-time-unit</label> <md-select required name=startIntervalTimeUnit aria-label="{{ \'tb.rulenode.start-interval-time-unit\' | translate }}" ng-model=configuration.startIntervalTimeUnit> <md-option ng-repeat="timeUnit in ruleNodeTypes.timeUnit" ng-value=timeUnit.value> {{timeUnit.name | translate}} </md-option> </md-select> </md-input-container> </div> <div layout=column layout-gt-sm=row> <md-input-container flex class="md-block tb-time-value"> <label translate class="tb-title no-padding">tb.rulenode.end-interval</label> <input required type=number step=1 min=1 max=2147483647 name=endInterval ng-model=configuration.endInterval> <div ng-messages=getTelemetryConfigForm.endInterval.$error> <div translate ng-message=required>tb.rulenode.end-interval-value-required</div> <div ng-message=min translate>tb.rulenode.time-value-range</div> <div ng-message=max translate>tb.rulenode.time-value-range</div> </div> </md-input-container> <md-input-container flex class="md-block tb-time-unit"> <label translate class="tb-title no-padding">tb.rulenode.end-interval-time-unit</label> <md-select required name=endIntervalTimeUnit aria-label="{{ \'tb.rulenode.end-interval-time-unit\' | translate }}" ng-model=configuration.endIntervalTimeUnit> <md-option ng-repeat="timeUnit in ruleNodeTypes.timeUnit" ng-value=timeUnit.value> {{timeUnit.name | translate}} </md-option> </md-select> </md-input-container> </div> </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> "},23,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:'...'}}" }\'>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> </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(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.$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(8),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(9),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(56),i=a(r),o=n(40),l=a(o),s=n(43),u=a(s),d=n(42),c=a(d),m=n(41),g=a(m),p=n(46),f=a(p),b=n(51),v=a(b),y=n(52),q=a(y),h=n(50),$=a(h),T=n(45),k=a(T),w=n(54),x=a(w),C=n(55),M=a(C),S=n(49),_=a(S),N=n(47),V=a(N),E=n(53),P=a(E),j=n(48),F=a(j);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",k.default).directive("tbActionNodeSnsConfig",x.default).directive("tbActionNodeSqsConfig",M.default).directive("tbActionNodeRabbitMqConfig",_.default).directive("tbActionNodeMqttConfig",V.default).directive("tbActionNodeSendEmailConfig",P.default).directive("tbActionNodeMsgDelayConfig",F.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(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.$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(11),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(12),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(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.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(14),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(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.$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(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){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(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.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(20),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(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),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(22),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(23),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); | ||
3 | +}),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(24),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);var u=186;a.separatorKeys=[t.KEY_CODE.ENTER,t.KEY_CODE.COMMA,u],a.ruleNodeTypes=n,a.aggPeriodTimeUnits={},a.aggPeriodTimeUnits.MINUTES=n.timeUnit.MINUTES,a.aggPeriodTimeUnits.HOURS=n.timeUnit.HOURS,a.aggPeriodTimeUnits.DAYS=n.timeUnit.DAYS,a.aggPeriodTimeUnits.MILLISECONDS=n.timeUnit.MILLISECONDS,a.aggPeriodTimeUnits.SECONDS=n.timeUnit.SECONDS,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:{},link:a}}r.$inject=["$compile","$mdConstant","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(25),o=a(i);n(3)},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(63),i=a(r),o=n(64),l=a(o),s=n(60),u=a(s),d=n(65),c=a(d),m=n(59),g=a(m),p=n(66),f=a(p),b=n(61),v=a(b);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).directive("tbEnrichmentNodeGetTelemetryFromDatabase",v.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(26),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(27),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(28),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(29),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(30),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(71),i=a(r),o=n(69),l=a(o),s=n(72),u=a(s),d=n(67),c=a(d),m=n(70),g=a(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 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){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(){a.messageTypesWatch&&(a.messageTypesWatch(),a.messageTypesWatch=null);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.messageTypesWatch=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","types"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r,n(4);var i=n(31),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.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(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,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(33),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(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){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(35),o=a(i);n(5)},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(36),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(37),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(77),l=a(o),s=n(78),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(38),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(39),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(82),i=a(r),o=n(68),l=a(o),s=n(62),u=a(s),d=n(76),c=a(d),m=n(44),g=a(m),p=n(58),f=a(p),b=n(74),v=a(b),y=n(57),q=a(y),h=n(73),$=a(h),T=n(81),k=a(T);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(k.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{"start-interval":"Start Interval","end-interval":"End Interval","start-interval-time-unit":"Start Interval Time Unit","end-interval-time-unit":"End Interval Time Unit","fetch-mode":"Fetch mode","time-unit-milliseconds":"Milliseconds","time-unit-seconds":"Seconds","time-unit-minutes":"Minutes","time-unit-hours":"Hours","time-unit-days":"Days","time-value-range":"Time value should be in a range from 1 to 2147483647'.","start-interval-value-required":"Start interval value is required.","end-interval-value-required":"End interval value is required.",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 a(e){return e&&e.__esModule?e:{default:e}}function r(e){(0,o.default)(e)}r.$inject=["$translateProvider"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=r;var i=n(80),o=a(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"}},fetchModeType:["FIRST","LAST","ALL"],httpRequestType:["GET","POST","PUT","DELETE"],sqsQueueType:{STANDARD:{name:"tb.rulenode.sqs-queue-standard",value:"STANDARD"},FIFO:{name:"tb.rulenode.sqs-queue-fifo",value:"FIFO"}},timeUnit:{MILLISECONDS:{value:"MILLISECONDS",name:"tb.rulenode.time-unit-milliseconds"},SECONDS:{value:"SECONDS",name:"tb.rulenode.time-unit-seconds"},MINUTES:{value:"MINUTES",name:"tb.rulenode.time-unit-minutes"},HOURS:{value:"HOURS",name:"tb.rulenode.time-unit-hours"},DAYS:{value:"DAYS",name:"tb.rulenode.time-unit-days"}},mqttCredentialTypes:{anonymous:{value:"anonymous",name:"tb.rulenode.credentials-anonymous"},basic:{value:"basic",name:"tb.rulenode.credentials-basic"},"cert.PEM":{value:"cert.PEM",name:"tb.rulenode.credentials-pem"}}}).name}])); | ||
4 | //# sourceMappingURL=rulenode-core-config.js.map | 4 | //# sourceMappingURL=rulenode-core-config.js.map |
ui/.stylelintrc
0 → 100644
1 | +{ | ||
2 | + "extends": ["stylelint-config-standard", "stylelint-config-recommended-scss"], | ||
3 | + "plugins": [ | ||
4 | + "stylelint-order" | ||
5 | + ], | ||
6 | + "rules": { | ||
7 | + "at-rule-empty-line-before": ["always", { | ||
8 | + "except": ["first-nested"], | ||
9 | + "ignore": ["after-comment"] | ||
10 | + }], | ||
11 | + "at-rule-name-space-after": "always", | ||
12 | + "at-rule-no-vendor-prefix": true, | ||
13 | + "at-rule-semicolon-space-before": "never", | ||
14 | + "block-closing-brace-empty-line-before": "never", | ||
15 | + "block-closing-brace-newline-after": null, | ||
16 | + "block-opening-brace-space-before": null, | ||
17 | + "color-named": "never", | ||
18 | + "declaration-block-semicolon-newline-after": "always-multi-line", | ||
19 | + "declaration-block-semicolon-newline-before": "never-multi-line", | ||
20 | + "declaration-block-semicolon-space-after": "always-single-line", | ||
21 | + "declaration-empty-line-before": null, | ||
22 | + "declaration-no-important": null, | ||
23 | + "font-family-name-quotes": "always-where-recommended", | ||
24 | + "font-weight-notation": [ | ||
25 | + "numeric", { | ||
26 | + "ignore": ["relative"] | ||
27 | + }], | ||
28 | + "function-url-no-scheme-relative": true, | ||
29 | + "function-url-quotes": "always", | ||
30 | + "length-zero-no-unit": true, | ||
31 | + "max-empty-lines": 2, | ||
32 | + "max-line-length": null, | ||
33 | + "media-feature-name-no-vendor-prefix": true, | ||
34 | + "media-feature-parentheses-space-inside": "never", | ||
35 | + "media-feature-range-operator-space-after": "always", | ||
36 | + "media-feature-range-operator-space-before": "never", | ||
37 | + "no-descending-specificity": null, | ||
38 | + "no-duplicate-selectors": true, | ||
39 | + "number-leading-zero": "never", | ||
40 | + "media-feature-name-no-unknown": [true, { | ||
41 | + "ignoreMediaFeatureNames": ["prefers-reduced-motion"] | ||
42 | + }], | ||
43 | + "order/properties-order": [ | ||
44 | + "position", | ||
45 | + "top", | ||
46 | + "right", | ||
47 | + "bottom", | ||
48 | + "left", | ||
49 | + "z-index", | ||
50 | + "box-sizing", | ||
51 | + "display", | ||
52 | + "flex", | ||
53 | + "flex-align", | ||
54 | + "flex-basis", | ||
55 | + "flex-direction", | ||
56 | + "flex-wrap", | ||
57 | + "flex-flow", | ||
58 | + "flex-shrink", | ||
59 | + "flex-grow", | ||
60 | + "flex-order", | ||
61 | + "flex-pack", | ||
62 | + "align-content", | ||
63 | + "align-items", | ||
64 | + "align-self", | ||
65 | + "justify-content", | ||
66 | + "order", | ||
67 | + "float", | ||
68 | + "width", | ||
69 | + "min-width", | ||
70 | + "max-width", | ||
71 | + "height", | ||
72 | + "min-height", | ||
73 | + "max-height", | ||
74 | + "padding", | ||
75 | + "padding-top", | ||
76 | + "padding-right", | ||
77 | + "padding-bottom", | ||
78 | + "padding-left", | ||
79 | + "margin", | ||
80 | + "margin-top", | ||
81 | + "margin-right", | ||
82 | + "margin-bottom", | ||
83 | + "margin-left", | ||
84 | + "overflow", | ||
85 | + "overflow-x", | ||
86 | + "overflow-y", | ||
87 | + "-webkit-overflow-scrolling", | ||
88 | + "-ms-overflow-x", | ||
89 | + "-ms-overflow-y", | ||
90 | + "-ms-overflow-style", | ||
91 | + "columns", | ||
92 | + "column-count", | ||
93 | + "column-fill", | ||
94 | + "column-gap", | ||
95 | + "column-rule", | ||
96 | + "column-rule-width", | ||
97 | + "column-rule-style", | ||
98 | + "column-rule-color", | ||
99 | + "column-span", | ||
100 | + "column-width", | ||
101 | + "orphans", | ||
102 | + "widows", | ||
103 | + "clip", | ||
104 | + "clear", | ||
105 | + "font", | ||
106 | + "font-family", | ||
107 | + "font-size", | ||
108 | + "font-style", | ||
109 | + "font-weight", | ||
110 | + "font-variant", | ||
111 | + "font-size-adjust", | ||
112 | + "font-stretch", | ||
113 | + "font-effect", | ||
114 | + "font-emphasize", | ||
115 | + "font-emphasize-position", | ||
116 | + "font-emphasize-style", | ||
117 | + "font-smooth", | ||
118 | + "src", | ||
119 | + "hyphens", | ||
120 | + "line-height", | ||
121 | + "color", | ||
122 | + "text-align", | ||
123 | + "text-align-last", | ||
124 | + "text-emphasis", | ||
125 | + "text-emphasis-color", | ||
126 | + "text-emphasis-style", | ||
127 | + "text-emphasis-position", | ||
128 | + "text-decoration", | ||
129 | + "text-indent", | ||
130 | + "text-justify", | ||
131 | + "text-outline", | ||
132 | + "-ms-text-overflow", | ||
133 | + "text-overflow", | ||
134 | + "text-overflow-ellipsis", | ||
135 | + "text-overflow-mode", | ||
136 | + "text-shadow", | ||
137 | + "text-transform", | ||
138 | + "text-wrap", | ||
139 | + "-webkit-text-size-adjust", | ||
140 | + "-ms-text-size-adjust", | ||
141 | + "letter-spacing", | ||
142 | + "-ms-word-break", | ||
143 | + "word-break", | ||
144 | + "word-spacing", | ||
145 | + "-ms-word-wrap", | ||
146 | + "word-wrap", | ||
147 | + "overflow-wrap", | ||
148 | + "tab-size", | ||
149 | + "white-space", | ||
150 | + "vertical-align", | ||
151 | + "direction", | ||
152 | + "unicode-bidi", | ||
153 | + "list-style", | ||
154 | + "list-style-position", | ||
155 | + "list-style-type", | ||
156 | + "list-style-image", | ||
157 | + "pointer-events", | ||
158 | + "-ms-touch-action", | ||
159 | + "touch-action", | ||
160 | + "cursor", | ||
161 | + "visibility", | ||
162 | + "zoom", | ||
163 | + "table-layout", | ||
164 | + "empty-cells", | ||
165 | + "caption-side", | ||
166 | + "border-spacing", | ||
167 | + "border-collapse", | ||
168 | + "content", | ||
169 | + "quotes", | ||
170 | + "counter-reset", | ||
171 | + "counter-increment", | ||
172 | + "resize", | ||
173 | + "user-select", | ||
174 | + "nav-index", | ||
175 | + "nav-up", | ||
176 | + "nav-right", | ||
177 | + "nav-down", | ||
178 | + "nav-left", | ||
179 | + "background", | ||
180 | + "background-color", | ||
181 | + "background-image", | ||
182 | + "filter", | ||
183 | + "background-repeat", | ||
184 | + "background-attachment", | ||
185 | + "background-position", | ||
186 | + "background-position-x", | ||
187 | + "background-position-y", | ||
188 | + "background-clip", | ||
189 | + "background-origin", | ||
190 | + "background-size", | ||
191 | + "border", | ||
192 | + "border-color", | ||
193 | + "border-style", | ||
194 | + "border-width", | ||
195 | + "border-top", | ||
196 | + "border-top-color", | ||
197 | + "border-top-style", | ||
198 | + "border-top-width", | ||
199 | + "border-right", | ||
200 | + "border-right-color", | ||
201 | + "border-right-style", | ||
202 | + "border-right-width", | ||
203 | + "border-bottom", | ||
204 | + "border-bottom-color", | ||
205 | + "border-bottom-style", | ||
206 | + "border-bottom-width", | ||
207 | + "border-left", | ||
208 | + "border-left-color", | ||
209 | + "border-left-style", | ||
210 | + "border-left-width", | ||
211 | + "border-radius", | ||
212 | + "border-top-left-radius", | ||
213 | + "border-top-right-radius", | ||
214 | + "border-bottom-right-radius", | ||
215 | + "border-bottom-left-radius", | ||
216 | + "border-image", | ||
217 | + "border-image-source", | ||
218 | + "border-image-slice", | ||
219 | + "border-image-width", | ||
220 | + "border-image-outset", | ||
221 | + "border-image-repeat", | ||
222 | + "outline", | ||
223 | + "outline-width", | ||
224 | + "outline-style", | ||
225 | + "outline-color", | ||
226 | + "outline-offset", | ||
227 | + "box-shadow", | ||
228 | + "opacity", | ||
229 | + "-ms-interpolation-mode", | ||
230 | + "page-break-after", | ||
231 | + "page-break-before", | ||
232 | + "page-break-inside", | ||
233 | + "transition", | ||
234 | + "transition-delay", | ||
235 | + "transition-timing-function", | ||
236 | + "transition-duration", | ||
237 | + "transition-property", | ||
238 | + "transform", | ||
239 | + "transform-origin", | ||
240 | + "perspective", | ||
241 | + "appearance", | ||
242 | + "animation", | ||
243 | + "animation-name", | ||
244 | + "animation-duration", | ||
245 | + "animation-play-state", | ||
246 | + "animation-timing-function", | ||
247 | + "animation-delay", | ||
248 | + "animation-iteration-count", | ||
249 | + "animation-direction", | ||
250 | + "animation-fill-mode", | ||
251 | + "fill", | ||
252 | + "stroke" | ||
253 | + ], | ||
254 | + "property-no-vendor-prefix": null, | ||
255 | + "rule-empty-line-before": ["always", { | ||
256 | + "except": ["first-nested"], | ||
257 | + "ignore": ["after-comment"] | ||
258 | + }], | ||
259 | + "scss/dollar-variable-default": [true, { "ignore": "local" }], | ||
260 | + "selector-attribute-quotes": "always", | ||
261 | + "selector-list-comma-newline-after": "always", | ||
262 | + "selector-list-comma-newline-before": "never-multi-line", | ||
263 | + "selector-list-comma-space-after": "always-single-line", | ||
264 | + "selector-list-comma-space-before": "never-single-line", | ||
265 | + "selector-max-attribute": 2, | ||
266 | + "selector-max-class": 6, | ||
267 | + "selector-max-combinators": 8, | ||
268 | + "selector-max-compound-selectors": 9, | ||
269 | + "selector-max-empty-lines": 1, | ||
270 | + "selector-max-id": 1, | ||
271 | + "selector-max-specificity": null, | ||
272 | + "selector-max-type": 5, | ||
273 | + "selector-max-universal": 1, | ||
274 | + "selector-no-qualifying-type": null, | ||
275 | + "selector-no-vendor-prefix": null, | ||
276 | + "selector-type-no-unknown": [true, { | ||
277 | + "ignoreTypes": [ | ||
278 | + "/^md-/", | ||
279 | + "/^mdp-/", | ||
280 | + "/^ng-/", | ||
281 | + "/^tb-/", | ||
282 | + "/^v-pane/" | ||
283 | + ] | ||
284 | + }], | ||
285 | + "string-quotes": "double", | ||
286 | + "value-keyword-case": "lower", | ||
287 | + "value-list-comma-newline-after": "always-multi-line", | ||
288 | + "value-list-comma-newline-before": "never-multi-line", | ||
289 | + "value-list-comma-space-after": "always-single-line", | ||
290 | + "value-no-vendor-prefix": null | ||
291 | + } | ||
292 | +} |
@@ -100,6 +100,7 @@ | @@ -100,6 +100,7 @@ | ||
100 | "copy-webpack-plugin": "^3.0.1", | 100 | "copy-webpack-plugin": "^3.0.1", |
101 | "cross-env": "^3.2.4", | 101 | "cross-env": "^3.2.4", |
102 | "css-loader": "^0.25.0", | 102 | "css-loader": "^0.25.0", |
103 | + "directory-tree": "^2.1.0", | ||
103 | "eslint": "^3.4.0", | 104 | "eslint": "^3.4.0", |
104 | "eslint-config-angular": "^0.5.0", | 105 | "eslint-config-angular": "^0.5.0", |
105 | "eslint-loader": "^1.5.0", | 106 | "eslint-loader": "^1.5.0", |
@@ -112,6 +113,7 @@ | @@ -112,6 +113,7 @@ | ||
112 | "html-minifier-loader": "^1.3.4", | 113 | "html-minifier-loader": "^1.3.4", |
113 | "html-webpack-plugin": "^2.30.1", | 114 | "html-webpack-plugin": "^2.30.1", |
114 | "img-loader": "^1.3.1", | 115 | "img-loader": "^1.3.1", |
116 | + "jsonminify": "^0.4.1", | ||
115 | "less": "^2.7.1", | 117 | "less": "^2.7.1", |
116 | "less-loader": "^2.2.3", | 118 | "less-loader": "^2.2.3", |
117 | "ng-annotate-loader": "^0.1.1", | 119 | "ng-annotate-loader": "^0.1.1", |
@@ -122,14 +124,18 @@ | @@ -122,14 +124,18 @@ | ||
122 | "react-hot-loader": "^3.0.0-beta.6", | 124 | "react-hot-loader": "^3.0.0-beta.6", |
123 | "sass-loader": "^4.0.2", | 125 | "sass-loader": "^4.0.2", |
124 | "style-loader": "^0.13.1", | 126 | "style-loader": "^0.13.1", |
127 | + "stylelint": "^9.5.0", | ||
128 | + "stylelint-config-recommended-scss": "^3.2.0", | ||
129 | + "stylelint-config-standard": "^18.2.0", | ||
130 | + "stylelint-order": "^1.0.0", | ||
131 | + "stylelint-scss": "^3.3.0", | ||
132 | + "stylelint-webpack-plugin": "^0.10.5", | ||
125 | "url-loader": "^0.5.7", | 133 | "url-loader": "^0.5.7", |
126 | "webpack": "^1.13.2", | 134 | "webpack": "^1.13.2", |
127 | "webpack-dev-middleware": "^1.6.1", | 135 | "webpack-dev-middleware": "^1.6.1", |
128 | "webpack-dev-server": "^1.15.1", | 136 | "webpack-dev-server": "^1.15.1", |
129 | "webpack-hot-middleware": "^2.12.2", | 137 | "webpack-hot-middleware": "^2.12.2", |
130 | - "webpack-material-design-icons": "^0.1.0", | ||
131 | - "directory-tree": "^2.1.0", | ||
132 | - "jsonminify": "^0.4.1" | 138 | + "webpack-material-design-icons": "^0.1.0" |
133 | }, | 139 | }, |
134 | "engine": "node >= 5.9.0", | 140 | "engine": "node >= 5.9.0", |
135 | "nyc": { | 141 | "nyc": { |
@@ -13,14 +13,16 @@ | @@ -13,14 +13,16 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-alarm-details-panel { | 17 | .tb-alarm-details-panel { |
17 | - margin-left: 15px; | ||
18 | - border: 1px solid #C0C0C0; | ||
19 | height: 100%; | 18 | height: 100%; |
19 | + margin-left: 15px; | ||
20 | + border: 1px solid #c0c0c0; | ||
21 | + | ||
20 | #tb-alarm-details { | 22 | #tb-alarm-details { |
21 | - min-width: 600px; | ||
22 | - min-height: 200px; | ||
23 | width: 100%; | 23 | width: 100%; |
24 | + min-width: 600px; | ||
24 | height: 100%; | 25 | height: 100%; |
26 | + min-height: 200px; | ||
25 | } | 27 | } |
26 | } | 28 | } |
@@ -13,26 +13,27 @@ | @@ -13,26 +13,27 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-alarm-container { | 17 | .tb-alarm-container { |
17 | overflow-x: auto; | 18 | overflow-x: auto; |
18 | } | 19 | } |
19 | 20 | ||
20 | md-list.tb-alarm-table { | 21 | md-list.tb-alarm-table { |
21 | - padding: 0px; | ||
22 | min-width: 700px; | 22 | min-width: 700px; |
23 | + padding: 0; | ||
23 | 24 | ||
24 | md-list-item { | 25 | md-list-item { |
25 | - padding: 0px; | 26 | + padding: 0; |
26 | } | 27 | } |
27 | 28 | ||
28 | .tb-row { | 29 | .tb-row { |
29 | height: 48px; | 30 | height: 48px; |
30 | - padding: 0px; | 31 | + padding: 0; |
31 | overflow: hidden; | 32 | overflow: hidden; |
32 | } | 33 | } |
33 | 34 | ||
34 | .tb-row:hover { | 35 | .tb-row:hover { |
35 | - background-color: #EEEEEE; | 36 | + background-color: #eee; |
36 | } | 37 | } |
37 | 38 | ||
38 | .tb-header:hover { | 39 | .tb-header:hover { |
@@ -41,9 +42,9 @@ md-list.tb-alarm-table { | @@ -41,9 +42,9 @@ md-list.tb-alarm-table { | ||
41 | 42 | ||
42 | .tb-header { | 43 | .tb-header { |
43 | .tb-cell { | 44 | .tb-cell { |
44 | - color: rgba(0,0,0,.54); | ||
45 | font-size: 12px; | 45 | font-size: 12px; |
46 | font-weight: 700; | 46 | font-weight: 700; |
47 | + color: rgba(0, 0, 0, .54); | ||
47 | white-space: nowrap; | 48 | white-space: nowrap; |
48 | background: none; | 49 | background: none; |
49 | } | 50 | } |
@@ -52,11 +53,12 @@ md-list.tb-alarm-table { | @@ -52,11 +53,12 @@ md-list.tb-alarm-table { | ||
52 | .tb-cell { | 53 | .tb-cell { |
53 | padding: 0 24px; | 54 | padding: 0 24px; |
54 | margin: auto 0; | 55 | margin: auto 0; |
55 | - color: rgba(0,0,0,.87); | 56 | + overflow: hidden; |
56 | font-size: 13px; | 57 | font-size: 13px; |
57 | - vertical-align: middle; | 58 | + color: rgba(0, 0, 0, .87); |
58 | text-align: left; | 59 | text-align: left; |
59 | - overflow: hidden; | 60 | + vertical-align: middle; |
61 | + | ||
60 | .md-button { | 62 | .md-button { |
61 | padding: 0; | 63 | padding: 0; |
62 | margin: 0; | 64 | margin: 0; |
@@ -66,12 +68,11 @@ md-list.tb-alarm-table { | @@ -66,12 +68,11 @@ md-list.tb-alarm-table { | ||
66 | .tb-cell.tb-number { | 68 | .tb-cell.tb-number { |
67 | text-align: right; | 69 | text-align: right; |
68 | } | 70 | } |
69 | - | ||
70 | } | 71 | } |
71 | 72 | ||
72 | #tb-alarm-content { | 73 | #tb-alarm-content { |
73 | - min-width: 400px; | ||
74 | - min-height: 50px; | ||
75 | width: 100%; | 74 | width: 100%; |
75 | + min-width: 400px; | ||
76 | height: 100%; | 76 | height: 100%; |
77 | + min-height: 50px; | ||
77 | } | 78 | } |
@@ -13,10 +13,12 @@ | @@ -13,10 +13,12 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -#tb-audit-log-action-data, #tb-audit-log-failure-details { | ||
17 | - min-width: 400px; | ||
18 | - min-height: 50px; | 16 | + |
17 | +#tb-audit-log-action-data, | ||
18 | +#tb-audit-log-failure-details { | ||
19 | width: 100%; | 19 | width: 100%; |
20 | + min-width: 400px; | ||
20 | height: 100%; | 21 | height: 100%; |
21 | - border: 1px solid #C0C0C0; | ||
22 | -} | ||
22 | + min-height: 50px; | ||
23 | + border: 1px solid #c0c0c0; | ||
24 | +} |
@@ -13,17 +13,21 @@ | @@ -13,17 +13,21 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-audit-logs { | 17 | .tb-audit-logs { |
17 | background-color: #fff; | 18 | background-color: #fff; |
19 | + | ||
18 | .tb-audit-log-margin-18px { | 20 | .tb-audit-log-margin-18px { |
19 | margin-bottom: 18px; | 21 | margin-bottom: 18px; |
20 | } | 22 | } |
23 | + | ||
21 | .tb-audit-log-toolbar { | 24 | .tb-audit-log-toolbar { |
22 | font-size: 20px; | 25 | font-size: 20px; |
23 | } | 26 | } |
27 | + | ||
24 | md-input-container.tb-audit-log-search-input { | 28 | md-input-container.tb-audit-log-search-input { |
25 | .md-errors-spacer { | 29 | .md-errors-spacer { |
26 | - min-height: 0px; | 30 | + min-height: 0; |
27 | } | 31 | } |
28 | } | 32 | } |
29 | } | 33 | } |
@@ -32,27 +36,26 @@ | @@ -32,27 +36,26 @@ | ||
32 | overflow-x: auto; | 36 | overflow-x: auto; |
33 | } | 37 | } |
34 | 38 | ||
35 | - | ||
36 | - | ||
37 | md-list.tb-audit-log-table { | 39 | md-list.tb-audit-log-table { |
38 | - padding: 0px; | ||
39 | min-width: 700px; | 40 | min-width: 700px; |
41 | + padding: 0; | ||
42 | + | ||
40 | &.tb-audit-log-table-full { | 43 | &.tb-audit-log-table-full { |
41 | min-width: 900px; | 44 | min-width: 900px; |
42 | } | 45 | } |
43 | 46 | ||
44 | md-list-item { | 47 | md-list-item { |
45 | - padding: 0px; | 48 | + padding: 0; |
46 | } | 49 | } |
47 | 50 | ||
48 | .tb-row { | 51 | .tb-row { |
49 | height: 48px; | 52 | height: 48px; |
50 | - padding: 0px; | 53 | + padding: 0; |
51 | overflow: hidden; | 54 | overflow: hidden; |
52 | } | 55 | } |
53 | 56 | ||
54 | .tb-row:hover { | 57 | .tb-row:hover { |
55 | - background-color: #EEEEEE; | 58 | + background-color: #eee; |
56 | } | 59 | } |
57 | 60 | ||
58 | .tb-header:hover { | 61 | .tb-header:hover { |
@@ -61,9 +64,9 @@ md-list.tb-audit-log-table { | @@ -61,9 +64,9 @@ md-list.tb-audit-log-table { | ||
61 | 64 | ||
62 | .tb-header { | 65 | .tb-header { |
63 | .tb-cell { | 66 | .tb-cell { |
64 | - color: rgba(0,0,0,.54); | ||
65 | font-size: 12px; | 67 | font-size: 12px; |
66 | font-weight: 700; | 68 | font-weight: 700; |
69 | + color: rgba(0, 0, 0, .54); | ||
67 | white-space: nowrap; | 70 | white-space: nowrap; |
68 | background: none; | 71 | background: none; |
69 | } | 72 | } |
@@ -72,11 +75,12 @@ md-list.tb-audit-log-table { | @@ -72,11 +75,12 @@ md-list.tb-audit-log-table { | ||
72 | .tb-cell { | 75 | .tb-cell { |
73 | padding: 0 24px; | 76 | padding: 0 24px; |
74 | margin: auto 0; | 77 | margin: auto 0; |
75 | - color: rgba(0,0,0,.87); | 78 | + overflow: hidden; |
76 | font-size: 13px; | 79 | font-size: 13px; |
77 | - vertical-align: middle; | 80 | + color: rgba(0, 0, 0, .87); |
78 | text-align: left; | 81 | text-align: left; |
79 | - overflow: hidden; | 82 | + vertical-align: middle; |
83 | + | ||
80 | .md-button { | 84 | .md-button { |
81 | padding: 0; | 85 | padding: 0; |
82 | margin: 0; | 86 | margin: 0; |
@@ -86,5 +90,4 @@ md-list.tb-audit-log-table { | @@ -86,5 +90,4 @@ md-list.tb-audit-log-table { | ||
86 | .tb-cell.tb-number { | 90 | .tb-cell.tb-number { |
87 | text-align: right; | 91 | text-align: right; |
88 | } | 92 | } |
89 | - | ||
90 | } | 93 | } |
@@ -13,16 +13,19 @@ | @@ -13,16 +13,19 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-dashboard-autocomplete { | 17 | .tb-dashboard-autocomplete { |
17 | .tb-not-found { | 18 | .tb-not-found { |
18 | display: block; | 19 | display: block; |
19 | - line-height: 1.5; | ||
20 | height: 48px; | 20 | height: 48px; |
21 | + line-height: 1.5; | ||
21 | } | 22 | } |
23 | + | ||
22 | .tb-dashboard-item { | 24 | .tb-dashboard-item { |
23 | display: block; | 25 | display: block; |
24 | height: 48px; | 26 | height: 48px; |
25 | } | 27 | } |
28 | + | ||
26 | li { | 29 | li { |
27 | height: auto !important; | 30 | height: auto !important; |
28 | white-space: normal !important; | 31 | white-space: normal !important; |
@@ -13,18 +13,20 @@ | @@ -13,18 +13,20 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -@import '../../scss/constants'; | 16 | +@import "../../scss/constants"; |
17 | 17 | ||
18 | tb-dashboard-select { | 18 | tb-dashboard-select { |
19 | min-width: 52px; | 19 | min-width: 52px; |
20 | + | ||
20 | md-select { | 21 | md-select { |
21 | - pointer-events: all; | ||
22 | max-width: 300px; | 22 | max-width: 300px; |
23 | + pointer-events: all; | ||
23 | } | 24 | } |
24 | } | 25 | } |
25 | 26 | ||
26 | .tb-dashboard-select { | 27 | .tb-dashboard-select { |
27 | min-height: 32px; | 28 | min-height: 32px; |
29 | + | ||
28 | span { | 30 | span { |
29 | pointer-events: all; | 31 | pointer-events: all; |
30 | cursor: pointer; | 32 | cursor: pointer; |
@@ -38,23 +40,27 @@ tb-dashboard-select { | @@ -38,23 +40,27 @@ tb-dashboard-select { | ||
38 | } | 40 | } |
39 | 41 | ||
40 | .tb-dashboard-select-panel { | 42 | .tb-dashboard-select-panel { |
43 | + min-width: 300px; | ||
44 | + max-width: 320px; | ||
41 | max-height: 150px; | 45 | max-height: 150px; |
46 | + overflow-x: hidden; | ||
47 | + overflow-y: auto; | ||
48 | + background: #fff; | ||
49 | + border-radius: 4px; | ||
50 | + box-shadow: | ||
51 | + 0 7px 8px -4px rgba(0, 0, 0, .2), | ||
52 | + 0 13px 19px 2px rgba(0, 0, 0, .14), | ||
53 | + 0 5px 24px 4px rgba(0, 0, 0, .12); | ||
54 | + | ||
42 | @media (min-height: 350px) { | 55 | @media (min-height: 350px) { |
43 | max-height: 250px; | 56 | max-height: 250px; |
44 | } | 57 | } |
45 | - max-width: 320px; | 58 | + |
46 | @media (min-width: $layout-breakpoint-xs) { | 59 | @media (min-width: $layout-breakpoint-xs) { |
47 | max-width: 100%; | 60 | max-width: 100%; |
48 | } | 61 | } |
49 | - min-width: 300px; | ||
50 | - background: white; | ||
51 | - border-radius: 4px; | ||
52 | - box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2), | ||
53 | - 0 13px 19px 2px rgba(0, 0, 0, 0.14), | ||
54 | - 0 5px 24px 4px rgba(0, 0, 0, 0.12); | ||
55 | - overflow-x: hidden; | ||
56 | - overflow-y: auto; | 62 | + |
57 | md-content { | 63 | md-content { |
58 | background-color: #fff; | 64 | background-color: #fff; |
59 | } | 65 | } |
60 | -} | ||
66 | +} |
@@ -21,6 +21,7 @@ div.tb-widget { | @@ -21,6 +21,7 @@ div.tb-widget { | ||
21 | margin: 0; | 21 | margin: 0; |
22 | overflow: hidden; | 22 | overflow: hidden; |
23 | outline: none; | 23 | outline: none; |
24 | + | ||
24 | @include transition(all .2s ease-in-out); | 25 | @include transition(all .2s ease-in-out); |
25 | 26 | ||
26 | .tb-widget-title { | 27 | .tb-widget-title { |
@@ -32,7 +33,7 @@ div.tb-widget { | @@ -32,7 +33,7 @@ div.tb-widget { | ||
32 | 33 | ||
33 | tb-timewindow { | 34 | tb-timewindow { |
34 | font-size: 14px; | 35 | font-size: 14px; |
35 | - opacity: 0.85; | 36 | + opacity: .85; |
36 | } | 37 | } |
37 | } | 38 | } |
38 | 39 | ||
@@ -44,17 +45,19 @@ div.tb-widget { | @@ -44,17 +45,19 @@ div.tb-widget { | ||
44 | margin: 0; | 45 | margin: 0; |
45 | 46 | ||
46 | .md-button.md-icon-button { | 47 | .md-button.md-icon-button { |
47 | - margin: 0 !important; | ||
48 | - padding: 0 !important; | ||
49 | - line-height: 20px; | ||
50 | width: 32px; | 48 | width: 32px; |
51 | - height: 32px; | ||
52 | min-width: 32px; | 49 | min-width: 32px; |
50 | + height: 32px; | ||
53 | min-height: 32px; | 51 | min-height: 32px; |
54 | - md-icon, ng-md-icon { | 52 | + padding: 0 !important; |
53 | + margin: 0 !important; | ||
54 | + line-height: 20px; | ||
55 | + | ||
56 | + md-icon, | ||
57 | + ng-md-icon { | ||
55 | width: 20px; | 58 | width: 20px; |
56 | - height: 20px; | ||
57 | min-width: 20px; | 59 | min-width: 20px; |
60 | + height: 20px; | ||
58 | min-height: 20px; | 61 | min-height: 20px; |
59 | font-size: 20px; | 62 | font-size: 20px; |
60 | } | 63 | } |
@@ -63,8 +66,8 @@ div.tb-widget { | @@ -63,8 +66,8 @@ div.tb-widget { | ||
63 | 66 | ||
64 | .tb-widget-content { | 67 | .tb-widget-content { |
65 | tb-widget { | 68 | tb-widget { |
66 | - width: 100%; | ||
67 | position: relative; | 69 | position: relative; |
70 | + width: 100%; | ||
68 | } | 71 | } |
69 | } | 72 | } |
70 | } | 73 | } |
@@ -75,40 +78,41 @@ div.tb-widget.tb-highlighted { | @@ -75,40 +78,41 @@ div.tb-widget.tb-highlighted { | ||
75 | } | 78 | } |
76 | 79 | ||
77 | div.tb-widget.tb-not-highlighted { | 80 | div.tb-widget.tb-not-highlighted { |
78 | - opacity: 0.5; | 81 | + opacity: .5; |
79 | } | 82 | } |
80 | 83 | ||
81 | tb-dashboard { | 84 | tb-dashboard { |
82 | position: absolute; | 85 | position: absolute; |
83 | top: 0; | 86 | top: 0; |
84 | - left: 0; | ||
85 | right: 0; | 87 | right: 0; |
86 | bottom: 0; | 88 | bottom: 0; |
89 | + left: 0; | ||
87 | } | 90 | } |
88 | 91 | ||
89 | md-content.tb-dashboard-content { | 92 | md-content.tb-dashboard-content { |
90 | position: absolute; | 93 | position: absolute; |
91 | top: 0; | 94 | top: 0; |
92 | - left: 0; | ||
93 | right: 0; | 95 | right: 0; |
94 | bottom: 0; | 96 | bottom: 0; |
95 | - outline: none; | 97 | + left: 0; |
96 | background: none; | 98 | background: none; |
99 | + outline: none; | ||
100 | + | ||
97 | .gridster-item { | 101 | .gridster-item { |
98 | - @include transition(none); | 102 | + @include transition(none); |
99 | } | 103 | } |
100 | } | 104 | } |
101 | 105 | ||
102 | .tb-widget-error-container { | 106 | .tb-widget-error-container { |
103 | position: absolute; | 107 | position: absolute; |
104 | - background-color: #fff; | ||
105 | width: 100%; | 108 | width: 100%; |
106 | height: 100%; | 109 | height: 100%; |
110 | + background-color: #fff; | ||
107 | } | 111 | } |
108 | 112 | ||
109 | .tb-widget-error-msg { | 113 | .tb-widget-error-msg { |
110 | - color: red; | 114 | + padding: 5px; |
111 | font-size: 16px; | 115 | font-size: 16px; |
116 | + color: #f00; | ||
112 | word-wrap: break-word; | 117 | word-wrap: break-word; |
113 | - padding: 5px; | ||
114 | } | 118 | } |
@@ -13,9 +13,11 @@ | @@ -13,9 +13,11 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-datakey-config { | 17 | .tb-datakey-config { |
17 | min-width: 500px !important; | 18 | min-width: 500px !important; |
18 | min-height: 500px !important; | 19 | min-height: 500px !important; |
20 | + | ||
19 | md-content { | 21 | md-content { |
20 | background-color: #fff; | 22 | background-color: #fff; |
21 | } | 23 | } |
@@ -13,17 +13,22 @@ | @@ -13,17 +13,22 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -@import '../../scss/constants'; | 16 | +@import "../../scss/constants"; |
17 | 17 | ||
18 | -.tb-entity-alias-autocomplete, .tb-timeseries-datakey-autocomplete, .tb-attribute-datakey-autocomplete, .tb-alarm-datakey-autocomplete { | 18 | +.tb-entity-alias-autocomplete, |
19 | +.tb-timeseries-datakey-autocomplete, | ||
20 | +.tb-attribute-datakey-autocomplete, | ||
21 | +.tb-alarm-datakey-autocomplete { | ||
19 | .tb-not-found { | 22 | .tb-not-found { |
20 | display: block; | 23 | display: block; |
21 | - line-height: 1.5; | ||
22 | height: 48px; | 24 | height: 48px; |
25 | + line-height: 1.5; | ||
26 | + | ||
23 | .tb-no-entries { | 27 | .tb-no-entries { |
24 | line-height: 48px; | 28 | line-height: 48px; |
25 | } | 29 | } |
26 | } | 30 | } |
31 | + | ||
27 | li { | 32 | li { |
28 | height: auto !important; | 33 | height: auto !important; |
29 | white-space: normal !important; | 34 | white-space: normal !important; |
@@ -32,13 +37,14 @@ | @@ -32,13 +37,14 @@ | ||
32 | 37 | ||
33 | tb-datasource-entity { | 38 | tb-datasource-entity { |
34 | @media (min-width: $layout-breakpoint-sm) { | 39 | @media (min-width: $layout-breakpoint-sm) { |
35 | - padding-left: 4px; | ||
36 | padding-right: 4px; | 40 | padding-right: 4px; |
41 | + padding-left: 4px; | ||
37 | } | 42 | } |
43 | + | ||
38 | tb-entity-alias-select { | 44 | tb-entity-alias-select { |
39 | @media (min-width: $layout-breakpoint-sm) { | 45 | @media (min-width: $layout-breakpoint-sm) { |
40 | width: 200px; | 46 | width: 200px; |
41 | max-width: 200px; | 47 | max-width: 200px; |
42 | } | 48 | } |
43 | } | 49 | } |
44 | -} | ||
50 | +} |
@@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -@import '../../scss/constants'; | 16 | +@import "../../scss/constants"; |
17 | 17 | ||
18 | .tb-datasource-func { | 18 | .tb-datasource-func { |
19 | @media (min-width: $layout-breakpoint-sm) { | 19 | @media (min-width: $layout-breakpoint-sm) { |
@@ -22,19 +22,22 @@ | @@ -22,19 +22,22 @@ | ||
22 | 22 | ||
23 | md-input-container.tb-datasource-name { | 23 | md-input-container.tb-datasource-name { |
24 | .md-errors-spacer { | 24 | .md-errors-spacer { |
25 | - display: none; | 25 | + display: none; |
26 | } | 26 | } |
27 | } | 27 | } |
28 | 28 | ||
29 | - .tb-func-datakey-autocomplete, .tb-alarm-datakey-autocomplete { | 29 | + .tb-func-datakey-autocomplete, |
30 | + .tb-alarm-datakey-autocomplete { | ||
30 | .tb-not-found { | 31 | .tb-not-found { |
31 | display: block; | 32 | display: block; |
32 | - line-height: 1.5; | ||
33 | height: 48px; | 33 | height: 48px; |
34 | + line-height: 1.5; | ||
35 | + | ||
34 | .tb-no-entries { | 36 | .tb-no-entries { |
35 | line-height: 48px; | 37 | line-height: 48px; |
36 | } | 38 | } |
37 | } | 39 | } |
40 | + | ||
38 | li { | 41 | li { |
39 | height: auto !important; | 42 | height: auto !important; |
40 | white-space: normal !important; | 43 | white-space: normal !important; |
@@ -13,39 +13,43 @@ | @@ -13,39 +13,43 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-datasource { | 17 | .tb-datasource { |
17 | - #entity-autocomplete { | ||
18 | - height: 30px; | ||
19 | - margin-top: 18px; | ||
20 | - md-autocomplete-wrap { | ||
21 | - height: 30px; | ||
22 | - } | ||
23 | - input, input:not(.md-input) { | ||
24 | - height: 30px; | ||
25 | - } | ||
26 | - } | ||
27 | - #datasourceType { | 18 | + #entity-autocomplete { |
19 | + height: 30px; | ||
20 | + margin-top: 18px; | ||
21 | + | ||
22 | + md-autocomplete-wrap { | ||
23 | + height: 30px; | ||
24 | + } | ||
25 | + | ||
26 | + input, | ||
27 | + input:not(.md-input) { | ||
28 | + height: 30px; | ||
29 | + } | ||
28 | } | 30 | } |
29 | } | 31 | } |
30 | 32 | ||
31 | @mixin tb-checkered-bg() { | 33 | @mixin tb-checkered-bg() { |
32 | background-color: #fff; | 34 | background-color: #fff; |
33 | - background-image: linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd), | ||
34 | - linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd); | ||
35 | - background-size: 8px 8px; | 35 | + background-image: |
36 | + linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd), | ||
37 | + linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd); | ||
36 | background-position: 0 0, 4px 4px; | 38 | background-position: 0 0, 4px 4px; |
39 | + background-size: 8px 8px; | ||
37 | } | 40 | } |
38 | 41 | ||
39 | .tb-color-preview { | 42 | .tb-color-preview { |
40 | - content: ''; | ||
41 | - min-width: 24px; | 43 | + position: relative; |
42 | width: 24px; | 44 | width: 24px; |
45 | + min-width: 24px; | ||
43 | height: 24px; | 46 | height: 24px; |
47 | + overflow: hidden; | ||
48 | + content: ""; | ||
44 | border: 2px solid #fff; | 49 | border: 2px solid #fff; |
45 | border-radius: 50%; | 50 | border-radius: 50%; |
46 | box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .14), 0 2px 2px 0 rgba(0, 0, 0, .098), 0 1px 5px 0 rgba(0, 0, 0, .084); | 51 | box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .14), 0 2px 2px 0 rgba(0, 0, 0, .098), 0 1px 5px 0 rgba(0, 0, 0, .084); |
47 | - position: relative; | ||
48 | - overflow: hidden; | 52 | + |
49 | @include tb-checkered-bg(); | 53 | @include tb-checkered-bg(); |
50 | 54 | ||
51 | .tb-color-result { | 55 | .tb-color-result { |
@@ -60,6 +64,7 @@ | @@ -60,6 +64,7 @@ | ||
60 | text-overflow: ellipsis; | 64 | text-overflow: ellipsis; |
61 | white-space: nowrap; | 65 | white-space: nowrap; |
62 | } | 66 | } |
67 | + | ||
63 | .tb-chip-separator { | 68 | .tb-chip-separator { |
64 | white-space: pre; | 69 | white-space: pre; |
65 | } | 70 | } |
@@ -13,13 +13,16 @@ | @@ -13,13 +13,16 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | tb-datetime-period { | 17 | tb-datetime-period { |
17 | md-input-container { | 18 | md-input-container { |
18 | - margin-bottom: 0px; | 19 | + margin-bottom: 0; |
20 | + | ||
19 | .md-errors-spacer { | 21 | .md-errors-spacer { |
20 | - min-height: 0px; | 22 | + min-height: 0; |
21 | } | 23 | } |
22 | } | 24 | } |
25 | + | ||
23 | mdp-date-picker { | 26 | mdp-date-picker { |
24 | .md-input { | 27 | .md-input { |
25 | width: 150px !important; | 28 | width: 150px !important; |
@@ -13,47 +13,51 @@ | @@ -13,47 +13,51 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -@import '../../scss/constants'; | 16 | +@import "../../scss/constants"; |
17 | 17 | ||
18 | .tb-details-title { | 18 | .tb-details-title { |
19 | - font-size: 1.000rem; | ||
20 | - @media (min-width: $layout-breakpoint-gt-sm) { | ||
21 | - font-size: 1.600rem; | ||
22 | - } | ||
23 | - font-weight: 400; | ||
24 | - text-transform: uppercase; | 19 | + width: inherit; |
25 | margin: 20px 8px 0 0; | 20 | margin: 20px 8px 0 0; |
26 | overflow: hidden; | 21 | overflow: hidden; |
22 | + font-size: 1rem; | ||
23 | + font-weight: 400; | ||
27 | text-overflow: ellipsis; | 24 | text-overflow: ellipsis; |
25 | + text-transform: uppercase; | ||
28 | white-space: nowrap; | 26 | white-space: nowrap; |
29 | - width: inherit; | 27 | + |
28 | + @media (min-width: $layout-breakpoint-gt-sm) { | ||
29 | + font-size: 1.6rem; | ||
30 | + } | ||
30 | } | 31 | } |
31 | 32 | ||
32 | .tb-details-subtitle { | 33 | .tb-details-subtitle { |
33 | - font-size: 1.000rem; | 34 | + width: inherit; |
34 | margin: 10px 0; | 35 | margin: 10px 0; |
35 | - opacity: 0.8; | ||
36 | overflow: hidden; | 36 | overflow: hidden; |
37 | + font-size: 1rem; | ||
37 | text-overflow: ellipsis; | 38 | text-overflow: ellipsis; |
38 | white-space: nowrap; | 39 | white-space: nowrap; |
39 | - width: inherit; | 40 | + opacity: .8; |
40 | } | 41 | } |
41 | 42 | ||
42 | md-sidenav.tb-sidenav-details { | 43 | md-sidenav.tb-sidenav-details { |
43 | .md-toolbar-tools { | 44 | .md-toolbar-tools { |
44 | - min-height: 100px; | ||
45 | - max-height: 120px; | ||
46 | - height: 100%; | 45 | + height: 100%; |
46 | + min-height: 100px; | ||
47 | + max-height: 120px; | ||
47 | } | 48 | } |
49 | + z-index: 59 !important; | ||
48 | width: 100% !important; | 50 | width: 100% !important; |
49 | max-width: 100% !important; | 51 | max-width: 100% !important; |
50 | - z-index: 59 !important; | 52 | + |
51 | @media (min-width: $layout-breakpoint-gt-sm) { | 53 | @media (min-width: $layout-breakpoint-gt-sm) { |
52 | width: 80% !important; | 54 | width: 80% !important; |
53 | } | 55 | } |
56 | + | ||
54 | @media (min-width: $layout-breakpoint-gt-md) { | 57 | @media (min-width: $layout-breakpoint-gt-md) { |
55 | width: 65% !important; | 58 | width: 65% !important; |
56 | } | 59 | } |
60 | + | ||
57 | tb-dashboard { | 61 | tb-dashboard { |
58 | md-content { | 62 | md-content { |
59 | background-color: $primary-hue-3; | 63 | background-color: $primary-hue-3; |
@@ -13,15 +13,18 @@ | @@ -13,15 +13,18 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-entity-alias-autocomplete { | 17 | .tb-entity-alias-autocomplete { |
17 | .tb-not-found { | 18 | .tb-not-found { |
18 | display: block; | 19 | display: block; |
19 | - line-height: 1.5; | ||
20 | height: 48px; | 20 | height: 48px; |
21 | + line-height: 1.5; | ||
22 | + | ||
21 | .tb-no-entries { | 23 | .tb-no-entries { |
22 | line-height: 48px; | 24 | line-height: 48px; |
23 | } | 25 | } |
24 | } | 26 | } |
27 | + | ||
25 | li { | 28 | li { |
26 | height: auto !important; | 29 | height: auto !important; |
27 | white-space: normal !important; | 30 | white-space: normal !important; |
@@ -16,37 +16,38 @@ | @@ -16,37 +16,38 @@ | ||
16 | @import "../../scss/constants"; | 16 | @import "../../scss/constants"; |
17 | 17 | ||
18 | .tb-fullscreen { | 18 | .tb-fullscreen { |
19 | - section.header-buttons { | ||
20 | - top: 25px; | ||
21 | - } | ||
22 | -} | ||
23 | - | ||
24 | -.tb-fullscreen { | ||
25 | - width: 100% !important; | ||
26 | - height: 100% !important; | ||
27 | position: fixed !important; | 19 | position: fixed !important; |
28 | top: 0; | 20 | top: 0; |
29 | left: 0; | 21 | left: 0; |
22 | + width: 100% !important; | ||
23 | + height: 100% !important; | ||
24 | + | ||
25 | + section.header-buttons { | ||
26 | + top: 25px; | ||
27 | + } | ||
30 | } | 28 | } |
31 | 29 | ||
32 | .tb-fullscreen-parent { | 30 | .tb-fullscreen-parent { |
33 | - background-color: $gray; | ||
34 | - width: 100%; | ||
35 | - height: 100%; | ||
36 | position: fixed; | 31 | position: fixed; |
37 | top: 0; | 32 | top: 0; |
38 | left: 0; | 33 | left: 0; |
34 | + width: 100%; | ||
35 | + height: 100%; | ||
36 | + background-color: $gray; | ||
39 | } | 37 | } |
40 | 38 | ||
41 | -.md-button.tb-fullscreen-button-style, .tb-fullscreen-button-style { | 39 | +.md-button.tb-fullscreen-button-style, |
40 | +.tb-fullscreen-button-style { | ||
42 | background: #ccc; | 41 | background: #ccc; |
43 | - opacity: 0.85; | 42 | + opacity: .85; |
43 | + | ||
44 | ng-md-icon { | 44 | ng-md-icon { |
45 | color: #666; | 45 | color: #666; |
46 | } | 46 | } |
47 | } | 47 | } |
48 | 48 | ||
49 | -.md-button.tb-fullscreen-button-pos, .tb-fullscreen-button-pos { | 49 | +.md-button.tb-fullscreen-button-pos, |
50 | +.tb-fullscreen-button-pos { | ||
50 | position: absolute; | 51 | position: absolute; |
51 | top: 10px; | 52 | top: 10px; |
52 | right: 10px; | 53 | right: 10px; |
@@ -21,15 +21,19 @@ | @@ -21,15 +21,19 @@ | ||
21 | 21 | ||
22 | .tb-card-item { | 22 | .tb-card-item { |
23 | @include transition(all .2s ease-in-out); | 23 | @include transition(all .2s ease-in-out); |
24 | + | ||
24 | md-card-content { | 25 | md-card-content { |
25 | - padding-top: 0px; | ||
26 | max-height: 53px; | 26 | max-height: 53px; |
27 | + padding-top: 0; | ||
27 | } | 28 | } |
29 | + | ||
28 | md-card-title { | 30 | md-card-title { |
29 | width: 100%; | 31 | width: 100%; |
32 | + | ||
30 | md-card-title-text { | 33 | md-card-title-text { |
31 | - max-height: 32px; | ||
32 | min-width: 50%; | 34 | min-width: 50%; |
35 | + max-height: 32px; | ||
36 | + | ||
33 | .md-headline { | 37 | .md-headline { |
34 | overflow: hidden; | 38 | overflow: hidden; |
35 | text-overflow: ellipsis; | 39 | text-overflow: ellipsis; |
@@ -40,14 +44,15 @@ | @@ -40,14 +44,15 @@ | ||
40 | } | 44 | } |
41 | 45 | ||
42 | .tb-current-item { | 46 | .tb-current-item { |
43 | - opacity: 0.5; | 47 | + opacity: .5; |
48 | + | ||
44 | @include transform(scale(1.05)); | 49 | @include transform(scale(1.05)); |
45 | } | 50 | } |
46 | 51 | ||
47 | #tb-vertical-container { | 52 | #tb-vertical-container { |
48 | position: absolute; | 53 | position: absolute; |
49 | top: 0; | 54 | top: 0; |
50 | - left: 0; | ||
51 | right: 0; | 55 | right: 0; |
52 | bottom: 0; | 56 | bottom: 0; |
57 | + left: 0; | ||
53 | } | 58 | } |
@@ -13,11 +13,14 @@ | @@ -13,11 +13,14 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | tb-js-func { | 17 | tb-js-func { |
17 | position: relative; | 18 | position: relative; |
19 | + | ||
18 | .tb-disabled { | 20 | .tb-disabled { |
19 | - color: rgba(0,0,0,0.38); | 21 | + color: rgba(0, 0, 0, .38); |
20 | } | 22 | } |
23 | + | ||
21 | .fill-height { | 24 | .fill-height { |
22 | height: 100%; | 25 | height: 100%; |
23 | } | 26 | } |
@@ -25,25 +28,27 @@ tb-js-func { | @@ -25,25 +28,27 @@ tb-js-func { | ||
25 | 28 | ||
26 | .tb-js-func-toolbar { | 29 | .tb-js-func-toolbar { |
27 | .md-button.tidy { | 30 | .md-button.tidy { |
28 | - color: #7B7B7B; | ||
29 | min-width: 32px; | 31 | min-width: 32px; |
30 | min-height: 15px; | 32 | min-height: 15px; |
31 | - line-height: 15px; | ||
32 | - font-size: 0.800rem; | ||
33 | - margin: 0 5px 0 0; | ||
34 | padding: 4px; | 33 | padding: 4px; |
35 | - background: rgba(220, 220, 220, 0.35); | 34 | + margin: 0 5px 0 0; |
35 | + font-size: .8rem; | ||
36 | + line-height: 15px; | ||
37 | + color: #7b7b7b; | ||
38 | + background: rgba(220, 220, 220, .35); | ||
36 | } | 39 | } |
37 | } | 40 | } |
38 | 41 | ||
39 | .tb-js-func-panel { | 42 | .tb-js-func-panel { |
40 | - margin-left: 15px; | ||
41 | - border: 1px solid #C0C0C0; | ||
42 | height: calc(100% - 80px); | 43 | height: calc(100% - 80px); |
44 | + margin-left: 15px; | ||
45 | + border: 1px solid #c0c0c0; | ||
46 | + | ||
43 | #tb-javascript-input { | 47 | #tb-javascript-input { |
44 | - min-width: 200px; | ||
45 | width: 100%; | 48 | width: 100%; |
49 | + min-width: 200px; | ||
46 | height: 100%; | 50 | height: 100%; |
51 | + | ||
47 | &:not(.fill-height) { | 52 | &:not(.fill-height) { |
48 | min-height: 200px; | 53 | min-height: 200px; |
49 | } | 54 | } |
@@ -13,8 +13,10 @@ | @@ -13,8 +13,10 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | tb-json-content { | 17 | tb-json-content { |
17 | position: relative; | 18 | position: relative; |
19 | + | ||
18 | .fill-height { | 20 | .fill-height { |
19 | height: 100%; | 21 | height: 100%; |
20 | } | 22 | } |
@@ -22,25 +24,27 @@ tb-json-content { | @@ -22,25 +24,27 @@ tb-json-content { | ||
22 | 24 | ||
23 | .tb-json-content-toolbar { | 25 | .tb-json-content-toolbar { |
24 | .md-button.tidy { | 26 | .md-button.tidy { |
25 | - color: #7B7B7B; | ||
26 | min-width: 32px; | 27 | min-width: 32px; |
27 | min-height: 15px; | 28 | min-height: 15px; |
28 | - line-height: 15px; | ||
29 | - font-size: 0.800rem; | ||
30 | - margin: 0 5px 0 0; | ||
31 | padding: 4px; | 29 | padding: 4px; |
32 | - background: rgba(220, 220, 220, 0.35); | 30 | + margin: 0 5px 0 0; |
31 | + font-size: .8rem; | ||
32 | + line-height: 15px; | ||
33 | + color: #7b7b7b; | ||
34 | + background: rgba(220, 220, 220, .35); | ||
33 | } | 35 | } |
34 | } | 36 | } |
35 | 37 | ||
36 | .tb-json-content-panel { | 38 | .tb-json-content-panel { |
37 | - margin-left: 15px; | ||
38 | - border: 1px solid #C0C0C0; | ||
39 | height: 100%; | 39 | height: 100%; |
40 | + margin-left: 15px; | ||
41 | + border: 1px solid #c0c0c0; | ||
42 | + | ||
40 | #tb-json-input { | 43 | #tb-json-input { |
41 | - min-width: 200px; | ||
42 | width: 100%; | 44 | width: 100%; |
45 | + min-width: 200px; | ||
43 | height: 100%; | 46 | height: 100%; |
47 | + | ||
44 | &:not(.fill-height) { | 48 | &:not(.fill-height) { |
45 | min-height: 200px; | 49 | min-height: 200px; |
46 | } | 50 | } |
@@ -13,7 +13,8 @@ | @@ -13,7 +13,8 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | tb-json-form { | 17 | tb-json-form { |
17 | - overflow: auto; | ||
18 | padding-bottom: 14px !important; | 18 | padding-bottom: 14px !important; |
19 | -} | ||
19 | + overflow: auto; | ||
20 | +} |
@@ -13,21 +13,25 @@ | @@ -13,21 +13,25 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | tb-json-object-edit { | 17 | tb-json-object-edit { |
17 | position: relative; | 18 | position: relative; |
19 | + | ||
18 | .fill-height { | 20 | .fill-height { |
19 | height: 100%; | 21 | height: 100%; |
20 | } | 22 | } |
21 | } | 23 | } |
22 | 24 | ||
23 | .tb-json-object-panel { | 25 | .tb-json-object-panel { |
24 | - margin-left: 15px; | ||
25 | - border: 1px solid #C0C0C0; | ||
26 | height: 100%; | 26 | height: 100%; |
27 | + margin-left: 15px; | ||
28 | + border: 1px solid #c0c0c0; | ||
29 | + | ||
27 | #tb-json-input { | 30 | #tb-json-input { |
28 | - min-width: 200px; | ||
29 | width: 100%; | 31 | width: 100%; |
32 | + min-width: 200px; | ||
30 | height: 100%; | 33 | height: 100%; |
34 | + | ||
31 | &:not(.fill-height) { | 35 | &:not(.fill-height) { |
32 | min-height: 200px; | 36 | min-height: 200px; |
33 | } | 37 | } |
@@ -13,14 +13,16 @@ | @@ -13,14 +13,16 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-kv-map { | 17 | .tb-kv-map { |
17 | span.no-data-found { | 18 | span.no-data-found { |
18 | position: relative; | 19 | position: relative; |
20 | + display: flex; | ||
19 | height: 40px; | 21 | height: 40px; |
20 | text-transform: uppercase; | 22 | text-transform: uppercase; |
21 | - display: flex; | 23 | + |
22 | &.disabled { | 24 | &.disabled { |
23 | - color: rgba(0,0,0,0.38); | 25 | + color: rgba(0, 0, 0, .38); |
24 | } | 26 | } |
25 | } | 27 | } |
26 | -} | ||
28 | +} |
@@ -13,6 +13,7 @@ | @@ -13,6 +13,7 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .md-panel { | 17 | .md-panel { |
17 | &.tb-legend-config-panel { | 18 | &.tb-legend-config-panel { |
18 | position: absolute; | 19 | position: absolute; |
@@ -20,21 +21,26 @@ | @@ -20,21 +21,26 @@ | ||
20 | } | 21 | } |
21 | 22 | ||
22 | .tb-legend-config-panel { | 23 | .tb-legend-config-panel { |
23 | - max-height: 220px; | ||
24 | min-width: 220px; | 24 | min-width: 220px; |
25 | - background: white; | ||
26 | - border-radius: 4px; | ||
27 | - box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2), | ||
28 | - 0 13px 19px 2px rgba(0, 0, 0, 0.14), | ||
29 | - 0 5px 24px 4px rgba(0, 0, 0, 0.12); | 25 | + max-height: 220px; |
30 | overflow: hidden; | 26 | overflow: hidden; |
31 | - form, fieldset { | 27 | + background: #fff; |
28 | + border-radius: 4px; | ||
29 | + box-shadow: | ||
30 | + 0 7px 8px -4px rgba(0, 0, 0, .2), | ||
31 | + 0 13px 19px 2px rgba(0, 0, 0, .14), | ||
32 | + 0 5px 24px 4px rgba(0, 0, 0, .12); | ||
33 | + | ||
34 | + form, | ||
35 | + fieldset { | ||
32 | height: 100%; | 36 | height: 100%; |
33 | } | 37 | } |
38 | + | ||
34 | md-content { | 39 | md-content { |
35 | - background-color: #fff; | ||
36 | overflow: hidden; | 40 | overflow: hidden; |
41 | + background-color: #fff; | ||
37 | } | 42 | } |
43 | + | ||
38 | .md-padding { | 44 | .md-padding { |
39 | padding: 0 16px; | 45 | padding: 0 16px; |
40 | } | 46 | } |
@@ -13,39 +13,49 @@ | @@ -13,39 +13,49 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | table.tb-legend { | 17 | table.tb-legend { |
17 | width: 100%; | 18 | width: 100%; |
18 | font-size: 12px; | 19 | font-size: 12px; |
19 | - .tb-legend-header, .tb-legend-value { | ||
20 | - text-align: right; | 20 | + |
21 | + .tb-legend-header, | ||
22 | + .tb-legend-value { | ||
23 | + text-align: right; | ||
21 | } | 24 | } |
25 | + | ||
22 | .tb-legend-header { | 26 | .tb-legend-header { |
23 | th { | 27 | th { |
24 | - color: rgb(255,110,64); | ||
25 | - white-space: nowrap; | ||
26 | padding: 0 10px 1px 0; | 28 | padding: 0 10px 1px 0; |
29 | + color: rgb(255, 110, 64); | ||
30 | + white-space: nowrap; | ||
27 | } | 31 | } |
28 | } | 32 | } |
33 | + | ||
29 | .tb-legend-keys { | 34 | .tb-legend-keys { |
30 | - td.tb-legend-label, td.tb-legend-value { | ||
31 | - white-space: nowrap; | 35 | + td.tb-legend-label, |
36 | + td.tb-legend-value { | ||
32 | padding: 2px 10px; | 37 | padding: 2px 10px; |
38 | + white-space: nowrap; | ||
33 | } | 39 | } |
40 | + | ||
34 | .tb-legend-line { | 41 | .tb-legend-line { |
42 | + display: inline-block; | ||
35 | width: 15px; | 43 | width: 15px; |
36 | height: 3px; | 44 | height: 3px; |
37 | - display: inline-block; | ||
38 | vertical-align: middle; | 45 | vertical-align: middle; |
39 | } | 46 | } |
47 | + | ||
40 | .tb-legend-label { | 48 | .tb-legend-label { |
41 | text-align: left; | 49 | text-align: left; |
42 | outline: none; | 50 | outline: none; |
51 | + | ||
43 | &.tb-horizontal { | 52 | &.tb-horizontal { |
44 | width: 95%; | 53 | width: 95%; |
45 | } | 54 | } |
55 | + | ||
46 | &.tb-hidden-label { | 56 | &.tb-hidden-label { |
47 | text-decoration: line-through; | 57 | text-decoration: line-through; |
48 | - opacity: 0.6; | 58 | + opacity: .6; |
49 | } | 59 | } |
50 | } | 60 | } |
51 | } | 61 | } |
@@ -13,14 +13,16 @@ | @@ -13,14 +13,16 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-material-icon-select { | 17 | .tb-material-icon-select { |
17 | - md-icon { | ||
18 | - padding: 4px; | ||
19 | - margin: 8px 4px 4px; | ||
20 | - cursor: pointer; | ||
21 | - border: solid 1px rgba(0,0,0,0.27); | ||
22 | - } | ||
23 | - md-input-container { | ||
24 | - margin-bottom: 0px; | ||
25 | - } | ||
26 | -} | ||
18 | + md-icon { | ||
19 | + padding: 4px; | ||
20 | + margin: 8px 4px 4px; | ||
21 | + cursor: pointer; | ||
22 | + border: solid 1px rgba(0, 0, 0, .27); | ||
23 | + } | ||
24 | + | ||
25 | + md-input-container { | ||
26 | + margin-bottom: 0; | ||
27 | + } | ||
28 | +} |
@@ -13,18 +13,20 @@ | @@ -13,18 +13,20 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-material-icons-dialog { | 17 | .tb-material-icons-dialog { |
17 | button.md-icon-button.tb-select-icon-button { | 18 | button.md-icon-button.tb-select-icon-button { |
18 | - border: solid 1px orange; | ||
19 | - border-radius: 0%; | ||
20 | - padding: 16px; | ||
21 | - height: 56px; | ||
22 | width: 56px; | 19 | width: 56px; |
20 | + height: 56px; | ||
21 | + padding: 16px; | ||
23 | margin: 10px; | 22 | margin: 10px; |
23 | + border: solid 1px #ffa500; | ||
24 | + border-radius: 0%; | ||
24 | } | 25 | } |
26 | + | ||
25 | .tb-icons-load { | 27 | .tb-icons-load { |
26 | top: 64px; | 28 | top: 64px; |
27 | - background: rgba(255,255,255,0.75); | ||
28 | z-index: 3; | 29 | z-index: 3; |
30 | + background: rgba(255, 255, 255, .75); | ||
29 | } | 31 | } |
30 | -} | ||
32 | +} |
@@ -24,9 +24,11 @@ | @@ -24,9 +24,11 @@ | ||
24 | } | 24 | } |
25 | 25 | ||
26 | .tb-menu-toggle-list { | 26 | .tb-menu-toggle-list { |
27 | - overflow: hidden; | ||
28 | position: relative; | 27 | position: relative; |
29 | z-index: 1; | 28 | z-index: 1; |
30 | - @include transition(0.75s cubic-bezier(0.35, 0, 0.25, 1)); | 29 | + overflow: hidden; |
30 | + | ||
31 | + @include transition(.75s cubic-bezier(.35, 0, .25, 1)); | ||
32 | + | ||
31 | @include transition-property(height); | 33 | @include transition-property(height); |
32 | } | 34 | } |
@@ -13,30 +13,35 @@ | @@ -13,30 +13,35 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .json-form-ace-editor { | 17 | .json-form-ace-editor { |
17 | - position: relative; | ||
18 | - border: 1px solid #C0C0C0; | ||
19 | - height: 100%; | ||
20 | - .title-panel { | ||
21 | - position: absolute; | ||
22 | - font-size: 0.800rem; | ||
23 | - font-weight: 500; | ||
24 | - top: 10px; | ||
25 | - right: 20px; | ||
26 | - z-index: 5; | ||
27 | - label { | ||
28 | - color: #00acc1; | ||
29 | - background: rgba(220, 220, 220, 0.35); | ||
30 | - border-radius: 5px; | ||
31 | - padding: 4px; | ||
32 | - text-transform: uppercase; | ||
33 | - } | ||
34 | - button.tidy-button { | ||
35 | - background: rgba(220, 220, 220, 0.35) !important; | ||
36 | - span { | ||
37 | - padding: 0px !important; | ||
38 | - font-size: 12px !important; | ||
39 | - } | ||
40 | - } | 18 | + position: relative; |
19 | + height: 100%; | ||
20 | + border: 1px solid #c0c0c0; | ||
21 | + | ||
22 | + .title-panel { | ||
23 | + position: absolute; | ||
24 | + top: 10px; | ||
25 | + right: 20px; | ||
26 | + z-index: 5; | ||
27 | + font-size: .8rem; | ||
28 | + font-weight: 500; | ||
29 | + | ||
30 | + label { | ||
31 | + padding: 4px; | ||
32 | + color: #00acc1; | ||
33 | + text-transform: uppercase; | ||
34 | + background: rgba(220, 220, 220, .35); | ||
35 | + border-radius: 5px; | ||
41 | } | 36 | } |
42 | -} | ||
37 | + | ||
38 | + button.tidy-button { | ||
39 | + background: rgba(220, 220, 220, .35) !important; | ||
40 | + | ||
41 | + span { | ||
42 | + padding: 0 !important; | ||
43 | + font-size: 12px !important; | ||
44 | + } | ||
45 | + } | ||
46 | + } | ||
47 | +} |
@@ -15,21 +15,23 @@ | @@ -15,21 +15,23 @@ | ||
15 | */ | 15 | */ |
16 | @mixin tb-checkered-bg() { | 16 | @mixin tb-checkered-bg() { |
17 | background-color: #fff; | 17 | background-color: #fff; |
18 | - background-image: linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd), | ||
19 | - linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd); | ||
20 | - background-size: 8px 8px; | 18 | + background-image: |
19 | + linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd), | ||
20 | + linear-gradient(45deg, #ddd 25%, transparent 25%, transparent 75%, #ddd 75%, #ddd); | ||
21 | background-position: 0 0, 4px 4px; | 21 | background-position: 0 0, 4px 4px; |
22 | + background-size: 8px 8px; | ||
22 | } | 23 | } |
23 | 24 | ||
24 | .tb-color-preview { | 25 | .tb-color-preview { |
25 | - content: ''; | 26 | + position: relative; |
26 | width: 24px; | 27 | width: 24px; |
27 | height: 24px; | 28 | height: 24px; |
29 | + overflow: hidden; | ||
30 | + content: ""; | ||
28 | border: 2px solid #fff; | 31 | border: 2px solid #fff; |
29 | border-radius: 50%; | 32 | border-radius: 50%; |
30 | box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .14), 0 2px 2px 0 rgba(0, 0, 0, .098), 0 1px 5px 0 rgba(0, 0, 0, .084); | 33 | box-shadow: 0 3px 1px -2px rgba(0, 0, 0, .14), 0 2px 2px 0 rgba(0, 0, 0, .098), 0 1px 5px 0 rgba(0, 0, 0, .084); |
31 | - position: relative; | ||
32 | - overflow: hidden; | 34 | + |
33 | @include tb-checkered-bg(); | 35 | @include tb-checkered-bg(); |
34 | 36 | ||
35 | .tb-color-result { | 37 | .tb-color-result { |
@@ -13,66 +13,69 @@ | @@ -13,66 +13,69 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -$previewSize: 100px; | 16 | +$previewSize: 100px !default; |
17 | 17 | ||
18 | .tb-image-select-container { | 18 | .tb-image-select-container { |
19 | position: relative; | 19 | position: relative; |
20 | - height: $previewSize; | ||
21 | width: 100%; | 20 | width: 100%; |
21 | + height: $previewSize; | ||
22 | } | 22 | } |
23 | 23 | ||
24 | .tb-image-preview { | 24 | .tb-image-preview { |
25 | - max-width: $previewSize; | ||
26 | - max-height: $previewSize; | ||
27 | - width: 100%; | ||
28 | - height: 100%; | 25 | + width: 100%; |
26 | + max-width: $previewSize; | ||
27 | + height: 100%; | ||
28 | + max-height: $previewSize; | ||
29 | } | 29 | } |
30 | 30 | ||
31 | .tb-image-preview-container { | 31 | .tb-image-preview-container { |
32 | - position: relative; | ||
33 | - width: $previewSize; | ||
34 | - height: $previewSize; | ||
35 | - margin-right: 12px; | ||
36 | - border: solid 1px; | ||
37 | - vertical-align: top; | ||
38 | - float: left; | ||
39 | - div { | ||
40 | - width: 100%; | ||
41 | - font-size: 18px; | ||
42 | - text-align: center; | ||
43 | - position: absolute; | ||
44 | - top: 50%; | ||
45 | - left: 50%; | ||
46 | - transform: translate(-50%,-50%); | ||
47 | - } | 32 | + position: relative; |
33 | + float: left; | ||
34 | + width: $previewSize; | ||
35 | + height: $previewSize; | ||
36 | + margin-right: 12px; | ||
37 | + vertical-align: top; | ||
38 | + border: solid 1px; | ||
39 | + | ||
40 | + div { | ||
41 | + position: absolute; | ||
42 | + top: 50%; | ||
43 | + left: 50%; | ||
44 | + width: 100%; | ||
45 | + font-size: 18px; | ||
46 | + text-align: center; | ||
47 | + transform: translate(-50%, -50%); | ||
48 | + } | ||
48 | } | 49 | } |
49 | 50 | ||
50 | .tb-dropzone { | 51 | .tb-dropzone { |
51 | - position: relative; | ||
52 | - border: dashed 2px; | ||
53 | - height: $previewSize; | ||
54 | - vertical-align: top; | ||
55 | - padding: 0 8px; | ||
56 | - overflow: hidden; | ||
57 | - div { | ||
58 | - width: 100%; | ||
59 | - font-size: 24px; | ||
60 | - text-align: center; | ||
61 | - position: absolute; | ||
62 | - top: 50%; | ||
63 | - left: 50%; | ||
64 | - transform: translate(-50%,-50%); | ||
65 | - } | 52 | + position: relative; |
53 | + height: $previewSize; | ||
54 | + padding: 0 8px; | ||
55 | + overflow: hidden; | ||
56 | + vertical-align: top; | ||
57 | + border: dashed 2px; | ||
58 | + | ||
59 | + div { | ||
60 | + position: absolute; | ||
61 | + top: 50%; | ||
62 | + left: 50%; | ||
63 | + width: 100%; | ||
64 | + font-size: 24px; | ||
65 | + text-align: center; | ||
66 | + transform: translate(-50%, -50%); | ||
67 | + } | ||
66 | } | 68 | } |
67 | 69 | ||
68 | .tb-image-clear-container { | 70 | .tb-image-clear-container { |
69 | - width: 48px; | ||
70 | - height: $previewSize; | ||
71 | - position: relative; | ||
72 | - float: right; | 71 | + position: relative; |
72 | + float: right; | ||
73 | + width: 48px; | ||
74 | + height: $previewSize; | ||
73 | } | 75 | } |
76 | + | ||
74 | .tb-image-clear-btn { | 77 | .tb-image-clear-btn { |
75 | - position: absolute !important; | ||
76 | - top: 50%; | ||
77 | - transform: translate(0%,-50%) !important; | 78 | + position: absolute !important; |
79 | + top: 50%; | ||
80 | + transform: translate(0%, -50%) !important; | ||
78 | } | 81 | } |
@@ -15,87 +15,92 @@ | @@ -15,87 +15,92 @@ | ||
15 | */ | 15 | */ |
16 | @import "~compass-sass-mixins/lib/compass"; | 16 | @import "~compass-sass-mixins/lib/compass"; |
17 | 17 | ||
18 | -$swift-ease-out-duration: 0.4s !default; | ||
19 | -$swift-ease-out-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1) !default; | 18 | +$swift-ease-out-duration: .4s !default; |
19 | +$swift-ease-out-timing-function: cubic-bezier(.25, .8, .25, 1) !default; | ||
20 | 20 | ||
21 | $input-label-float-offset: 6px !default; | 21 | $input-label-float-offset: 6px !default; |
22 | -$input-label-float-scale: 0.75 !default; | 22 | +$input-label-float-scale: .75 !default; |
23 | 23 | ||
24 | .json-form-error { | 24 | .json-form-error { |
25 | - position: relative; | ||
26 | - bottom: -5px; | ||
27 | - font-size: 12px; | ||
28 | - line-height: 12px; | ||
29 | - color: rgb(244, 67, 54); | ||
30 | - @include transition(all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms); | 25 | + position: relative; |
26 | + bottom: -5px; | ||
27 | + font-size: 12px; | ||
28 | + line-height: 12px; | ||
29 | + color: rgb(244, 67, 54); | ||
30 | + | ||
31 | + @include transition(all 450ms cubic-bezier(.23, 1, .32, 1) 0ms); | ||
31 | } | 32 | } |
32 | 33 | ||
33 | .tb-container { | 34 | .tb-container { |
34 | - position: relative; | ||
35 | - margin-top: 32px; | ||
36 | - padding: 10px 0; | 35 | + position: relative; |
36 | + padding: 10px 0; | ||
37 | + margin-top: 32px; | ||
37 | } | 38 | } |
38 | 39 | ||
39 | .tb-field { | 40 | .tb-field { |
40 | - &.tb-required { | ||
41 | - label:after { | ||
42 | - content: ' *'; | ||
43 | - font-size: 13px; | ||
44 | - vertical-align: top; | ||
45 | - color: rgba(0,0,0,0.54); | ||
46 | - } | 41 | + &.tb-required { |
42 | + label::after { | ||
43 | + font-size: 13px; | ||
44 | + color: rgba(0, 0, 0, .54); | ||
45 | + vertical-align: top; | ||
46 | + content: " *"; | ||
47 | } | 47 | } |
48 | - &.tb-focused:not(.tb-readonly) { | ||
49 | - label:after { | ||
50 | - color: rgb(221,44,0); | ||
51 | - } | 48 | + } |
49 | + | ||
50 | + &.tb-focused:not(.tb-readonly) { | ||
51 | + label::after { | ||
52 | + color: rgb(221, 44, 0); | ||
52 | } | 53 | } |
54 | + } | ||
53 | } | 55 | } |
54 | 56 | ||
55 | .tb-date-field { | 57 | .tb-date-field { |
56 | - &.tb-required { | ||
57 | - div>div:first-child:after { | ||
58 | - content: ' *'; | ||
59 | - font-size: 13px; | ||
60 | - vertical-align: top; | ||
61 | - color: rgba(0,0,0,0.54); | ||
62 | - } | 58 | + &.tb-required { |
59 | + div > div:first-child::after { | ||
60 | + font-size: 13px; | ||
61 | + color: rgba(0, 0, 0, .54); | ||
62 | + vertical-align: top; | ||
63 | + content: " *"; | ||
63 | } | 64 | } |
64 | - &.tb-focused:not(.tb-readonly) { | ||
65 | - div>div:first-child:after { | ||
66 | - color: rgb(221,44,0); | ||
67 | - } | 65 | + } |
66 | + | ||
67 | + &.tb-focused:not(.tb-readonly) { | ||
68 | + div > div:first-child::after { | ||
69 | + color: rgb(221, 44, 0); | ||
68 | } | 70 | } |
71 | + } | ||
69 | } | 72 | } |
70 | 73 | ||
71 | label.tb-label { | 74 | label.tb-label { |
72 | - color: rgba(0,0,0,0.54); | ||
73 | - -webkit-font-smoothing: antialiased; | ||
74 | - position: absolute; | ||
75 | - bottom: 100%; | ||
76 | - left: 0; | ||
77 | - right: auto; | ||
78 | - @include transform(translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale)); | ||
79 | - @include transition(transform $swift-ease-out-timing-function $swift-ease-out-duration, | ||
80 | - width $swift-ease-out-timing-function $swift-ease-out-duration); | ||
81 | - transform-origin: left top; | ||
82 | - | ||
83 | - &.tb-focused { | ||
84 | - color: rgb(96,125,139); | ||
85 | - } | 75 | + position: absolute; |
76 | + right: auto; | ||
77 | + bottom: 100%; | ||
78 | + left: 0; | ||
79 | + color: rgba(0, 0, 0, .54); | ||
80 | + transform-origin: left top; | ||
81 | + -webkit-font-smoothing: antialiased; | ||
86 | 82 | ||
87 | - &.tb-required:after { | ||
88 | - content: ' *'; | ||
89 | - font-size: 13px; | ||
90 | - vertical-align: top; | ||
91 | - color: rgba(0,0,0,0.54); | ||
92 | - } | 83 | + @include transform(translate3d(0, $input-label-float-offset, 0) scale($input-label-float-scale)); |
93 | 84 | ||
94 | - &.tb-focused:not(.tb-readonly):after { | ||
95 | - color: rgb(221,44,0); | ||
96 | - } | 85 | + @include transition(transform $swift-ease-out-timing-function $swift-ease-out-duration, |
86 | + width $swift-ease-out-timing-function $swift-ease-out-duration); | ||
87 | + | ||
88 | + &.tb-focused { | ||
89 | + color: rgb(96, 125, 139); | ||
90 | + } | ||
91 | + | ||
92 | + &.tb-required::after { | ||
93 | + font-size: 13px; | ||
94 | + color: rgba(0, 0, 0, .54); | ||
95 | + vertical-align: top; | ||
96 | + content: " *"; | ||
97 | + } | ||
98 | + | ||
99 | + &.tb-focused:not(.tb-readonly)::after { | ||
100 | + color: rgb(221, 44, 0); | ||
101 | + } | ||
97 | } | 102 | } |
98 | 103 | ||
99 | .tb-head-label { | 104 | .tb-head-label { |
100 | - color: rgba(0,0,0,0.54); | 105 | + color: rgba(0, 0, 0, .54); |
101 | } | 106 | } |
@@ -13,16 +13,19 @@ | @@ -13,16 +13,19 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-related-entity-autocomplete { | 17 | .tb-related-entity-autocomplete { |
17 | .tb-not-found { | 18 | .tb-not-found { |
18 | display: block; | 19 | display: block; |
19 | - line-height: 1.5; | ||
20 | height: 48px; | 20 | height: 48px; |
21 | + line-height: 1.5; | ||
21 | } | 22 | } |
23 | + | ||
22 | .tb-entity-item { | 24 | .tb-entity-item { |
23 | display: block; | 25 | display: block; |
24 | height: 48px; | 26 | height: 48px; |
25 | } | 27 | } |
28 | + | ||
26 | li { | 29 | li { |
27 | height: auto !important; | 30 | height: auto !important; |
28 | white-space: normal !important; | 31 | white-space: normal !important; |
@@ -16,21 +16,22 @@ | @@ -16,21 +16,22 @@ | ||
16 | @import "~compass-sass-mixins/lib/compass"; | 16 | @import "~compass-sass-mixins/lib/compass"; |
17 | 17 | ||
18 | .tb-side-menu .md-button.tb-active { | 18 | .tb-side-menu .md-button.tb-active { |
19 | - background-color: rgba(255, 255, 255, 0.15); | ||
20 | font-weight: 500; | 19 | font-weight: 500; |
20 | + background-color: rgba(255, 255, 255, .15); | ||
21 | } | 21 | } |
22 | 22 | ||
23 | -.tb-side-menu, .tb-side-menu ul { | ||
24 | - list-style: none; | 23 | +.tb-side-menu, |
24 | +.tb-side-menu ul { | ||
25 | padding: 0; | 25 | padding: 0; |
26 | margin-top: 0; | 26 | margin-top: 0; |
27 | + list-style: none; | ||
27 | } | 28 | } |
28 | 29 | ||
29 | .tb-side-menu .tb-menu-toggle-list a.md-button { | 30 | .tb-side-menu .tb-menu-toggle-list a.md-button { |
30 | padding: 0 16px 0 32px; | 31 | padding: 0 16px 0 32px; |
32 | + font-weight: 500; | ||
31 | text-transform: none; | 33 | text-transform: none; |
32 | text-rendering: optimizeLegibility; | 34 | text-rendering: optimizeLegibility; |
33 | - font-weight: 500; | ||
34 | } | 35 | } |
35 | 36 | ||
36 | .tb-side-menu .tb-menu-toggle-list .md-button { | 37 | .tb-side-menu .tb-menu-toggle-list .md-button { |
@@ -43,36 +44,38 @@ | @@ -43,36 +44,38 @@ | ||
43 | } | 44 | } |
44 | 45 | ||
45 | .tb-side-menu > li { | 46 | .tb-side-menu > li { |
46 | - border-bottom: 1px solid rgba(0, 0, 0, 0.12); | 47 | + border-bottom: 1px solid rgba(0, 0, 0, .12); |
47 | } | 48 | } |
48 | 49 | ||
49 | .tb-side-menu .md-button-toggle .md-toggle-icon { | 50 | .tb-side-menu .md-button-toggle .md-toggle-icon { |
50 | - background-size: 100% auto; | ||
51 | display: inline-block; | 51 | display: inline-block; |
52 | - margin: auto 0 auto auto; | ||
53 | width: 15px; | 52 | width: 15px; |
53 | + margin: auto 0 auto auto; | ||
54 | + background-size: 100% auto; | ||
55 | + | ||
54 | @include transition(transform .3s, ease-in-out); | 56 | @include transition(transform .3s, ease-in-out); |
55 | } | 57 | } |
56 | 58 | ||
57 | .tb-side-menu .md-button { | 59 | .tb-side-menu .md-button { |
58 | display: flex; | 60 | display: flex; |
59 | - border-radius: 0; | ||
60 | - color: inherit; | ||
61 | - cursor: pointer; | ||
62 | - line-height: 40px; | ||
63 | - margin: 0; | 61 | + width: 100%; |
64 | max-height: 40px; | 62 | max-height: 40px; |
63 | + padding: 0 16px; | ||
64 | + margin: 0; | ||
65 | overflow: hidden; | 65 | overflow: hidden; |
66 | - padding: 0px 16px; | 66 | + line-height: 40px; |
67 | + color: inherit; | ||
67 | text-align: left; | 68 | text-align: left; |
68 | text-decoration: none; | 69 | text-decoration: none; |
69 | - white-space: nowrap; | ||
70 | text-overflow: ellipsis; | 70 | text-overflow: ellipsis; |
71 | - width: 100%; | 71 | + white-space: nowrap; |
72 | + cursor: pointer; | ||
73 | + border-radius: 0; | ||
74 | + | ||
72 | span { | 75 | span { |
73 | overflow: hidden; | 76 | overflow: hidden; |
74 | - white-space: nowrap; | ||
75 | text-overflow: ellipsis; | 77 | text-overflow: ellipsis; |
78 | + white-space: nowrap; | ||
76 | } | 79 | } |
77 | } | 80 | } |
78 | 81 | ||
@@ -83,10 +86,10 @@ | @@ -83,10 +86,10 @@ | ||
83 | 86 | ||
84 | .tb-side-menu ng-md-icon { | 87 | .tb-side-menu ng-md-icon { |
85 | margin-right: 8px; | 88 | margin-right: 8px; |
86 | - margin-left: 0px; | 89 | + margin-left: 0; |
87 | } | 90 | } |
88 | 91 | ||
89 | .tb-side-menu md-icon { | 92 | .tb-side-menu md-icon { |
90 | margin-right: 8px; | 93 | margin-right: 8px; |
91 | - margin-left: 0px; | 94 | + margin-left: 0; |
92 | } | 95 | } |
@@ -13,25 +13,32 @@ | @@ -13,25 +13,32 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | tb-timeinterval { | 17 | tb-timeinterval { |
17 | min-width: 355px; | 18 | min-width: 355px; |
19 | + | ||
18 | md-input-container { | 20 | md-input-container { |
19 | - margin-bottom: 0px; | 21 | + margin-bottom: 0; |
22 | + | ||
20 | .md-errors-spacer { | 23 | .md-errors-spacer { |
21 | - min-height: 0px; | 24 | + min-height: 0; |
22 | } | 25 | } |
23 | } | 26 | } |
27 | + | ||
24 | mdp-date-picker { | 28 | mdp-date-picker { |
25 | .md-input { | 29 | .md-input { |
26 | width: 150px; | 30 | width: 150px; |
27 | } | 31 | } |
28 | } | 32 | } |
33 | + | ||
29 | .md-input { | 34 | .md-input { |
30 | width: 70px !important; | 35 | width: 70px !important; |
31 | } | 36 | } |
37 | + | ||
32 | .advanced-switch { | 38 | .advanced-switch { |
33 | margin-top: 0; | 39 | margin-top: 0; |
34 | } | 40 | } |
41 | + | ||
35 | .advanced-label { | 42 | .advanced-label { |
36 | margin: 5px 0; | 43 | margin: 5px 0; |
37 | } | 44 | } |
@@ -13,6 +13,7 @@ | @@ -13,6 +13,7 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .md-panel { | 17 | .md-panel { |
17 | &.tb-timewindow-panel { | 18 | &.tb-timewindow-panel { |
18 | position: absolute; | 19 | position: absolute; |
@@ -20,38 +21,48 @@ | @@ -20,38 +21,48 @@ | ||
20 | } | 21 | } |
21 | 22 | ||
22 | .tb-timewindow-panel { | 23 | .tb-timewindow-panel { |
23 | - max-height: 440px; | ||
24 | min-width: 417px; | 24 | min-width: 417px; |
25 | - background: white; | ||
26 | - border-radius: 4px; | ||
27 | - box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2), | ||
28 | - 0 13px 19px 2px rgba(0, 0, 0, 0.14), | ||
29 | - 0 5px 24px 4px rgba(0, 0, 0, 0.12); | 25 | + max-height: 440px; |
30 | overflow: hidden; | 26 | overflow: hidden; |
31 | - form, fieldset { | 27 | + background: #fff; |
28 | + border-radius: 4px; | ||
29 | + box-shadow: | ||
30 | + 0 7px 8px -4px rgba(0, 0, 0, .2), | ||
31 | + 0 13px 19px 2px rgba(0, 0, 0, .14), | ||
32 | + 0 5px 24px 4px rgba(0, 0, 0, .12); | ||
33 | + | ||
34 | + form, | ||
35 | + fieldset { | ||
32 | height: 100%; | 36 | height: 100%; |
33 | } | 37 | } |
38 | + | ||
34 | md-content { | 39 | md-content { |
35 | - background-color: #fff; | ||
36 | overflow: hidden; | 40 | overflow: hidden; |
41 | + background-color: #fff; | ||
37 | } | 42 | } |
43 | + | ||
38 | .md-padding { | 44 | .md-padding { |
39 | padding: 0 16px; | 45 | padding: 0 16px; |
40 | } | 46 | } |
47 | + | ||
41 | .md-radio-interactive { | 48 | .md-radio-interactive { |
42 | - md-select, md-switch { | 49 | + md-select, |
50 | + md-switch { | ||
43 | pointer-events: all; | 51 | pointer-events: all; |
44 | } | 52 | } |
45 | } | 53 | } |
54 | + | ||
46 | md-radio-button { | 55 | md-radio-button { |
47 | .md-label { | 56 | .md-label { |
48 | width: 100%; | 57 | width: 100%; |
49 | } | 58 | } |
59 | + | ||
50 | tb-timeinterval { | 60 | tb-timeinterval { |
51 | width: 355px; | 61 | width: 355px; |
62 | + | ||
52 | .advanced-switch { | 63 | .advanced-switch { |
53 | - min-height: 30px; | ||
54 | max-width: 44px; | 64 | max-width: 44px; |
65 | + min-height: 30px; | ||
55 | } | 66 | } |
56 | } | 67 | } |
57 | } | 68 | } |
@@ -59,15 +70,17 @@ | @@ -59,15 +70,17 @@ | ||
59 | 70 | ||
60 | tb-timewindow { | 71 | tb-timewindow { |
61 | min-width: 52px; | 72 | min-width: 52px; |
73 | + | ||
62 | section.tb-timewindow { | 74 | section.tb-timewindow { |
63 | min-height: 32px; | 75 | min-height: 32px; |
64 | padding: 0 6px; | 76 | padding: 0 6px; |
77 | + | ||
65 | span { | 78 | span { |
66 | - pointer-events: all; | ||
67 | - cursor: pointer; | ||
68 | overflow: hidden; | 79 | overflow: hidden; |
69 | text-overflow: ellipsis; | 80 | text-overflow: ellipsis; |
70 | white-space: nowrap; | 81 | white-space: nowrap; |
82 | + pointer-events: all; | ||
83 | + cursor: pointer; | ||
71 | } | 84 | } |
72 | } | 85 | } |
73 | } | 86 | } |
@@ -13,18 +13,19 @@ | @@ -13,18 +13,19 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-manage-widget-actions { | 17 | .tb-manage-widget-actions { |
17 | table.md-table { | 18 | table.md-table { |
18 | tbody { | 19 | tbody { |
19 | tr { | 20 | tr { |
20 | td { | 21 | td { |
21 | &.tb-action-cell { | 22 | &.tb-action-cell { |
23 | + width: 100px; | ||
24 | + min-width: 100px; | ||
25 | + max-width: 100px; | ||
22 | overflow: hidden; | 26 | overflow: hidden; |
23 | text-overflow: ellipsis; | 27 | text-overflow: ellipsis; |
24 | white-space: nowrap; | 28 | white-space: nowrap; |
25 | - min-width: 100px; | ||
26 | - max-width: 100px; | ||
27 | - width: 100px; | ||
28 | } | 29 | } |
29 | } | 30 | } |
30 | } | 31 | } |
@@ -13,11 +13,13 @@ | @@ -13,11 +13,13 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-widget-config { | 17 | .tb-widget-config { |
17 | md-tab-content.md-active > div { | 18 | md-tab-content.md-active > div { |
18 | height: 100%; | 19 | height: 100%; |
19 | } | 20 | } |
21 | + | ||
20 | .tb-advanced-widget-config { | 22 | .tb-advanced-widget-config { |
21 | height: 100%; | 23 | height: 100%; |
22 | } | 24 | } |
23 | -} | ||
25 | +} |
@@ -13,18 +13,21 @@ | @@ -13,18 +13,21 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-widget { | 17 | .tb-widget { |
17 | .tb-widget-error { | 18 | .tb-widget-error { |
18 | display: flex; | 19 | display: flex; |
19 | - justify-content: center; | ||
20 | align-items: center; | 20 | align-items: center; |
21 | - background: rgba(255,255,255,0.5); | 21 | + justify-content: center; |
22 | + background: rgba(255, 255, 255, .5); | ||
23 | + | ||
22 | span { | 24 | span { |
23 | - color: red; | 25 | + color: #f00; |
24 | } | 26 | } |
25 | } | 27 | } |
28 | + | ||
26 | .tb-widget-loading { | 29 | .tb-widget-loading { |
27 | - background: rgba(255,255,255,0.15); | ||
28 | z-index: 3; | 30 | z-index: 3; |
31 | + background: rgba(255, 255, 255, .15); | ||
29 | } | 32 | } |
30 | } | 33 | } |
@@ -17,9 +17,10 @@ | @@ -17,9 +17,10 @@ | ||
17 | 17 | ||
18 | tb-widgets-bundle-select { | 18 | tb-widgets-bundle-select { |
19 | md-select { | 19 | md-select { |
20 | - margin: 0; | ||
21 | padding: 5px 20px; | 20 | padding: 5px 20px; |
21 | + margin: 0; | ||
22 | } | 22 | } |
23 | + | ||
23 | .tb-bundle-item { | 24 | .tb-bundle-item { |
24 | height: 24px; | 25 | height: 24px; |
25 | line-height: 24px; | 26 | line-height: 24px; |
@@ -33,24 +34,29 @@ tb-widgets-bundle-select { | @@ -33,24 +34,29 @@ tb-widgets-bundle-select { | ||
33 | } | 34 | } |
34 | } | 35 | } |
35 | 36 | ||
36 | -tb-widgets-bundle-select, .tb-widgets-bundle-select { | 37 | +tb-widgets-bundle-select, |
38 | +.tb-widgets-bundle-select { | ||
37 | .md-text { | 39 | .md-text { |
38 | display: block; | 40 | display: block; |
39 | width: 100%; | 41 | width: 100%; |
40 | } | 42 | } |
43 | + | ||
41 | .tb-bundle-item { | 44 | .tb-bundle-item { |
42 | display: inline-block; | 45 | display: inline-block; |
43 | width: 100%; | 46 | width: 100%; |
47 | + | ||
44 | span { | 48 | span { |
45 | display: inline-block; | 49 | display: inline-block; |
46 | vertical-align: middle; | 50 | vertical-align: middle; |
47 | } | 51 | } |
52 | + | ||
48 | .tb-bundle-system { | 53 | .tb-bundle-system { |
49 | - font-size: 0.8rem; | ||
50 | - opacity: 0.8; | ||
51 | float: right; | 54 | float: right; |
55 | + font-size: .8rem; | ||
56 | + opacity: .8; | ||
52 | } | 57 | } |
53 | } | 58 | } |
59 | + | ||
54 | md-option { | 60 | md-option { |
55 | height: auto !important; | 61 | height: auto !important; |
56 | white-space: normal !important; | 62 | white-space: normal !important; |
@@ -60,19 +66,23 @@ tb-widgets-bundle-select, .tb-widgets-bundle-select { | @@ -60,19 +66,23 @@ tb-widgets-bundle-select, .tb-widgets-bundle-select { | ||
60 | md-toolbar { | 66 | md-toolbar { |
61 | tb-widgets-bundle-select { | 67 | tb-widgets-bundle-select { |
62 | md-select { | 68 | md-select { |
63 | - background: rgba(255,255,255,0.2); | 69 | + background: rgba(255, 255, 255, .2); |
70 | + | ||
64 | .md-select-value { | 71 | .md-select-value { |
65 | - color: #fff; | ||
66 | font-size: 1.2rem; | 72 | font-size: 1.2rem; |
67 | - span:first-child:after { | ||
68 | - color: #fff; | 73 | + color: #fff; |
74 | + | ||
75 | + span:first-child::after { | ||
76 | + color: #fff; | ||
69 | } | 77 | } |
70 | } | 78 | } |
79 | + | ||
71 | .md-select-value.md-select-placeholder { | 80 | .md-select-value.md-select-placeholder { |
72 | color: #fff; | 81 | color: #fff; |
73 | - opacity: 0.8; | 82 | + opacity: .8; |
74 | } | 83 | } |
75 | } | 84 | } |
85 | + | ||
76 | md-select.ng-invalid.ng-touched { | 86 | md-select.ng-invalid.ng-touched { |
77 | .md-select-value { | 87 | .md-select-value { |
78 | color: #fff !important; | 88 | color: #fff !important; |
@@ -13,14 +13,15 @@ | @@ -13,14 +13,15 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-dashboard-assigned-customers { | 17 | .tb-dashboard-assigned-customers { |
17 | display: block; | 18 | display: block; |
18 | display: -webkit-box; | 19 | display: -webkit-box; |
19 | height: 34px; | 20 | height: 34px; |
21 | + margin-bottom: 4px; | ||
20 | overflow: hidden; | 22 | overflow: hidden; |
21 | text-overflow: ellipsis; | 23 | text-overflow: ellipsis; |
22 | -webkit-line-clamp: 2; | 24 | -webkit-line-clamp: 2; |
23 | -webkit-box-orient: vertical; | 25 | -webkit-box-orient: vertical; |
24 | - margin-bottom: 4px; | ||
25 | } | 26 | } |
26 | 27 |
@@ -13,50 +13,54 @@ | @@ -13,50 +13,54 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -$previewSize: 100px; | 16 | +$previewSize: 100px !default; |
17 | 17 | ||
18 | .tb-image-select-container { | 18 | .tb-image-select-container { |
19 | position: relative; | 19 | position: relative; |
20 | - height: $previewSize; | ||
21 | width: 100%; | 20 | width: 100%; |
21 | + height: $previewSize; | ||
22 | } | 22 | } |
23 | 23 | ||
24 | .tb-image-preview { | 24 | .tb-image-preview { |
25 | - max-width: $previewSize; | ||
26 | - max-height: $previewSize; | ||
27 | width: auto; | 25 | width: auto; |
26 | + max-width: $previewSize; | ||
28 | height: auto; | 27 | height: auto; |
28 | + max-height: $previewSize; | ||
29 | } | 29 | } |
30 | 30 | ||
31 | .tb-image-preview-container { | 31 | .tb-image-preview-container { |
32 | position: relative; | 32 | position: relative; |
33 | + float: left; | ||
33 | width: $previewSize; | 34 | width: $previewSize; |
34 | height: $previewSize; | 35 | height: $previewSize; |
35 | margin-right: 12px; | 36 | margin-right: 12px; |
36 | - border: solid 1px; | ||
37 | vertical-align: top; | 37 | vertical-align: top; |
38 | - float: left; | 38 | + border: solid 1px; |
39 | + | ||
39 | div { | 40 | div { |
40 | width: 100%; | 41 | width: 100%; |
41 | font-size: 18px; | 42 | font-size: 18px; |
42 | text-align: center; | 43 | text-align: center; |
43 | } | 44 | } |
44 | - div, .tb-image-preview { | 45 | + |
46 | + div, | ||
47 | + .tb-image-preview { | ||
45 | position: absolute; | 48 | position: absolute; |
46 | top: 50%; | 49 | top: 50%; |
47 | left: 50%; | 50 | left: 50%; |
48 | - transform: translate(-50%,-50%); | 51 | + transform: translate(-50%, -50%); |
49 | } | 52 | } |
50 | } | 53 | } |
51 | 54 | ||
52 | .tb-image-clear-container { | 55 | .tb-image-clear-container { |
53 | - width: 48px; | ||
54 | - height: $previewSize; | ||
55 | position: relative; | 56 | position: relative; |
56 | float: right; | 57 | float: right; |
58 | + width: 48px; | ||
59 | + height: $previewSize; | ||
57 | } | 60 | } |
61 | + | ||
58 | .tb-image-clear-btn { | 62 | .tb-image-clear-btn { |
59 | position: absolute !important; | 63 | position: absolute !important; |
60 | top: 50%; | 64 | top: 50%; |
61 | - transform: translate(0%,-50%) !important; | 65 | + transform: translate(0%, -50%) !important; |
62 | } | 66 | } |
@@ -14,13 +14,14 @@ | @@ -14,13 +14,14 @@ | ||
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | @import "~compass-sass-mixins/lib/compass"; | 16 | @import "~compass-sass-mixins/lib/compass"; |
17 | -@import '../../scss/constants'; | ||
18 | 17 | ||
19 | -$toolbar-height: 50px; | ||
20 | -$fullscreen-toolbar-height: 64px; | ||
21 | -$mobile-toolbar-height: 80px; | ||
22 | -$half-mobile-toolbar-height: 40px; | ||
23 | -$mobile-toolbar-height-total: 84px; | 18 | +@import "../../scss/constants"; |
19 | + | ||
20 | +$toolbar-height: 50px !default; | ||
21 | +$fullscreen-toolbar-height: 64px !default; | ||
22 | +$mobile-toolbar-height: 80px !default; | ||
23 | +$half-mobile-toolbar-height: 40px !default; | ||
24 | +$mobile-toolbar-height-total: 84px !default; | ||
24 | 25 | ||
25 | tb-dashboard-toolbar { | 26 | tb-dashboard-toolbar { |
26 | md-fab-toolbar { | 27 | md-fab-toolbar { |
@@ -29,126 +30,156 @@ tb-dashboard-toolbar { | @@ -29,126 +30,156 @@ tb-dashboard-toolbar { | ||
29 | .md-button { | 30 | .md-button { |
30 | &.md-fab { | 31 | &.md-fab { |
31 | opacity: 1; | 32 | opacity: 1; |
33 | + | ||
32 | @include transition(opacity .3s cubic-bezier(.55,0,.55,.2)); | 34 | @include transition(opacity .3s cubic-bezier(.55,0,.55,.2)); |
35 | + | ||
33 | .md-fab-toolbar-background { | 36 | .md-fab-toolbar-background { |
34 | - background-color: $primary-default !important; | 37 | + background-color: $primary-default !important; |
35 | } | 38 | } |
36 | } | 39 | } |
37 | } | 40 | } |
38 | } | 41 | } |
39 | } | 42 | } |
43 | + | ||
40 | md-fab-trigger { | 44 | md-fab-trigger { |
41 | .md-button { | 45 | .md-button { |
42 | &.md-fab { | 46 | &.md-fab { |
43 | - line-height: 36px; | ||
44 | width: 36px; | 47 | width: 36px; |
45 | height: 36px; | 48 | height: 36px; |
46 | margin: 4px 0 0 4px; | 49 | margin: 4px 0 0 4px; |
47 | - opacity: 0.5; | 50 | + line-height: 36px; |
51 | + opacity: .5; | ||
52 | + | ||
48 | @include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s); | 53 | @include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s); |
54 | + | ||
49 | md-icon { | 55 | md-icon { |
50 | position: absolute; | 56 | position: absolute; |
51 | top: 25%; | 57 | top: 25%; |
52 | - margin: 0; | ||
53 | - line-height: 18px; | ||
54 | - height: 18px; | ||
55 | width: 18px; | 58 | width: 18px; |
56 | - min-height: 18px; | ||
57 | min-width: 18px; | 59 | min-width: 18px; |
60 | + height: 18px; | ||
61 | + min-height: 18px; | ||
62 | + margin: 0; | ||
63 | + line-height: 18px; | ||
58 | } | 64 | } |
59 | } | 65 | } |
60 | } | 66 | } |
61 | } | 67 | } |
68 | + | ||
62 | &.is-fullscreen { | 69 | &.is-fullscreen { |
63 | &.md-is-open { | 70 | &.md-is-open { |
64 | md-fab-trigger { | 71 | md-fab-trigger { |
65 | .md-button { | 72 | .md-button { |
66 | &.md-fab { | 73 | &.md-fab { |
67 | .md-fab-toolbar-background { | 74 | .md-fab-toolbar-background { |
68 | - transition-delay: 0ms !important; | ||
69 | - transition-duration: 0ms !important; | 75 | + transition-delay: 0ms !important; |
76 | + transition-duration: 0ms !important; | ||
70 | } | 77 | } |
71 | } | 78 | } |
72 | } | 79 | } |
73 | } | 80 | } |
74 | } | 81 | } |
82 | + | ||
75 | .md-fab-toolbar-wrapper { | 83 | .md-fab-toolbar-wrapper { |
76 | height: $mobile-toolbar-height-total; | 84 | height: $mobile-toolbar-height-total; |
85 | + | ||
77 | @media (min-width: $layout-breakpoint-sm) { | 86 | @media (min-width: $layout-breakpoint-sm) { |
78 | height: $fullscreen-toolbar-height; | 87 | height: $fullscreen-toolbar-height; |
79 | } | 88 | } |
89 | + | ||
80 | md-toolbar { | 90 | md-toolbar { |
81 | - min-height: $mobile-toolbar-height; | ||
82 | height: $mobile-toolbar-height; | 91 | height: $mobile-toolbar-height; |
92 | + min-height: $mobile-toolbar-height; | ||
93 | + | ||
83 | @media (min-width: $layout-breakpoint-sm) { | 94 | @media (min-width: $layout-breakpoint-sm) { |
84 | - min-height: $fullscreen-toolbar-height; | ||
85 | height: $fullscreen-toolbar-height; | 95 | height: $fullscreen-toolbar-height; |
96 | + min-height: $fullscreen-toolbar-height; | ||
86 | } | 97 | } |
87 | } | 98 | } |
88 | } | 99 | } |
89 | } | 100 | } |
101 | + | ||
90 | .md-fab-toolbar-wrapper { | 102 | .md-fab-toolbar-wrapper { |
91 | height: $mobile-toolbar-height-total; | 103 | height: $mobile-toolbar-height-total; |
104 | + | ||
92 | @media (min-width: $layout-breakpoint-sm) { | 105 | @media (min-width: $layout-breakpoint-sm) { |
93 | height: $toolbar-height; | 106 | height: $toolbar-height; |
94 | } | 107 | } |
108 | + | ||
95 | md-toolbar { | 109 | md-toolbar { |
96 | - min-height: $mobile-toolbar-height; | ||
97 | height: $mobile-toolbar-height; | 110 | height: $mobile-toolbar-height; |
111 | + min-height: $mobile-toolbar-height; | ||
112 | + | ||
98 | @media (min-width: $layout-breakpoint-sm) { | 113 | @media (min-width: $layout-breakpoint-sm) { |
99 | - min-height: $toolbar-height; | ||
100 | height: $toolbar-height; | 114 | height: $toolbar-height; |
115 | + min-height: $toolbar-height; | ||
101 | } | 116 | } |
117 | + | ||
102 | md-fab-actions { | 118 | md-fab-actions { |
119 | + margin-top: 0; | ||
103 | font-size: 16px; | 120 | font-size: 16px; |
104 | - margin-top: 0px; | 121 | + |
105 | @media (max-width: $layout-breakpoint-sm) { | 122 | @media (max-width: $layout-breakpoint-sm) { |
106 | height: $mobile-toolbar-height; | 123 | height: $mobile-toolbar-height; |
107 | max-height: $mobile-toolbar-height; | 124 | max-height: $mobile-toolbar-height; |
108 | } | 125 | } |
126 | + | ||
109 | .close-action { | 127 | .close-action { |
110 | margin-right: -18px; | 128 | margin-right: -18px; |
111 | } | 129 | } |
130 | + | ||
112 | .md-fab-action-item { | 131 | .md-fab-action-item { |
113 | width: 100%; | 132 | width: 100%; |
114 | height: $mobile-toolbar-height; | 133 | height: $mobile-toolbar-height; |
134 | + | ||
115 | @media (min-width: $layout-breakpoint-sm) { | 135 | @media (min-width: $layout-breakpoint-sm) { |
116 | height: 46px; | 136 | height: 46px; |
117 | } | 137 | } |
138 | + | ||
118 | .tb-dashboard-action-panels { | 139 | .tb-dashboard-action-panels { |
140 | + flex-direction: column-reverse; | ||
119 | height: $mobile-toolbar-height; | 141 | height: $mobile-toolbar-height; |
142 | + | ||
120 | @media (min-width: $layout-breakpoint-sm) { | 143 | @media (min-width: $layout-breakpoint-sm) { |
121 | height: 46px; | 144 | height: 46px; |
122 | } | 145 | } |
123 | - flex-direction: column-reverse; | 146 | + |
124 | @media (min-width: $layout-breakpoint-sm) { | 147 | @media (min-width: $layout-breakpoint-sm) { |
125 | flex-direction: row-reverse; | 148 | flex-direction: row-reverse; |
126 | } | 149 | } |
150 | + | ||
127 | .tb-dashboard-action-panel { | 151 | .tb-dashboard-action-panel { |
128 | - min-width: 0px; | 152 | + flex-direction: row-reverse; |
153 | + min-width: 0; | ||
129 | height: $half-mobile-toolbar-height; | 154 | height: $half-mobile-toolbar-height; |
155 | + | ||
130 | @media (min-width: $layout-breakpoint-sm) { | 156 | @media (min-width: $layout-breakpoint-sm) { |
131 | height: 46px; | 157 | height: 46px; |
132 | } | 158 | } |
133 | - flex-direction: row-reverse; | 159 | + |
134 | div { | 160 | div { |
135 | height: $half-mobile-toolbar-height; | 161 | height: $half-mobile-toolbar-height; |
162 | + | ||
136 | @media (min-width: $layout-breakpoint-sm) { | 163 | @media (min-width: $layout-breakpoint-sm) { |
137 | height: 46px; | 164 | height: 46px; |
138 | } | 165 | } |
139 | } | 166 | } |
167 | + | ||
140 | md-select { | 168 | md-select { |
141 | pointer-events: all; | 169 | pointer-events: all; |
142 | } | 170 | } |
171 | + | ||
143 | tb-states-component { | 172 | tb-states-component { |
144 | pointer-events: all; | 173 | pointer-events: all; |
145 | } | 174 | } |
175 | + | ||
146 | button.md-icon-button { | 176 | button.md-icon-button { |
147 | min-width: 40px; | 177 | min-width: 40px; |
178 | + | ||
148 | @media (max-width: $layout-breakpoint-sm) { | 179 | @media (max-width: $layout-breakpoint-sm) { |
149 | min-width: 28px; | 180 | min-width: 28px; |
150 | - margin: 0px; | ||
151 | padding: 2px; | 181 | padding: 2px; |
182 | + margin: 0; | ||
152 | } | 183 | } |
153 | } | 184 | } |
154 | } | 185 | } |
@@ -158,4 +189,4 @@ tb-dashboard-toolbar { | @@ -158,4 +189,4 @@ tb-dashboard-toolbar { | ||
158 | } | 189 | } |
159 | } | 190 | } |
160 | } | 191 | } |
161 | -} | ||
192 | +} |
@@ -14,11 +14,12 @@ | @@ -14,11 +14,12 @@ | ||
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | @import "~compass-sass-mixins/lib/compass"; | 16 | @import "~compass-sass-mixins/lib/compass"; |
17 | -@import '../../scss/constants'; | ||
18 | 17 | ||
19 | -$toolbar-height: 50px; | ||
20 | -$fullscreen-toolbar-height: 64px; | ||
21 | -$mobile-toolbar-height: 84px; | 18 | +@import "../../scss/constants"; |
19 | + | ||
20 | +$toolbar-height: 50px !default; | ||
21 | +$fullscreen-toolbar-height: 64px !default; | ||
22 | +$mobile-toolbar-height: 84px !default; | ||
22 | 23 | ||
23 | section.tb-dashboard-title { | 24 | section.tb-dashboard-title { |
24 | position: absolute; | 25 | position: absolute; |
@@ -27,10 +28,10 @@ section.tb-dashboard-title { | @@ -27,10 +28,10 @@ section.tb-dashboard-title { | ||
27 | } | 28 | } |
28 | 29 | ||
29 | input.tb-dashboard-title { | 30 | input.tb-dashboard-title { |
30 | - font-size: 2.000rem; | ||
31 | - font-weight: 500; | ||
32 | - letter-spacing: 0.005em; | ||
33 | height: 38px; | 31 | height: 38px; |
32 | + font-size: 2rem; | ||
33 | + font-weight: 500; | ||
34 | + letter-spacing: .005em; | ||
34 | } | 35 | } |
35 | 36 | ||
36 | div.tb-padded { | 37 | div.tb-padded { |
@@ -50,9 +51,11 @@ tb-details-sidenav.tb-widget-details-sidenav { | @@ -50,9 +51,11 @@ tb-details-sidenav.tb-widget-details-sidenav { | ||
50 | @media (min-width: $layout-breakpoint-sm) { | 51 | @media (min-width: $layout-breakpoint-sm) { |
51 | width: 85% !important; | 52 | width: 85% !important; |
52 | } | 53 | } |
54 | + | ||
53 | @media (min-width: $layout-breakpoint-md) { | 55 | @media (min-width: $layout-breakpoint-md) { |
54 | width: 75% !important; | 56 | width: 75% !important; |
55 | } | 57 | } |
58 | + | ||
56 | @media (min-width: $layout-breakpoint-lg) { | 59 | @media (min-width: $layout-breakpoint-lg) { |
57 | width: 60% !important; | 60 | width: 60% !important; |
58 | } | 61 | } |
@@ -65,47 +68,55 @@ tb-details-sidenav.tb-widget-details-sidenav { | @@ -65,47 +68,55 @@ tb-details-sidenav.tb-widget-details-sidenav { | ||
65 | 68 | ||
66 | section.tb-dashboard-toolbar { | 69 | section.tb-dashboard-toolbar { |
67 | position: absolute; | 70 | position: absolute; |
68 | - top: 0px; | ||
69 | - left: 0px; | 71 | + top: 0; |
72 | + left: 0; | ||
70 | z-index: 13; | 73 | z-index: 13; |
71 | pointer-events: none; | 74 | pointer-events: none; |
75 | + | ||
72 | &.tb-dashboard-toolbar-opened { | 76 | &.tb-dashboard-toolbar-opened { |
73 | - right: 0px; | ||
74 | - // @include transition(right .3s cubic-bezier(.55,0,.55,.2)); | 77 | + right: 0; |
78 | + // @include transition(right .3s cubic-bezier(.55,0,.55,.2)); | ||
75 | } | 79 | } |
80 | + | ||
76 | &.tb-dashboard-toolbar-closed { | 81 | &.tb-dashboard-toolbar-closed { |
77 | right: 18px; | 82 | right: 18px; |
83 | + | ||
78 | @include transition(right .3s cubic-bezier(.55,0,.55,.2) .2s); | 84 | @include transition(right .3s cubic-bezier(.55,0,.55,.2) .2s); |
79 | } | 85 | } |
80 | } | 86 | } |
81 | 87 | ||
82 | .tb-dashboard-container { | 88 | .tb-dashboard-container { |
83 | - &.tb-dashboard-toolbar-opened { | ||
84 | - &.is-fullscreen { | ||
85 | - margin-top: $mobile-toolbar-height; | ||
86 | - @media (min-width: $layout-breakpoint-sm) { | ||
87 | - margin-top: $fullscreen-toolbar-height; | ||
88 | - } | ||
89 | - } | ||
90 | - &:not(.is-fullscreen) { | ||
91 | - margin-top: $mobile-toolbar-height; | ||
92 | - @media (min-width: $layout-breakpoint-sm) { | ||
93 | - margin-top: $toolbar-height; | ||
94 | - } | ||
95 | - @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2)); | ||
96 | - } | ||
97 | - } | ||
98 | - &.tb-dashboard-toolbar-closed { | ||
99 | - margin-top: 0px; | ||
100 | - @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2) .2s); | 89 | + &.tb-dashboard-toolbar-opened { |
90 | + &.is-fullscreen { | ||
91 | + margin-top: $mobile-toolbar-height; | ||
92 | + | ||
93 | + @media (min-width: $layout-breakpoint-sm) { | ||
94 | + margin-top: $fullscreen-toolbar-height; | ||
95 | + } | ||
96 | + } | ||
97 | + | ||
98 | + &:not(.is-fullscreen) { | ||
99 | + margin-top: $mobile-toolbar-height; | ||
100 | + | ||
101 | + @media (min-width: $layout-breakpoint-sm) { | ||
102 | + margin-top: $toolbar-height; | ||
103 | + } | ||
104 | + | ||
105 | + @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2)); | ||
106 | + } | ||
107 | + } | ||
108 | + | ||
109 | + &.tb-dashboard-toolbar-closed { | ||
110 | + margin-top: 0; | ||
111 | + | ||
112 | + @include transition(margin-top .3s cubic-bezier(.55,0,.55,.2) .2s); | ||
101 | } | 113 | } |
114 | + | ||
102 | .tb-dashboard-layouts { | 115 | .tb-dashboard-layouts { |
103 | md-backdrop { | 116 | md-backdrop { |
104 | z-index: 1; | 117 | z-index: 1; |
105 | } | 118 | } |
106 | - #tb-main-layout { | ||
107 | 119 | ||
108 | - } | ||
109 | #tb-right-layout { | 120 | #tb-right-layout { |
110 | md-sidenav { | 121 | md-sidenav { |
111 | z-index: 1; | 122 | z-index: 1; |
@@ -124,13 +135,15 @@ section.tb-powered-by-footer { | @@ -124,13 +135,15 @@ section.tb-powered-by-footer { | ||
124 | bottom: 5px; | 135 | bottom: 5px; |
125 | z-index: 3; | 136 | z-index: 3; |
126 | pointer-events: none; | 137 | pointer-events: none; |
138 | + | ||
127 | span { | 139 | span { |
128 | font-size: 12px; | 140 | font-size: 12px; |
141 | + | ||
129 | a { | 142 | a { |
130 | - font-weight: bold; | 143 | + font-weight: 700; |
131 | text-decoration: none; | 144 | text-decoration: none; |
132 | - border: none; | ||
133 | pointer-events: all; | 145 | pointer-events: all; |
146 | + border: none; | ||
134 | } | 147 | } |
135 | } | 148 | } |
136 | } | 149 | } |
@@ -13,6 +13,7 @@ | @@ -13,6 +13,7 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | md-select.default-state-controller { | 17 | md-select.default-state-controller { |
17 | - margin: 0px; | ||
18 | -} | ||
18 | + margin: 0; | ||
19 | +} |
@@ -13,34 +13,37 @@ | @@ -13,34 +13,37 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -@import '../../../scss/constants'; | 16 | +@import "../../../scss/constants"; |
17 | 17 | ||
18 | tb-states-component { | 18 | tb-states-component { |
19 | - min-width: 0px; | 19 | + min-width: 0; |
20 | } | 20 | } |
21 | 21 | ||
22 | .entity-state-controller { | 22 | .entity-state-controller { |
23 | - .state-divider { | ||
24 | - font-size: 18px; | ||
25 | - padding-left: 15px; | ||
26 | - padding-right: 15px; | ||
27 | - overflow: hidden; | ||
28 | - text-overflow: ellipsis; | ||
29 | - white-space: nowrap; | ||
30 | - pointer-events: none; | ||
31 | - } | ||
32 | - .state-entry { | ||
33 | - font-size: 18px; | ||
34 | - overflow: hidden; | ||
35 | - text-overflow: ellipsis; | ||
36 | - white-space: nowrap; | ||
37 | - outline: none; | ||
38 | - } | ||
39 | - md-select { | ||
40 | - margin: 0px; | ||
41 | - .md-text { | ||
42 | - font-size: 18px; | ||
43 | - font-weight: bold; | ||
44 | - } | 23 | + .state-divider { |
24 | + padding-right: 15px; | ||
25 | + padding-left: 15px; | ||
26 | + overflow: hidden; | ||
27 | + font-size: 18px; | ||
28 | + text-overflow: ellipsis; | ||
29 | + white-space: nowrap; | ||
30 | + pointer-events: none; | ||
31 | + } | ||
32 | + | ||
33 | + .state-entry { | ||
34 | + overflow: hidden; | ||
35 | + font-size: 18px; | ||
36 | + text-overflow: ellipsis; | ||
37 | + white-space: nowrap; | ||
38 | + outline: none; | ||
39 | + } | ||
40 | + | ||
41 | + md-select { | ||
42 | + margin: 0; | ||
43 | + | ||
44 | + .md-text { | ||
45 | + font-size: 18px; | ||
46 | + font-weight: 700; | ||
45 | } | 47 | } |
46 | -} | ||
48 | + } | ||
49 | +} |
@@ -13,21 +13,22 @@ | @@ -13,21 +13,22 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .manage-dashboard-states { | 17 | .manage-dashboard-states { |
17 | table.md-table { | 18 | table.md-table { |
18 | tbody { | 19 | tbody { |
19 | tr { | 20 | tr { |
20 | td { | 21 | td { |
21 | &.tb-action-cell { | 22 | &.tb-action-cell { |
23 | + width: 100px; | ||
24 | + min-width: 100px; | ||
25 | + max-width: 100px; | ||
22 | overflow: hidden; | 26 | overflow: hidden; |
23 | text-overflow: ellipsis; | 27 | text-overflow: ellipsis; |
24 | white-space: nowrap; | 28 | white-space: nowrap; |
25 | - min-width: 100px; | ||
26 | - max-width: 100px; | ||
27 | - width: 100px; | ||
28 | } | 29 | } |
29 | } | 30 | } |
30 | } | 31 | } |
31 | } | 32 | } |
32 | } | 33 | } |
33 | -} | ||
34 | +} |
@@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | -@import '../../../scss/constants'; | 16 | +@import "../../../scss/constants"; |
17 | 17 | ||
18 | tb-aliases-entity-select { | 18 | tb-aliases-entity-select { |
19 | min-width: 52px; | 19 | min-width: 52px; |
@@ -26,18 +26,21 @@ tb-aliases-entity-select { | @@ -26,18 +26,21 @@ tb-aliases-entity-select { | ||
26 | } | 26 | } |
27 | 27 | ||
28 | .tb-aliases-entity-select-panel { | 28 | .tb-aliases-entity-select-panel { |
29 | + min-width: 300px; | ||
29 | max-height: 150px; | 30 | max-height: 150px; |
31 | + overflow-x: hidden; | ||
32 | + overflow-y: auto; | ||
33 | + background: #fff; | ||
34 | + border-radius: 4px; | ||
35 | + box-shadow: | ||
36 | + 0 7px 8px -4px rgba(0, 0, 0, .2), | ||
37 | + 0 13px 19px 2px rgba(0, 0, 0, .14), | ||
38 | + 0 5px 24px 4px rgba(0, 0, 0, .12); | ||
39 | + | ||
30 | @media (min-height: 350px) { | 40 | @media (min-height: 350px) { |
31 | max-height: 250px; | 41 | max-height: 250px; |
32 | } | 42 | } |
33 | - min-width: 300px; | ||
34 | - background: white; | ||
35 | - border-radius: 4px; | ||
36 | - box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2), | ||
37 | - 0 13px 19px 2px rgba(0, 0, 0, 0.14), | ||
38 | - 0 5px 24px 4px rgba(0, 0, 0, 0.12); | ||
39 | - overflow-x: hidden; | ||
40 | - overflow-y: auto; | 43 | + |
41 | md-content { | 44 | md-content { |
42 | background-color: #fff; | 45 | background-color: #fff; |
43 | } | 46 | } |
@@ -46,15 +49,17 @@ tb-aliases-entity-select { | @@ -46,15 +49,17 @@ tb-aliases-entity-select { | ||
46 | section.tb-aliases-entity-select { | 49 | section.tb-aliases-entity-select { |
47 | min-height: 32px; | 50 | min-height: 32px; |
48 | padding: 0 6px; | 51 | padding: 0 6px; |
52 | + | ||
49 | @media (max-width: $layout-breakpoint-sm) { | 53 | @media (max-width: $layout-breakpoint-sm) { |
50 | - padding: 0px; | 54 | + padding: 0; |
51 | } | 55 | } |
56 | + | ||
52 | span { | 57 | span { |
53 | max-width: 200px; | 58 | max-width: 200px; |
54 | - pointer-events: all; | ||
55 | - cursor: pointer; | ||
56 | overflow: hidden; | 59 | overflow: hidden; |
57 | text-overflow: ellipsis; | 60 | text-overflow: ellipsis; |
58 | white-space: nowrap; | 61 | white-space: nowrap; |
62 | + pointer-events: all; | ||
63 | + cursor: pointer; | ||
59 | } | 64 | } |
60 | } | 65 | } |
@@ -13,14 +13,17 @@ | @@ -13,14 +13,17 @@ | ||
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | + | ||
16 | .tb-entity-alias-dialog { | 17 | .tb-entity-alias-dialog { |
17 | .tb-resolve-multiple-switch { | 18 | .tb-resolve-multiple-switch { |
18 | padding-left: 10px; | 19 | padding-left: 10px; |
20 | + | ||
19 | .resolve-multiple-switch { | 21 | .resolve-multiple-switch { |
20 | margin: 0; | 22 | margin: 0; |
21 | } | 23 | } |
24 | + | ||
22 | .resolve-multiple-label { | 25 | .resolve-multiple-label { |
23 | margin: 5px 0; | 26 | margin: 5px 0; |
24 | } | 27 | } |
25 | } | 28 | } |
26 | -} | ||
29 | +} |