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.
... | ... | @@ -76,7 +76,7 @@ public class DeviceController extends BaseController { |
76 | 76 | device.setTenantId(getCurrentUser().getTenantId()); |
77 | 77 | if (getCurrentUser().getAuthority() == Authority.CUSTOMER_USER) { |
78 | 78 | if (device.getId() == null || device.getId().isNullUid() || |
79 | - device.getCustomerId() == null || device.getCustomerId().isNullUid()) { | |
79 | + device.getCustomerId() == null || device.getCustomerId().isNullUid()) { | |
80 | 80 | throw new ThingsboardException("You don't have permission to perform this operation!", |
81 | 81 | ThingsboardErrorCode.PERMISSION_DENIED); |
82 | 82 | } else { | ... | ... |
... | ... | @@ -49,15 +49,15 @@ import org.thingsboard.server.common.data.kv.Aggregation; |
49 | 49 | import org.thingsboard.server.common.data.kv.AttributeKey; |
50 | 50 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
51 | 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 | 53 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
54 | 54 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
55 | 55 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
56 | 56 | import org.thingsboard.server.common.data.kv.KvEntry; |
57 | 57 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
58 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | |
58 | 59 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
59 | 60 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
60 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | |
61 | 61 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; |
62 | 62 | import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; |
63 | 63 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
... | ... | @@ -81,7 +81,6 @@ import java.util.LinkedHashMap; |
81 | 81 | import java.util.List; |
82 | 82 | import java.util.Map; |
83 | 83 | import java.util.Set; |
84 | -import java.util.UUID; | |
85 | 84 | import java.util.concurrent.ExecutorService; |
86 | 85 | import java.util.concurrent.Executors; |
87 | 86 | import java.util.stream.Collectors; |
... | ... | @@ -201,7 +200,7 @@ public class TelemetryController extends BaseController { |
201 | 200 | (result, entityId) -> { |
202 | 201 | // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted |
203 | 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 | 204 | .collect(Collectors.toList()); |
206 | 205 | |
207 | 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 | 22 | import delight.nashornsandbox.NashornSandboxes; |
23 | 23 | import jdk.nashorn.api.scripting.NashornScriptEngineFactory; |
24 | 24 | import lombok.extern.slf4j.Slf4j; |
25 | +import org.apache.commons.lang3.tuple.Pair; | |
25 | 26 | |
26 | 27 | import javax.annotation.PostConstruct; |
27 | 28 | import javax.annotation.PreDestroy; |
... | ... | @@ -42,9 +43,10 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic |
42 | 43 | private ScriptEngine engine; |
43 | 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 | 51 | @PostConstruct |
50 | 52 | public void init() { |
... | ... | @@ -78,19 +80,27 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic |
78 | 80 | |
79 | 81 | @Override |
80 | 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 | 105 | return Futures.immediateFuture(scriptId); |
96 | 106 | } |
... | ... | @@ -122,6 +132,13 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic |
122 | 132 | |
123 | 133 | @Override |
124 | 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 | 142 | String functionName = functionsMap.get(scriptId); |
126 | 143 | if (functionName != null) { |
127 | 144 | try { |
... | ... | @@ -156,4 +173,16 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic |
156 | 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 | 45 | try { |
46 | 46 | this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get(); |
47 | 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 | 24 | import org.springframework.context.annotation.Lazy; |
25 | 25 | import org.springframework.stereotype.Service; |
26 | 26 | import org.springframework.util.StringUtils; |
27 | +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; | |
27 | 28 | import org.thingsboard.rule.engine.api.util.DonAsynchron; |
29 | +import org.thingsboard.server.actors.service.ActorService; | |
28 | 30 | import org.thingsboard.server.common.data.DataConstants; |
29 | 31 | import org.thingsboard.server.common.data.EntityType; |
30 | 32 | import org.thingsboard.server.common.data.EntityView; |
31 | 33 | import org.thingsboard.server.common.data.id.DeviceId; |
32 | 34 | import org.thingsboard.server.common.data.id.EntityId; |
33 | 35 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
36 | +//<<<<<<< HEAD | |
34 | 37 | import org.thingsboard.server.common.data.id.EntityViewId; |
38 | +//======= | |
39 | +import org.thingsboard.server.common.data.id.TenantId; | |
40 | +//>>>>>>> d909192071880b7af2137333142bc62ece369ec1 | |
35 | 41 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
36 | 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 | 44 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
39 | 45 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
40 | 46 | import org.thingsboard.server.common.data.kv.DataType; |
41 | 47 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
42 | 48 | import org.thingsboard.server.common.data.kv.KvEntry; |
43 | 49 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
50 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | |
44 | 51 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
45 | 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 | 54 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
48 | 55 | import org.thingsboard.server.dao.attributes.AttributesService; |
49 | 56 | import org.thingsboard.server.dao.entityview.EntityViewService; |
... | ... | @@ -108,6 +115,10 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio |
108 | 115 | @Lazy |
109 | 116 | private DeviceStateService stateService; |
110 | 117 | |
118 | + @Autowired | |
119 | + @Lazy | |
120 | + private ActorService actorService; | |
121 | + | |
111 | 122 | private ExecutorService tsCallBackExecutor; |
112 | 123 | private ExecutorService wsCallBackExecutor; |
113 | 124 | |
... | ... | @@ -213,6 +224,13 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio |
213 | 224 | } |
214 | 225 | |
215 | 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 | 234 | public void onNewRemoteSubscription(ServerAddress serverAddress, byte[] data) { |
217 | 235 | ClusterAPIProtos.SubscriptionProto proto; |
218 | 236 | try { |
... | ... | @@ -364,9 +382,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio |
364 | 382 | e -> log.error("Failed to fetch missed updates.", e), tsCallBackExecutor); |
365 | 383 | } else if (subscription.getType() == TelemetryFeature.TIMESERIES) { |
366 | 384 | long curTs = System.currentTimeMillis(); |
367 | - List<TsKvQuery> queries = new ArrayList<>(); | |
385 | + List<ReadTsKvQuery> queries = new ArrayList<>(); | |
368 | 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 | 390 | DonAsynchron.withCallback(tsService.findAll(entityId, queries), | ... | ... |
... | ... | @@ -30,10 +30,10 @@ import org.thingsboard.server.common.data.id.EntityId; |
30 | 30 | import org.thingsboard.server.common.data.id.EntityIdFactory; |
31 | 31 | import org.thingsboard.server.common.data.kv.Aggregation; |
32 | 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 | 34 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
35 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | |
35 | 36 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
36 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | |
37 | 37 | import org.thingsboard.server.dao.attributes.AttributesService; |
38 | 38 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
39 | 39 | import org.thingsboard.server.service.security.AccessValidator; |
... | ... | @@ -251,7 +251,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi |
251 | 251 | } |
252 | 252 | EntityId entityId = EntityIdFactory.getByTypeAndId(cmd.getEntityType(), cmd.getEntityId()); |
253 | 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 | 255 | .collect(Collectors.toList()); |
256 | 256 | |
257 | 257 | FutureCallback<List<TsKvEntry>> callback = new FutureCallback<List<TsKvEntry>>() { |
... | ... | @@ -337,7 +337,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi |
337 | 337 | log.debug("[{}] fetching timeseries data for last {} ms for keys: ({}) for device : {}", sessionId, cmd.getTimeWindow(), cmd.getKeys(), entityId); |
338 | 338 | startTs = cmd.getStartTs(); |
339 | 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 | 341 | getLimit(cmd.getLimit()), getAggregation(cmd.getAgg()))).collect(Collectors.toList()); |
342 | 342 | |
343 | 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 | 23 | private final String key; |
24 | 24 | private final long startTs; |
25 | 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 | 28 | this.key = key; |
32 | 29 | this.startTs = startTs; |
33 | 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 | 16 | package org.thingsboard.server.dao.relation; |
17 | 17 | |
18 | 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 | 20 | import lombok.extern.slf4j.Slf4j; |
23 | 21 | import org.springframework.beans.factory.annotation.Autowired; |
24 | 22 | import org.springframework.cache.Cache; |
... | ... | @@ -41,7 +39,6 @@ import org.thingsboard.server.dao.exception.DataValidationException; |
41 | 39 | |
42 | 40 | import javax.annotation.Nullable; |
43 | 41 | import java.util.ArrayList; |
44 | -import java.util.Arrays; | |
45 | 42 | import java.util.Collections; |
46 | 43 | import java.util.HashSet; |
47 | 44 | import java.util.List; |
... | ... | @@ -94,10 +91,10 @@ public class BaseRelationService implements RelationService { |
94 | 91 | |
95 | 92 | @Caching(evict = { |
96 | 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 | 99 | @Override |
103 | 100 | public boolean saveRelation(EntityRelation relation) { |
... | ... | @@ -108,10 +105,10 @@ public class BaseRelationService implements RelationService { |
108 | 105 | |
109 | 106 | @Caching(evict = { |
110 | 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 | 113 | @Override |
117 | 114 | public ListenableFuture<Boolean> saveRelationAsync(EntityRelation relation) { |
... | ... | @@ -122,10 +119,10 @@ public class BaseRelationService implements RelationService { |
122 | 119 | |
123 | 120 | @Caching(evict = { |
124 | 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 | 127 | @Override |
131 | 128 | public boolean deleteRelation(EntityRelation relation) { |
... | ... | @@ -136,10 +133,10 @@ public class BaseRelationService implements RelationService { |
136 | 133 | |
137 | 134 | @Caching(evict = { |
138 | 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 | 141 | @Override |
145 | 142 | public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) { |
... | ... | @@ -150,10 +147,10 @@ public class BaseRelationService implements RelationService { |
150 | 147 | |
151 | 148 | @Caching(evict = { |
152 | 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 | 155 | @Override |
159 | 156 | public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
... | ... | @@ -164,10 +161,10 @@ public class BaseRelationService implements RelationService { |
164 | 161 | |
165 | 162 | @Caching(evict = { |
166 | 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 | 169 | @Override |
173 | 170 | public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
... | ... | @@ -250,30 +247,36 @@ public class BaseRelationService implements RelationService { |
250 | 247 | fromTypeAndTypeGroup.add(relation.getFrom()); |
251 | 248 | fromTypeAndTypeGroup.add(relation.getType()); |
252 | 249 | fromTypeAndTypeGroup.add(relation.getTypeGroup()); |
250 | + fromTypeAndTypeGroup.add(EntitySearchDirection.FROM.name()); | |
253 | 251 | cache.evict(fromTypeAndTypeGroup); |
254 | 252 | |
255 | 253 | List<Object> fromAndTypeGroup = new ArrayList<>(); |
256 | 254 | fromAndTypeGroup.add(relation.getFrom()); |
257 | 255 | fromAndTypeGroup.add(relation.getTypeGroup()); |
256 | + fromAndTypeGroup.add(EntitySearchDirection.FROM.name()); | |
258 | 257 | cache.evict(fromAndTypeGroup); |
259 | 258 | |
260 | 259 | List<Object> toAndTypeGroup = new ArrayList<>(); |
261 | 260 | toAndTypeGroup.add(relation.getTo()); |
262 | 261 | toAndTypeGroup.add(relation.getTypeGroup()); |
262 | + toAndTypeGroup.add(EntitySearchDirection.TO.name()); | |
263 | 263 | cache.evict(toAndTypeGroup); |
264 | 264 | |
265 | 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 | 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 | 274 | @Override |
274 | 275 | public List<EntityRelation> findByFrom(EntityId from, RelationTypeGroup typeGroup) { |
276 | + validate(from); | |
277 | + validateTypeGroup(typeGroup); | |
275 | 278 | try { |
276 | - return findByFromAsync(from, typeGroup).get(); | |
279 | + return relationDao.findAllByFrom(from, typeGroup).get(); | |
277 | 280 | } catch (InterruptedException | ExecutionException e) { |
278 | 281 | throw new RuntimeException(e); |
279 | 282 | } |
... | ... | @@ -284,7 +287,29 @@ public class BaseRelationService implements RelationService { |
284 | 287 | log.trace("Executing findByFrom [{}][{}]", from, typeGroup); |
285 | 288 | validate(from); |
286 | 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 | 315 | @Override |
... | ... | @@ -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 | 334 | @Override |
310 | 335 | public List<EntityRelation> findByFromAndType(EntityId from, String relationType, RelationTypeGroup typeGroup) { |
311 | 336 | try { |
... | ... | @@ -324,11 +349,13 @@ public class BaseRelationService implements RelationService { |
324 | 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 | 353 | @Override |
329 | 354 | public List<EntityRelation> findByTo(EntityId to, RelationTypeGroup typeGroup) { |
355 | + validate(to); | |
356 | + validateTypeGroup(typeGroup); | |
330 | 357 | try { |
331 | - return findByToAsync(to, typeGroup).get(); | |
358 | + return relationDao.findAllByTo(to, typeGroup).get(); | |
332 | 359 | } catch (InterruptedException | ExecutionException e) { |
333 | 360 | throw new RuntimeException(e); |
334 | 361 | } |
... | ... | @@ -339,7 +366,29 @@ public class BaseRelationService implements RelationService { |
339 | 366 | log.trace("Executing findByTo [{}][{}]", to, typeGroup); |
340 | 367 | validate(to); |
341 | 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 | 394 | @Override |
... | ... | @@ -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 | 424 | @Override |
376 | 425 | public List<EntityRelation> findByToAndType(EntityId to, String relationType, RelationTypeGroup typeGroup) { |
377 | 426 | try { | ... | ... |
... | ... | @@ -31,9 +31,10 @@ import org.thingsboard.server.common.data.UUIDConverter; |
31 | 31 | import org.thingsboard.server.common.data.id.EntityId; |
32 | 32 | import org.thingsboard.server.common.data.kv.Aggregation; |
33 | 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 | 36 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
35 | 37 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
36 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | |
37 | 38 | import org.thingsboard.server.dao.DaoUtil; |
38 | 39 | import org.thingsboard.server.dao.model.sql.TsKvEntity; |
39 | 40 | import org.thingsboard.server.dao.model.sql.TsKvLatestCompositeKey; |
... | ... | @@ -102,7 +103,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp |
102 | 103 | } |
103 | 104 | |
104 | 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 | 107 | List<ListenableFuture<List<TsKvEntry>>> futures = queries |
107 | 108 | .stream() |
108 | 109 | .map(query -> findAllAsync(entityId, query)) |
... | ... | @@ -121,7 +122,7 @@ public class JpaTimeseriesDao extends JpaAbstractDaoListeningExecutorService imp |
121 | 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 | 126 | if (query.getAggregation() == Aggregation.NONE) { |
126 | 127 | return findAllAsyncWithLimit(entityId, query); |
127 | 128 | } else { |
... | ... | @@ -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 | 233 | return Futures.immediateFuture( |
233 | 234 | DaoUtil.convertDataList( |
234 | 235 | tsKvRepository.findAllWithLimit( |
... | ... | @@ -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 | 340 | @PreDestroy |
310 | 341 | void onDestroy() { |
311 | 342 | if (insertService != null) { | ... | ... |
... | ... | @@ -16,10 +16,12 @@ |
16 | 16 | package org.thingsboard.server.dao.sql.timeseries; |
17 | 17 | |
18 | 18 | import org.springframework.data.domain.Pageable; |
19 | +import org.springframework.data.jpa.repository.Modifying; | |
19 | 20 | import org.springframework.data.jpa.repository.Query; |
20 | 21 | import org.springframework.data.repository.CrudRepository; |
21 | 22 | import org.springframework.data.repository.query.Param; |
22 | 23 | import org.springframework.scheduling.annotation.Async; |
24 | +import org.springframework.transaction.annotation.Transactional; | |
23 | 25 | import org.thingsboard.server.common.data.EntityType; |
24 | 26 | import org.thingsboard.server.dao.model.sql.TsKvCompositeKey; |
25 | 27 | import org.thingsboard.server.dao.model.sql.TsKvEntity; |
... | ... | @@ -41,6 +43,17 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite |
41 | 43 | @Param("endTs") long endTs, |
42 | 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 | 57 | @Async |
45 | 58 | @Query("SELECT new TsKvEntity(MAX(tskv.strValue), MAX(tskv.longValue), MAX(tskv.doubleValue)) FROM TsKvEntity tskv " + |
46 | 59 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + |
... | ... | @@ -56,30 +69,30 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite |
56 | 69 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + |
57 | 70 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") |
58 | 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 | 77 | @Async |
65 | 78 | @Query("SELECT new TsKvEntity(COUNT(tskv.booleanValue), COUNT(tskv.strValue), COUNT(tskv.longValue), COUNT(tskv.doubleValue)) FROM TsKvEntity tskv " + |
66 | 79 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + |
67 | 80 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") |
68 | 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 | 87 | @Async |
75 | 88 | @Query("SELECT new TsKvEntity(AVG(tskv.longValue), AVG(tskv.doubleValue)) FROM TsKvEntity tskv " + |
76 | 89 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + |
77 | 90 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") |
78 | 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 | 98 | @Async |
... | ... | @@ -87,8 +100,8 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite |
87 | 100 | "WHERE tskv.entityId = :entityId AND tskv.entityType = :entityType " + |
88 | 101 | "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts < :endTs") |
89 | 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 | 25 | import org.thingsboard.server.common.data.EntityView; |
26 | 26 | import org.thingsboard.server.common.data.id.EntityId; |
27 | 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 | 31 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
30 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | |
31 | 32 | import org.thingsboard.server.dao.entityview.EntityViewService; |
32 | 33 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
33 | 34 | import org.thingsboard.server.dao.service.Validator; |
... | ... | @@ -46,6 +47,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank; |
46 | 47 | public class BaseTimeseriesService implements TimeseriesService { |
47 | 48 | |
48 | 49 | public static final int INSERTS_PER_ENTRY = 3; |
50 | + public static final int DELETES_PER_ENTRY = INSERTS_PER_ENTRY; | |
49 | 51 | |
50 | 52 | @Autowired |
51 | 53 | private TimeseriesDao timeseriesDao; |
... | ... | @@ -54,9 +56,9 @@ public class BaseTimeseriesService implements TimeseriesService { |
54 | 56 | private EntityViewService entityViewService; |
55 | 57 | |
56 | 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 | 60 | validate(entityId); |
59 | - queries.forEach(query -> validate(query)); | |
61 | + queries.forEach(BaseTimeseriesService::validate); | |
60 | 62 | if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { |
61 | 63 | EntityView entityView = entityViewService.findEntityViewById((EntityViewId) entityId); |
62 | 64 | return timeseriesDao.findAllAsync(entityView.getEntityId(), updateQueriesForEntityView(entityView, queries)); |
... | ... | @@ -69,13 +71,14 @@ public class BaseTimeseriesService implements TimeseriesService { |
69 | 71 | validate(entityId); |
70 | 72 | List<ListenableFuture<TsKvEntry>> futures = Lists.newArrayListWithExpectedSize(keys.size()); |
71 | 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 | 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 | 82 | return Futures.allAsList(futures); |
80 | 83 | } |
81 | 84 | |
... | ... | @@ -129,8 +132,8 @@ public class BaseTimeseriesService implements TimeseriesService { |
129 | 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 | 137 | entityView.getKeys().getTimeseries() |
135 | 138 | .forEach(viewKey -> queries |
136 | 139 | .forEach(query -> { |
... | ... | @@ -148,7 +151,6 @@ public class BaseTimeseriesService implements TimeseriesService { |
148 | 151 | return newQueries; |
149 | 152 | } |
150 | 153 | |
151 | - @Deprecated /*Will be a modified*/ | |
152 | 154 | private Collection<String> chooseKeysForEntityView(EntityView entityView, Collection<String> keys) { |
153 | 155 | Collection<String> newKeys = new ArrayList<>(); |
154 | 156 | entityView.getKeys().getTimeseries() |
... | ... | @@ -156,27 +158,53 @@ public class BaseTimeseriesService implements TimeseriesService { |
156 | 158 | .forEach(key -> { |
157 | 159 | if (key.equals(viewKey)) { |
158 | 160 | newKeys.add(key); |
159 | - }})); | |
161 | + } | |
162 | + })); | |
160 | 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 | 183 | private static void validate(EntityId entityId) { |
164 | 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 | 188 | if (query == null) { |
169 | - throw new IncorrectParameterException("TsKvQuery can't be null"); | |
189 | + throw new IncorrectParameterException("ReadTsKvQuery can't be null"); | |
170 | 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 | 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 | 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 | 210 | private static void checkForNonEntityView(EntityId entityId) throws Exception { | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import com.datastax.driver.core.PreparedStatement; |
20 | 20 | import com.datastax.driver.core.ResultSet; |
21 | 21 | import com.datastax.driver.core.ResultSetFuture; |
22 | 22 | import com.datastax.driver.core.Row; |
23 | +import com.datastax.driver.core.Statement; | |
23 | 24 | import com.datastax.driver.core.querybuilder.QueryBuilder; |
24 | 25 | import com.datastax.driver.core.querybuilder.Select; |
25 | 26 | import com.google.common.base.Function; |
... | ... | @@ -34,16 +35,17 @@ import org.springframework.core.env.Environment; |
34 | 35 | import org.springframework.stereotype.Component; |
35 | 36 | import org.thingsboard.server.common.data.id.EntityId; |
36 | 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 | 39 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
39 | 40 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
40 | 41 | import org.thingsboard.server.common.data.kv.DataType; |
42 | +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; | |
41 | 43 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
42 | 44 | import org.thingsboard.server.common.data.kv.KvEntry; |
43 | 45 | import org.thingsboard.server.common.data.kv.LongDataEntry; |
46 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | |
44 | 47 | import org.thingsboard.server.common.data.kv.StringDataEntry; |
45 | 48 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
46 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | |
47 | 49 | import org.thingsboard.server.dao.model.ModelConstants; |
48 | 50 | import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao; |
49 | 51 | import org.thingsboard.server.dao.util.NoSqlDao; |
... | ... | @@ -54,11 +56,11 @@ import javax.annotation.PreDestroy; |
54 | 56 | import java.time.Instant; |
55 | 57 | import java.time.LocalDateTime; |
56 | 58 | import java.time.ZoneOffset; |
59 | +import java.util.ArrayList; | |
57 | 60 | import java.util.Arrays; |
61 | +import java.util.Collections; | |
58 | 62 | import java.util.List; |
59 | -import java.util.ArrayList; | |
60 | 63 | import java.util.Optional; |
61 | -import java.util.Collections; | |
62 | 64 | import java.util.stream.Collectors; |
63 | 65 | |
64 | 66 | import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; |
... | ... | @@ -76,8 +78,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
76 | 78 | public static final String GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID = "Generated query [{}] for entityType {} and entityId {}"; |
77 | 79 | public static final String SELECT_PREFIX = "SELECT "; |
78 | 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 | 83 | private static List<Long> FIXED_PARTITION = Arrays.asList(new Long[]{0L}); |
82 | 84 | |
83 | 85 | @Autowired |
... | ... | @@ -96,9 +98,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
96 | 98 | private PreparedStatement latestInsertStmt; |
97 | 99 | private PreparedStatement[] saveStmts; |
98 | 100 | private PreparedStatement[] saveTtlStmts; |
99 | - private PreparedStatement[] fetchStmts; | |
101 | + private PreparedStatement[] fetchStmtsAsc; | |
102 | + private PreparedStatement[] fetchStmtsDesc; | |
100 | 103 | private PreparedStatement findLatestStmt; |
101 | 104 | private PreparedStatement findAllLatestStmt; |
105 | + private PreparedStatement deleteStmt; | |
106 | + private PreparedStatement deletePartitionStmt; | |
102 | 107 | |
103 | 108 | private boolean isInstall() { |
104 | 109 | return environment.acceptsProfiles("install"); |
... | ... | @@ -108,7 +113,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
108 | 113 | public void init() { |
109 | 114 | super.startExecutor(); |
110 | 115 | if (!isInstall()) { |
111 | - getFetchStmt(Aggregation.NONE); | |
116 | + getFetchStmt(Aggregation.NONE, DESC_ORDER); | |
112 | 117 | Optional<TsPartitionDate> partition = TsPartitionDate.parse(partitioning); |
113 | 118 | if (partition.isPresent()) { |
114 | 119 | tsFormat = partition.get(); |
... | ... | @@ -125,7 +130,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
125 | 130 | } |
126 | 131 | |
127 | 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 | 134 | List<ListenableFuture<List<TsKvEntry>>> futures = queries.stream().map(query -> findAllAsync(entityId, query)).collect(Collectors.toList()); |
130 | 135 | return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() { |
131 | 136 | @Nullable |
... | ... | @@ -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 | 151 | if (query.getAggregation() == Aggregation.NONE) { |
147 | 152 | return findAllAsyncWithLimit(entityId, query); |
148 | 153 | } else { |
... | ... | @@ -152,7 +157,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
152 | 157 | while (stepTs < query.getEndTs()) { |
153 | 158 | long startTs = stepTs; |
154 | 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 | 161 | futures.add(findAndAggregateAsync(entityId, subQuery, toPartitionTs(startTs), toPartitionTs(endTs))); |
157 | 162 | stepTs = endTs; |
158 | 163 | } |
... | ... | @@ -171,7 +176,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
171 | 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 | 180 | if (isFixedPartitioning()) { //no need to fetch partitions from DB |
176 | 181 | return Futures.immediateFuture(FIXED_PARTITION); |
177 | 182 | } |
... | ... | @@ -179,11 +184,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
179 | 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 | 188 | long minPartition = toPartitionTs(query.getStartTs()); |
185 | 189 | long maxPartition = toPartitionTs(query.getEndTs()); |
186 | - | |
187 | 190 | final ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition); |
188 | 191 | final SimpleListenableFuture<List<TsKvEntry>> resultFuture = new SimpleListenableFuture<>(); |
189 | 192 | |
... | ... | @@ -212,7 +215,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
212 | 215 | if (cursor.isFull() || !cursor.hasNextPartition()) { |
213 | 216 | resultFuture.set(cursor.getData()); |
214 | 217 | } else { |
215 | - PreparedStatement proto = getFetchStmt(Aggregation.NONE); | |
218 | + PreparedStatement proto = getFetchStmt(Aggregation.NONE, cursor.getOrderBy()); | |
216 | 219 | BoundStatement stmt = proto.bind(); |
217 | 220 | stmt.setString(0, cursor.getEntityType()); |
218 | 221 | stmt.setUUID(1, cursor.getEntityId()); |
... | ... | @@ -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 | 244 | final Aggregation aggregation = query.getAggregation(); |
242 | 245 | final String key = query.getKey(); |
243 | 246 | final long startTs = query.getStartTs(); |
244 | 247 | final long endTs = query.getEndTs(); |
245 | 248 | final long ts = startTs + (endTs - startTs) / 2; |
246 | - | |
247 | - | |
248 | 249 | ListenableFuture<List<Long>> partitionsListFuture = getPartitionsFuture(query, entityId, minPartition, maxPartition); |
249 | 250 | ListenableFuture<List<ResultSet>> aggregationChunks = Futures.transformAsync(partitionsListFuture, |
250 | 251 | getFetchChunksAsyncFunction(entityId, key, aggregation, startTs, endTs), readResultsProcessingExecutor); |
... | ... | @@ -260,7 +261,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
260 | 261 | private AsyncFunction<List<Long>, List<ResultSet>> getFetchChunksAsyncFunction(EntityId entityId, String key, Aggregation aggregation, long startTs, long endTs) { |
261 | 262 | return partitions -> { |
262 | 263 | try { |
263 | - PreparedStatement proto = getFetchStmt(aggregation); | |
264 | + PreparedStatement proto = getFetchStmt(aggregation, DESC_ORDER); | |
264 | 265 | List<ResultSetFuture> futures = new ArrayList<>(partitions.size()); |
265 | 266 | for (Long partition : partitions) { |
266 | 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 | 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 | 565 | private List<TsKvEntry> convertResultToTsKvEntryList(List<Row> rows) { |
367 | 566 | List<TsKvEntry> entries = new ArrayList<>(rows.size()); |
368 | 567 | if (!rows.isEmpty()) { |
... | ... | @@ -458,28 +657,43 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem |
458 | 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 | 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 | 17 | |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | 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 | 22 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
21 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | |
22 | 23 | |
23 | 24 | import java.util.List; |
24 | 25 | |
... | ... | @@ -27,7 +28,7 @@ import java.util.List; |
27 | 28 | */ |
28 | 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 | 33 | ListenableFuture<TsKvEntry> findLatest(EntityId entityId, String key); |
33 | 34 | |
... | ... | @@ -38,4 +39,10 @@ public interface TimeseriesDao { |
38 | 39 | ListenableFuture<Void> savePartition(EntityId entityId, long tsKvEntryTs, String key, long ttl); |
39 | 40 | |
40 | 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 | 17 | |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | 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 | 22 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
21 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | |
22 | 23 | |
23 | 24 | import java.util.Collection; |
24 | 25 | import java.util.List; |
... | ... | @@ -28,7 +29,7 @@ import java.util.List; |
28 | 29 | */ |
29 | 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 | 34 | ListenableFuture<List<TsKvEntry>> findLatest(EntityId entityId, Collection<String> keys); |
34 | 35 | |
... | ... | @@ -37,4 +38,6 @@ public interface TimeseriesService { |
37 | 38 | ListenableFuture<List<Void>> save(EntityId entityId, TsKvEntry tsKvEntry); |
38 | 39 | |
39 | 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 | 16 | package org.thingsboard.server.dao.timeseries; |
17 | 17 | |
18 | 18 | import lombok.Getter; |
19 | +import org.thingsboard.server.common.data.kv.ReadTsKvQuery; | |
19 | 20 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
20 | -import org.thingsboard.server.common.data.kv.TsKvQuery; | |
21 | 21 | |
22 | 22 | import java.util.ArrayList; |
23 | 23 | import java.util.List; |
24 | 24 | import java.util.UUID; |
25 | 25 | |
26 | +import static org.thingsboard.server.dao.timeseries.CassandraBaseTimeseriesDao.DESC_ORDER; | |
27 | + | |
26 | 28 | /** |
27 | 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 | 33 | @Getter |
42 | 34 | private final List<TsKvEntry> data; |
35 | + @Getter | |
36 | + private String orderBy; | |
43 | 37 | |
44 | 38 | private int partitionIndex; |
45 | 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 | 45 | this.data = new ArrayList<>(); |
56 | 46 | this.currentLimit = baseQuery.getLimit(); |
57 | 47 | } |
58 | 48 | |
49 | + @Override | |
59 | 50 | public boolean hasNextPartition() { |
60 | - return partitionIndex >= 0; | |
51 | + return isDesc() ? partitionIndex >= 0 : partitionIndex <= partitions.size() - 1; | |
61 | 52 | } |
62 | 53 | |
63 | 54 | public boolean isFull() { |
64 | 55 | return currentLimit <= 0; |
65 | 56 | } |
66 | 57 | |
58 | + @Override | |
67 | 59 | public long getNextPartition() { |
68 | 60 | long partition = partitions.get(partitionIndex); |
69 | - partitionIndex--; | |
61 | + if (isDesc()) { | |
62 | + partitionIndex--; | |
63 | + } else { | |
64 | + partitionIndex++; | |
65 | + } | |
70 | 66 | return partition; |
71 | 67 | } |
72 | 68 | |
... | ... | @@ -78,4 +74,8 @@ public class TsKvQueryCursor { |
78 | 74 | currentLimit -= newData.size(); |
79 | 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 | 227 | Assert.assertTrue(relations.contains(relationA)); |
228 | 228 | Assert.assertTrue(relations.contains(relationB)); |
229 | 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 | 239 | @Test |
... | ... | @@ -253,6 +260,12 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { |
253 | 260 | Assert.assertEquals(2, relations.size()); |
254 | 261 | Assert.assertTrue(relations.contains(relationAB)); |
255 | 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 | 21 | import org.junit.Test; |
22 | 22 | import org.thingsboard.server.common.data.id.DeviceId; |
23 | 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 | 26 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
26 | 27 | import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
27 | 28 | import org.thingsboard.server.common.data.kv.DoubleDataEntry; |
... | ... | @@ -53,6 +54,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { |
53 | 54 | private static final String BOOLEAN_KEY = "booleanKey"; |
54 | 55 | |
55 | 56 | private static final long TS = 42L; |
57 | + private static final String DESC_ORDER = "DESC"; | |
56 | 58 | |
57 | 59 | KvEntry stringKvEntry = new StringDataEntry(STRING_KEY, "value"); |
58 | 60 | KvEntry longKvEntry = new LongDataEntry(LONG_KEY, Long.MAX_VALUE); |
... | ... | @@ -101,6 +103,26 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { |
101 | 103 | } |
102 | 104 | |
103 | 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 | 126 | public void testFindDeviceTsData() throws Exception { |
105 | 127 | DeviceId deviceId = new DeviceId(UUIDs.timeBased()); |
106 | 128 | List<TsKvEntry> entries = new ArrayList<>(); |
... | ... | @@ -114,7 +136,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { |
114 | 136 | entries.add(save(deviceId, 45000, 500)); |
115 | 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 | 140 | 60000, 20000, 3, Aggregation.NONE))).get(); |
119 | 141 | assertEquals(3, list.size()); |
120 | 142 | assertEquals(55000, list.get(0).getTs()); |
... | ... | @@ -126,7 +148,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { |
126 | 148 | assertEquals(35000, list.get(2).getTs()); |
127 | 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 | 152 | 60000, 20000, 3, Aggregation.AVG))).get(); |
131 | 153 | assertEquals(3, list.size()); |
132 | 154 | assertEquals(10000, list.get(0).getTs()); |
... | ... | @@ -138,7 +160,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { |
138 | 160 | assertEquals(50000, list.get(2).getTs()); |
139 | 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 | 164 | 60000, 20000, 3, Aggregation.SUM))).get(); |
143 | 165 | |
144 | 166 | assertEquals(3, list.size()); |
... | ... | @@ -151,7 +173,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { |
151 | 173 | assertEquals(50000, list.get(2).getTs()); |
152 | 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 | 177 | 60000, 20000, 3, Aggregation.MIN))).get(); |
156 | 178 | |
157 | 179 | assertEquals(3, list.size()); |
... | ... | @@ -164,7 +186,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { |
164 | 186 | assertEquals(50000, list.get(2).getTs()); |
165 | 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 | 190 | 60000, 20000, 3, Aggregation.MAX))).get(); |
169 | 191 | |
170 | 192 | assertEquals(3, list.size()); |
... | ... | @@ -177,7 +199,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest { |
177 | 199 | assertEquals(50000, list.get(2).getTs()); |
178 | 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 | 203 | 60000, 20000, 3, Aggregation.COUNT))).get(); |
182 | 204 | |
183 | 205 | assertEquals(3, list.size()); | ... | ... |
... | ... | @@ -40,6 +40,8 @@ public final class MqttClientConfig { |
40 | 40 | private Class<? extends Channel> channelClass = NioSocketChannel.class; |
41 | 41 | |
42 | 42 | private boolean reconnect = true; |
43 | + private long reconnectDelay = 1L; | |
44 | + private int maxBytesInMessage = 8092; | |
43 | 45 | |
44 | 46 | public MqttClientConfig() { |
45 | 47 | this(null); |
... | ... | @@ -146,4 +148,38 @@ public final class MqttClientConfig { |
146 | 148 | public void setReconnect(boolean reconnect) { |
147 | 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 | 155 | if (reconnect) { |
156 | 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 | 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 | 516 | ch.pipeline().addLast("mqttEncoder", MqttEncoder.INSTANCE); |
517 | 517 | ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(MqttClientImpl.this.clientConfig.getTimeoutSeconds(), MqttClientImpl.this.clientConfig.getTimeoutSeconds(), 0)); |
518 | 518 | ch.pipeline().addLast("mqttPingHandler", new MqttPingHandler(MqttClientImpl.this.clientConfig.getTimeoutSeconds())); | ... | ... |
... | ... | @@ -16,11 +16,14 @@ |
16 | 16 | package org.thingsboard.rule.engine.api; |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.FutureCallback; |
19 | +import org.thingsboard.server.common.data.id.DeviceId; | |
19 | 20 | import org.thingsboard.server.common.data.id.EntityId; |
21 | +import org.thingsboard.server.common.data.id.TenantId; | |
20 | 22 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
21 | 23 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
22 | 24 | |
23 | 25 | import java.util.List; |
26 | +import java.util.Set; | |
24 | 27 | |
25 | 28 | /** |
26 | 29 | * Created by ashvayka on 02.04.18. |
... | ... | @@ -41,4 +44,6 @@ public interface RuleEngineTelemetryService { |
41 | 44 | |
42 | 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 | 17 | |
18 | 18 | import com.google.gson.JsonParser; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | -import org.thingsboard.rule.engine.api.util.TbNodeUtils; | |
21 | 20 | import org.thingsboard.rule.engine.api.RuleNode; |
22 | 21 | import org.thingsboard.rule.engine.api.TbContext; |
23 | 22 | import org.thingsboard.rule.engine.api.TbNode; |
24 | 23 | import org.thingsboard.rule.engine.api.TbNodeConfiguration; |
25 | 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 | 29 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
27 | 30 | import org.thingsboard.server.common.data.plugin.ComponentType; |
28 | 31 | import org.thingsboard.server.common.msg.TbMsg; |
... | ... | @@ -62,6 +65,9 @@ public class TbMsgAttributesNode implements TbNode { |
62 | 65 | String src = msg.getData(); |
63 | 66 | Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src)).getAttributes(); |
64 | 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 | 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 | 4 | //# sourceMappingURL=rulenode-core-config.js.map |
\ No newline at end of file | ... | ... |
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 | 100 | "copy-webpack-plugin": "^3.0.1", |
101 | 101 | "cross-env": "^3.2.4", |
102 | 102 | "css-loader": "^0.25.0", |
103 | + "directory-tree": "^2.1.0", | |
103 | 104 | "eslint": "^3.4.0", |
104 | 105 | "eslint-config-angular": "^0.5.0", |
105 | 106 | "eslint-loader": "^1.5.0", |
... | ... | @@ -112,6 +113,7 @@ |
112 | 113 | "html-minifier-loader": "^1.3.4", |
113 | 114 | "html-webpack-plugin": "^2.30.1", |
114 | 115 | "img-loader": "^1.3.1", |
116 | + "jsonminify": "^0.4.1", | |
115 | 117 | "less": "^2.7.1", |
116 | 118 | "less-loader": "^2.2.3", |
117 | 119 | "ng-annotate-loader": "^0.1.1", |
... | ... | @@ -122,14 +124,18 @@ |
122 | 124 | "react-hot-loader": "^3.0.0-beta.6", |
123 | 125 | "sass-loader": "^4.0.2", |
124 | 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 | 133 | "url-loader": "^0.5.7", |
126 | 134 | "webpack": "^1.13.2", |
127 | 135 | "webpack-dev-middleware": "^1.6.1", |
128 | 136 | "webpack-dev-server": "^1.15.1", |
129 | 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 | 140 | "engine": "node >= 5.9.0", |
135 | 141 | "nyc": { | ... | ... |
... | ... | @@ -13,14 +13,16 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-alarm-details-panel { |
17 | - margin-left: 15px; | |
18 | - border: 1px solid #C0C0C0; | |
19 | 18 | height: 100%; |
19 | + margin-left: 15px; | |
20 | + border: 1px solid #c0c0c0; | |
21 | + | |
20 | 22 | #tb-alarm-details { |
21 | - min-width: 600px; | |
22 | - min-height: 200px; | |
23 | 23 | width: 100%; |
24 | + min-width: 600px; | |
24 | 25 | height: 100%; |
26 | + min-height: 200px; | |
25 | 27 | } |
26 | 28 | } | ... | ... |
... | ... | @@ -13,26 +13,27 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-alarm-container { |
17 | 18 | overflow-x: auto; |
18 | 19 | } |
19 | 20 | |
20 | 21 | md-list.tb-alarm-table { |
21 | - padding: 0px; | |
22 | 22 | min-width: 700px; |
23 | + padding: 0; | |
23 | 24 | |
24 | 25 | md-list-item { |
25 | - padding: 0px; | |
26 | + padding: 0; | |
26 | 27 | } |
27 | 28 | |
28 | 29 | .tb-row { |
29 | 30 | height: 48px; |
30 | - padding: 0px; | |
31 | + padding: 0; | |
31 | 32 | overflow: hidden; |
32 | 33 | } |
33 | 34 | |
34 | 35 | .tb-row:hover { |
35 | - background-color: #EEEEEE; | |
36 | + background-color: #eee; | |
36 | 37 | } |
37 | 38 | |
38 | 39 | .tb-header:hover { |
... | ... | @@ -41,9 +42,9 @@ md-list.tb-alarm-table { |
41 | 42 | |
42 | 43 | .tb-header { |
43 | 44 | .tb-cell { |
44 | - color: rgba(0,0,0,.54); | |
45 | 45 | font-size: 12px; |
46 | 46 | font-weight: 700; |
47 | + color: rgba(0, 0, 0, .54); | |
47 | 48 | white-space: nowrap; |
48 | 49 | background: none; |
49 | 50 | } |
... | ... | @@ -52,11 +53,12 @@ md-list.tb-alarm-table { |
52 | 53 | .tb-cell { |
53 | 54 | padding: 0 24px; |
54 | 55 | margin: auto 0; |
55 | - color: rgba(0,0,0,.87); | |
56 | + overflow: hidden; | |
56 | 57 | font-size: 13px; |
57 | - vertical-align: middle; | |
58 | + color: rgba(0, 0, 0, .87); | |
58 | 59 | text-align: left; |
59 | - overflow: hidden; | |
60 | + vertical-align: middle; | |
61 | + | |
60 | 62 | .md-button { |
61 | 63 | padding: 0; |
62 | 64 | margin: 0; |
... | ... | @@ -66,12 +68,11 @@ md-list.tb-alarm-table { |
66 | 68 | .tb-cell.tb-number { |
67 | 69 | text-align: right; |
68 | 70 | } |
69 | - | |
70 | 71 | } |
71 | 72 | |
72 | 73 | #tb-alarm-content { |
73 | - min-width: 400px; | |
74 | - min-height: 50px; | |
75 | 74 | width: 100%; |
75 | + min-width: 400px; | |
76 | 76 | height: 100%; |
77 | + min-height: 50px; | |
77 | 78 | } | ... | ... |
... | ... | @@ -13,10 +13,12 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 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 | 19 | width: 100%; |
20 | + min-width: 400px; | |
20 | 21 | height: 100%; |
21 | - border: 1px solid #C0C0C0; | |
22 | -} | |
\ No newline at end of file | ||
22 | + min-height: 50px; | |
23 | + border: 1px solid #c0c0c0; | |
24 | +} | ... | ... |
... | ... | @@ -13,17 +13,21 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-audit-logs { |
17 | 18 | background-color: #fff; |
19 | + | |
18 | 20 | .tb-audit-log-margin-18px { |
19 | 21 | margin-bottom: 18px; |
20 | 22 | } |
23 | + | |
21 | 24 | .tb-audit-log-toolbar { |
22 | 25 | font-size: 20px; |
23 | 26 | } |
27 | + | |
24 | 28 | md-input-container.tb-audit-log-search-input { |
25 | 29 | .md-errors-spacer { |
26 | - min-height: 0px; | |
30 | + min-height: 0; | |
27 | 31 | } |
28 | 32 | } |
29 | 33 | } |
... | ... | @@ -32,27 +36,26 @@ |
32 | 36 | overflow-x: auto; |
33 | 37 | } |
34 | 38 | |
35 | - | |
36 | - | |
37 | 39 | md-list.tb-audit-log-table { |
38 | - padding: 0px; | |
39 | 40 | min-width: 700px; |
41 | + padding: 0; | |
42 | + | |
40 | 43 | &.tb-audit-log-table-full { |
41 | 44 | min-width: 900px; |
42 | 45 | } |
43 | 46 | |
44 | 47 | md-list-item { |
45 | - padding: 0px; | |
48 | + padding: 0; | |
46 | 49 | } |
47 | 50 | |
48 | 51 | .tb-row { |
49 | 52 | height: 48px; |
50 | - padding: 0px; | |
53 | + padding: 0; | |
51 | 54 | overflow: hidden; |
52 | 55 | } |
53 | 56 | |
54 | 57 | .tb-row:hover { |
55 | - background-color: #EEEEEE; | |
58 | + background-color: #eee; | |
56 | 59 | } |
57 | 60 | |
58 | 61 | .tb-header:hover { |
... | ... | @@ -61,9 +64,9 @@ md-list.tb-audit-log-table { |
61 | 64 | |
62 | 65 | .tb-header { |
63 | 66 | .tb-cell { |
64 | - color: rgba(0,0,0,.54); | |
65 | 67 | font-size: 12px; |
66 | 68 | font-weight: 700; |
69 | + color: rgba(0, 0, 0, .54); | |
67 | 70 | white-space: nowrap; |
68 | 71 | background: none; |
69 | 72 | } |
... | ... | @@ -72,11 +75,12 @@ md-list.tb-audit-log-table { |
72 | 75 | .tb-cell { |
73 | 76 | padding: 0 24px; |
74 | 77 | margin: auto 0; |
75 | - color: rgba(0,0,0,.87); | |
78 | + overflow: hidden; | |
76 | 79 | font-size: 13px; |
77 | - vertical-align: middle; | |
80 | + color: rgba(0, 0, 0, .87); | |
78 | 81 | text-align: left; |
79 | - overflow: hidden; | |
82 | + vertical-align: middle; | |
83 | + | |
80 | 84 | .md-button { |
81 | 85 | padding: 0; |
82 | 86 | margin: 0; |
... | ... | @@ -86,5 +90,4 @@ md-list.tb-audit-log-table { |
86 | 90 | .tb-cell.tb-number { |
87 | 91 | text-align: right; |
88 | 92 | } |
89 | - | |
90 | 93 | } | ... | ... |
... | ... | @@ -13,16 +13,19 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-dashboard-autocomplete { |
17 | 18 | .tb-not-found { |
18 | 19 | display: block; |
19 | - line-height: 1.5; | |
20 | 20 | height: 48px; |
21 | + line-height: 1.5; | |
21 | 22 | } |
23 | + | |
22 | 24 | .tb-dashboard-item { |
23 | 25 | display: block; |
24 | 26 | height: 48px; |
25 | 27 | } |
28 | + | |
26 | 29 | li { |
27 | 30 | height: auto !important; |
28 | 31 | white-space: normal !important; | ... | ... |
... | ... | @@ -13,18 +13,20 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -@import '../../scss/constants'; | |
16 | +@import "../../scss/constants"; | |
17 | 17 | |
18 | 18 | tb-dashboard-select { |
19 | 19 | min-width: 52px; |
20 | + | |
20 | 21 | md-select { |
21 | - pointer-events: all; | |
22 | 22 | max-width: 300px; |
23 | + pointer-events: all; | |
23 | 24 | } |
24 | 25 | } |
25 | 26 | |
26 | 27 | .tb-dashboard-select { |
27 | 28 | min-height: 32px; |
29 | + | |
28 | 30 | span { |
29 | 31 | pointer-events: all; |
30 | 32 | cursor: pointer; |
... | ... | @@ -38,23 +40,27 @@ tb-dashboard-select { |
38 | 40 | } |
39 | 41 | |
40 | 42 | .tb-dashboard-select-panel { |
43 | + min-width: 300px; | |
44 | + max-width: 320px; | |
41 | 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 | 55 | @media (min-height: 350px) { |
43 | 56 | max-height: 250px; |
44 | 57 | } |
45 | - max-width: 320px; | |
58 | + | |
46 | 59 | @media (min-width: $layout-breakpoint-xs) { |
47 | 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 | 63 | md-content { |
58 | 64 | background-color: #fff; |
59 | 65 | } |
60 | -} | |
\ No newline at end of file | ||
66 | +} | ... | ... |
... | ... | @@ -21,6 +21,7 @@ div.tb-widget { |
21 | 21 | margin: 0; |
22 | 22 | overflow: hidden; |
23 | 23 | outline: none; |
24 | + | |
24 | 25 | @include transition(all .2s ease-in-out); |
25 | 26 | |
26 | 27 | .tb-widget-title { |
... | ... | @@ -32,7 +33,7 @@ div.tb-widget { |
32 | 33 | |
33 | 34 | tb-timewindow { |
34 | 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 | 45 | margin: 0; |
45 | 46 | |
46 | 47 | .md-button.md-icon-button { |
47 | - margin: 0 !important; | |
48 | - padding: 0 !important; | |
49 | - line-height: 20px; | |
50 | 48 | width: 32px; |
51 | - height: 32px; | |
52 | 49 | min-width: 32px; |
50 | + height: 32px; | |
53 | 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 | 58 | width: 20px; |
56 | - height: 20px; | |
57 | 59 | min-width: 20px; |
60 | + height: 20px; | |
58 | 61 | min-height: 20px; |
59 | 62 | font-size: 20px; |
60 | 63 | } |
... | ... | @@ -63,8 +66,8 @@ div.tb-widget { |
63 | 66 | |
64 | 67 | .tb-widget-content { |
65 | 68 | tb-widget { |
66 | - width: 100%; | |
67 | 69 | position: relative; |
70 | + width: 100%; | |
68 | 71 | } |
69 | 72 | } |
70 | 73 | } |
... | ... | @@ -75,40 +78,41 @@ div.tb-widget.tb-highlighted { |
75 | 78 | } |
76 | 79 | |
77 | 80 | div.tb-widget.tb-not-highlighted { |
78 | - opacity: 0.5; | |
81 | + opacity: .5; | |
79 | 82 | } |
80 | 83 | |
81 | 84 | tb-dashboard { |
82 | 85 | position: absolute; |
83 | 86 | top: 0; |
84 | - left: 0; | |
85 | 87 | right: 0; |
86 | 88 | bottom: 0; |
89 | + left: 0; | |
87 | 90 | } |
88 | 91 | |
89 | 92 | md-content.tb-dashboard-content { |
90 | 93 | position: absolute; |
91 | 94 | top: 0; |
92 | - left: 0; | |
93 | 95 | right: 0; |
94 | 96 | bottom: 0; |
95 | - outline: none; | |
97 | + left: 0; | |
96 | 98 | background: none; |
99 | + outline: none; | |
100 | + | |
97 | 101 | .gridster-item { |
98 | - @include transition(none); | |
102 | + @include transition(none); | |
99 | 103 | } |
100 | 104 | } |
101 | 105 | |
102 | 106 | .tb-widget-error-container { |
103 | 107 | position: absolute; |
104 | - background-color: #fff; | |
105 | 108 | width: 100%; |
106 | 109 | height: 100%; |
110 | + background-color: #fff; | |
107 | 111 | } |
108 | 112 | |
109 | 113 | .tb-widget-error-msg { |
110 | - color: red; | |
114 | + padding: 5px; | |
111 | 115 | font-size: 16px; |
116 | + color: #f00; | |
112 | 117 | word-wrap: break-word; |
113 | - padding: 5px; | |
114 | 118 | } | ... | ... |
... | ... | @@ -13,9 +13,11 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-datakey-config { |
17 | 18 | min-width: 500px !important; |
18 | 19 | min-height: 500px !important; |
20 | + | |
19 | 21 | md-content { |
20 | 22 | background-color: #fff; |
21 | 23 | } | ... | ... |
... | ... | @@ -13,17 +13,22 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 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 | 22 | .tb-not-found { |
20 | 23 | display: block; |
21 | - line-height: 1.5; | |
22 | 24 | height: 48px; |
25 | + line-height: 1.5; | |
26 | + | |
23 | 27 | .tb-no-entries { |
24 | 28 | line-height: 48px; |
25 | 29 | } |
26 | 30 | } |
31 | + | |
27 | 32 | li { |
28 | 33 | height: auto !important; |
29 | 34 | white-space: normal !important; |
... | ... | @@ -32,13 +37,14 @@ |
32 | 37 | |
33 | 38 | tb-datasource-entity { |
34 | 39 | @media (min-width: $layout-breakpoint-sm) { |
35 | - padding-left: 4px; | |
36 | 40 | padding-right: 4px; |
41 | + padding-left: 4px; | |
37 | 42 | } |
43 | + | |
38 | 44 | tb-entity-alias-select { |
39 | 45 | @media (min-width: $layout-breakpoint-sm) { |
40 | 46 | width: 200px; |
41 | 47 | max-width: 200px; |
42 | 48 | } |
43 | 49 | } |
44 | -} | |
\ No newline at end of file | ||
50 | +} | ... | ... |
... | ... | @@ -13,7 +13,7 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -@import '../../scss/constants'; | |
16 | +@import "../../scss/constants"; | |
17 | 17 | |
18 | 18 | .tb-datasource-func { |
19 | 19 | @media (min-width: $layout-breakpoint-sm) { |
... | ... | @@ -22,19 +22,22 @@ |
22 | 22 | |
23 | 23 | md-input-container.tb-datasource-name { |
24 | 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 | 31 | .tb-not-found { |
31 | 32 | display: block; |
32 | - line-height: 1.5; | |
33 | 33 | height: 48px; |
34 | + line-height: 1.5; | |
35 | + | |
34 | 36 | .tb-no-entries { |
35 | 37 | line-height: 48px; |
36 | 38 | } |
37 | 39 | } |
40 | + | |
38 | 41 | li { |
39 | 42 | height: auto !important; |
40 | 43 | white-space: normal !important; | ... | ... |
... | ... | @@ -13,39 +13,43 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 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 | 33 | @mixin tb-checkered-bg() { |
32 | 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 | 38 | background-position: 0 0, 4px 4px; |
39 | + background-size: 8px 8px; | |
37 | 40 | } |
38 | 41 | |
39 | 42 | .tb-color-preview { |
40 | - content: ''; | |
41 | - min-width: 24px; | |
43 | + position: relative; | |
42 | 44 | width: 24px; |
45 | + min-width: 24px; | |
43 | 46 | height: 24px; |
47 | + overflow: hidden; | |
48 | + content: ""; | |
44 | 49 | border: 2px solid #fff; |
45 | 50 | border-radius: 50%; |
46 | 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 | 53 | @include tb-checkered-bg(); |
50 | 54 | |
51 | 55 | .tb-color-result { |
... | ... | @@ -60,6 +64,7 @@ |
60 | 64 | text-overflow: ellipsis; |
61 | 65 | white-space: nowrap; |
62 | 66 | } |
67 | + | |
63 | 68 | .tb-chip-separator { |
64 | 69 | white-space: pre; |
65 | 70 | } | ... | ... |
... | ... | @@ -13,13 +13,16 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | tb-datetime-period { |
17 | 18 | md-input-container { |
18 | - margin-bottom: 0px; | |
19 | + margin-bottom: 0; | |
20 | + | |
19 | 21 | .md-errors-spacer { |
20 | - min-height: 0px; | |
22 | + min-height: 0; | |
21 | 23 | } |
22 | 24 | } |
25 | + | |
23 | 26 | mdp-date-picker { |
24 | 27 | .md-input { |
25 | 28 | width: 150px !important; | ... | ... |
... | ... | @@ -13,47 +13,51 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -@import '../../scss/constants'; | |
16 | +@import "../../scss/constants"; | |
17 | 17 | |
18 | 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 | 20 | margin: 20px 8px 0 0; |
26 | 21 | overflow: hidden; |
22 | + font-size: 1rem; | |
23 | + font-weight: 400; | |
27 | 24 | text-overflow: ellipsis; |
25 | + text-transform: uppercase; | |
28 | 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 | 33 | .tb-details-subtitle { |
33 | - font-size: 1.000rem; | |
34 | + width: inherit; | |
34 | 35 | margin: 10px 0; |
35 | - opacity: 0.8; | |
36 | 36 | overflow: hidden; |
37 | + font-size: 1rem; | |
37 | 38 | text-overflow: ellipsis; |
38 | 39 | white-space: nowrap; |
39 | - width: inherit; | |
40 | + opacity: .8; | |
40 | 41 | } |
41 | 42 | |
42 | 43 | md-sidenav.tb-sidenav-details { |
43 | 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 | 50 | width: 100% !important; |
49 | 51 | max-width: 100% !important; |
50 | - z-index: 59 !important; | |
52 | + | |
51 | 53 | @media (min-width: $layout-breakpoint-gt-sm) { |
52 | 54 | width: 80% !important; |
53 | 55 | } |
56 | + | |
54 | 57 | @media (min-width: $layout-breakpoint-gt-md) { |
55 | 58 | width: 65% !important; |
56 | 59 | } |
60 | + | |
57 | 61 | tb-dashboard { |
58 | 62 | md-content { |
59 | 63 | background-color: $primary-hue-3; | ... | ... |
... | ... | @@ -13,15 +13,18 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-entity-alias-autocomplete { |
17 | 18 | .tb-not-found { |
18 | 19 | display: block; |
19 | - line-height: 1.5; | |
20 | 20 | height: 48px; |
21 | + line-height: 1.5; | |
22 | + | |
21 | 23 | .tb-no-entries { |
22 | 24 | line-height: 48px; |
23 | 25 | } |
24 | 26 | } |
27 | + | |
25 | 28 | li { |
26 | 29 | height: auto !important; |
27 | 30 | white-space: normal !important; | ... | ... |
... | ... | @@ -16,37 +16,38 @@ |
16 | 16 | @import "../../scss/constants"; |
17 | 17 | |
18 | 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 | 19 | position: fixed !important; |
28 | 20 | top: 0; |
29 | 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 | 30 | .tb-fullscreen-parent { |
33 | - background-color: $gray; | |
34 | - width: 100%; | |
35 | - height: 100%; | |
36 | 31 | position: fixed; |
37 | 32 | top: 0; |
38 | 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 | 41 | background: #ccc; |
43 | - opacity: 0.85; | |
42 | + opacity: .85; | |
43 | + | |
44 | 44 | ng-md-icon { |
45 | 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 | 51 | position: absolute; |
51 | 52 | top: 10px; |
52 | 53 | right: 10px; | ... | ... |
... | ... | @@ -21,15 +21,19 @@ |
21 | 21 | |
22 | 22 | .tb-card-item { |
23 | 23 | @include transition(all .2s ease-in-out); |
24 | + | |
24 | 25 | md-card-content { |
25 | - padding-top: 0px; | |
26 | 26 | max-height: 53px; |
27 | + padding-top: 0; | |
27 | 28 | } |
29 | + | |
28 | 30 | md-card-title { |
29 | 31 | width: 100%; |
32 | + | |
30 | 33 | md-card-title-text { |
31 | - max-height: 32px; | |
32 | 34 | min-width: 50%; |
35 | + max-height: 32px; | |
36 | + | |
33 | 37 | .md-headline { |
34 | 38 | overflow: hidden; |
35 | 39 | text-overflow: ellipsis; |
... | ... | @@ -40,14 +44,15 @@ |
40 | 44 | } |
41 | 45 | |
42 | 46 | .tb-current-item { |
43 | - opacity: 0.5; | |
47 | + opacity: .5; | |
48 | + | |
44 | 49 | @include transform(scale(1.05)); |
45 | 50 | } |
46 | 51 | |
47 | 52 | #tb-vertical-container { |
48 | 53 | position: absolute; |
49 | 54 | top: 0; |
50 | - left: 0; | |
51 | 55 | right: 0; |
52 | 56 | bottom: 0; |
57 | + left: 0; | |
53 | 58 | } | ... | ... |
... | ... | @@ -13,11 +13,14 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | tb-js-func { |
17 | 18 | position: relative; |
19 | + | |
18 | 20 | .tb-disabled { |
19 | - color: rgba(0,0,0,0.38); | |
21 | + color: rgba(0, 0, 0, .38); | |
20 | 22 | } |
23 | + | |
21 | 24 | .fill-height { |
22 | 25 | height: 100%; |
23 | 26 | } |
... | ... | @@ -25,25 +28,27 @@ tb-js-func { |
25 | 28 | |
26 | 29 | .tb-js-func-toolbar { |
27 | 30 | .md-button.tidy { |
28 | - color: #7B7B7B; | |
29 | 31 | min-width: 32px; |
30 | 32 | min-height: 15px; |
31 | - line-height: 15px; | |
32 | - font-size: 0.800rem; | |
33 | - margin: 0 5px 0 0; | |
34 | 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 | 42 | .tb-js-func-panel { |
40 | - margin-left: 15px; | |
41 | - border: 1px solid #C0C0C0; | |
42 | 43 | height: calc(100% - 80px); |
44 | + margin-left: 15px; | |
45 | + border: 1px solid #c0c0c0; | |
46 | + | |
43 | 47 | #tb-javascript-input { |
44 | - min-width: 200px; | |
45 | 48 | width: 100%; |
49 | + min-width: 200px; | |
46 | 50 | height: 100%; |
51 | + | |
47 | 52 | &:not(.fill-height) { |
48 | 53 | min-height: 200px; |
49 | 54 | } | ... | ... |
... | ... | @@ -13,8 +13,10 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | tb-json-content { |
17 | 18 | position: relative; |
19 | + | |
18 | 20 | .fill-height { |
19 | 21 | height: 100%; |
20 | 22 | } |
... | ... | @@ -22,25 +24,27 @@ tb-json-content { |
22 | 24 | |
23 | 25 | .tb-json-content-toolbar { |
24 | 26 | .md-button.tidy { |
25 | - color: #7B7B7B; | |
26 | 27 | min-width: 32px; |
27 | 28 | min-height: 15px; |
28 | - line-height: 15px; | |
29 | - font-size: 0.800rem; | |
30 | - margin: 0 5px 0 0; | |
31 | 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 | 38 | .tb-json-content-panel { |
37 | - margin-left: 15px; | |
38 | - border: 1px solid #C0C0C0; | |
39 | 39 | height: 100%; |
40 | + margin-left: 15px; | |
41 | + border: 1px solid #c0c0c0; | |
42 | + | |
40 | 43 | #tb-json-input { |
41 | - min-width: 200px; | |
42 | 44 | width: 100%; |
45 | + min-width: 200px; | |
43 | 46 | height: 100%; |
47 | + | |
44 | 48 | &:not(.fill-height) { |
45 | 49 | min-height: 200px; |
46 | 50 | } | ... | ... |
... | ... | @@ -13,7 +13,8 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | tb-json-form { |
17 | - overflow: auto; | |
18 | 18 | padding-bottom: 14px !important; |
19 | -} | |
\ No newline at end of file | ||
19 | + overflow: auto; | |
20 | +} | ... | ... |
... | ... | @@ -13,21 +13,25 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | tb-json-object-edit { |
17 | 18 | position: relative; |
19 | + | |
18 | 20 | .fill-height { |
19 | 21 | height: 100%; |
20 | 22 | } |
21 | 23 | } |
22 | 24 | |
23 | 25 | .tb-json-object-panel { |
24 | - margin-left: 15px; | |
25 | - border: 1px solid #C0C0C0; | |
26 | 26 | height: 100%; |
27 | + margin-left: 15px; | |
28 | + border: 1px solid #c0c0c0; | |
29 | + | |
27 | 30 | #tb-json-input { |
28 | - min-width: 200px; | |
29 | 31 | width: 100%; |
32 | + min-width: 200px; | |
30 | 33 | height: 100%; |
34 | + | |
31 | 35 | &:not(.fill-height) { |
32 | 36 | min-height: 200px; |
33 | 37 | } | ... | ... |
... | ... | @@ -13,14 +13,16 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-kv-map { |
17 | 18 | span.no-data-found { |
18 | 19 | position: relative; |
20 | + display: flex; | |
19 | 21 | height: 40px; |
20 | 22 | text-transform: uppercase; |
21 | - display: flex; | |
23 | + | |
22 | 24 | &.disabled { |
23 | - color: rgba(0,0,0,0.38); | |
25 | + color: rgba(0, 0, 0, .38); | |
24 | 26 | } |
25 | 27 | } |
26 | -} | |
\ No newline at end of file | ||
28 | +} | ... | ... |
... | ... | @@ -13,6 +13,7 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .md-panel { |
17 | 18 | &.tb-legend-config-panel { |
18 | 19 | position: absolute; |
... | ... | @@ -20,21 +21,26 @@ |
20 | 21 | } |
21 | 22 | |
22 | 23 | .tb-legend-config-panel { |
23 | - max-height: 220px; | |
24 | 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 | 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 | 36 | height: 100%; |
33 | 37 | } |
38 | + | |
34 | 39 | md-content { |
35 | - background-color: #fff; | |
36 | 40 | overflow: hidden; |
41 | + background-color: #fff; | |
37 | 42 | } |
43 | + | |
38 | 44 | .md-padding { |
39 | 45 | padding: 0 16px; |
40 | 46 | } | ... | ... |
... | ... | @@ -13,39 +13,49 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | table.tb-legend { |
17 | 18 | width: 100%; |
18 | 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 | 26 | .tb-legend-header { |
23 | 27 | th { |
24 | - color: rgb(255,110,64); | |
25 | - white-space: nowrap; | |
26 | 28 | padding: 0 10px 1px 0; |
29 | + color: rgb(255, 110, 64); | |
30 | + white-space: nowrap; | |
27 | 31 | } |
28 | 32 | } |
33 | + | |
29 | 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 | 37 | padding: 2px 10px; |
38 | + white-space: nowrap; | |
33 | 39 | } |
40 | + | |
34 | 41 | .tb-legend-line { |
42 | + display: inline-block; | |
35 | 43 | width: 15px; |
36 | 44 | height: 3px; |
37 | - display: inline-block; | |
38 | 45 | vertical-align: middle; |
39 | 46 | } |
47 | + | |
40 | 48 | .tb-legend-label { |
41 | 49 | text-align: left; |
42 | 50 | outline: none; |
51 | + | |
43 | 52 | &.tb-horizontal { |
44 | 53 | width: 95%; |
45 | 54 | } |
55 | + | |
46 | 56 | &.tb-hidden-label { |
47 | 57 | text-decoration: line-through; |
48 | - opacity: 0.6; | |
58 | + opacity: .6; | |
49 | 59 | } |
50 | 60 | } |
51 | 61 | } | ... | ... |
... | ... | @@ -13,14 +13,16 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 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 | -} | |
\ No newline at end of file | ||
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 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-material-icons-dialog { |
17 | 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 | 19 | width: 56px; |
20 | + height: 56px; | |
21 | + padding: 16px; | |
23 | 22 | margin: 10px; |
23 | + border: solid 1px #ffa500; | |
24 | + border-radius: 0%; | |
24 | 25 | } |
26 | + | |
25 | 27 | .tb-icons-load { |
26 | 28 | top: 64px; |
27 | - background: rgba(255,255,255,0.75); | |
28 | 29 | z-index: 3; |
30 | + background: rgba(255, 255, 255, .75); | |
29 | 31 | } |
30 | -} | |
\ No newline at end of file | ||
32 | +} | ... | ... |
... | ... | @@ -24,9 +24,11 @@ |
24 | 24 | } |
25 | 25 | |
26 | 26 | .tb-menu-toggle-list { |
27 | - overflow: hidden; | |
28 | 27 | position: relative; |
29 | 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 | 33 | @include transition-property(height); |
32 | 34 | } | ... | ... |
... | ... | @@ -13,30 +13,35 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 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 | -} | |
\ No newline at end of file | ||
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 | 15 | */ |
16 | 16 | @mixin tb-checkered-bg() { |
17 | 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 | 21 | background-position: 0 0, 4px 4px; |
22 | + background-size: 8px 8px; | |
22 | 23 | } |
23 | 24 | |
24 | 25 | .tb-color-preview { |
25 | - content: ''; | |
26 | + position: relative; | |
26 | 27 | width: 24px; |
27 | 28 | height: 24px; |
29 | + overflow: hidden; | |
30 | + content: ""; | |
28 | 31 | border: 2px solid #fff; |
29 | 32 | border-radius: 50%; |
30 | 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 | 35 | @include tb-checkered-bg(); |
34 | 36 | |
35 | 37 | .tb-color-result { | ... | ... |
... | ... | @@ -13,66 +13,69 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -$previewSize: 100px; | |
16 | +$previewSize: 100px !default; | |
17 | 17 | |
18 | 18 | .tb-image-select-container { |
19 | 19 | position: relative; |
20 | - height: $previewSize; | |
21 | 20 | width: 100%; |
21 | + height: $previewSize; | |
22 | 22 | } |
23 | 23 | |
24 | 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 | 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 | 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 | 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 | 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 | 15 | */ |
16 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-related-entity-autocomplete { |
17 | 18 | .tb-not-found { |
18 | 19 | display: block; |
19 | - line-height: 1.5; | |
20 | 20 | height: 48px; |
21 | + line-height: 1.5; | |
21 | 22 | } |
23 | + | |
22 | 24 | .tb-entity-item { |
23 | 25 | display: block; |
24 | 26 | height: 48px; |
25 | 27 | } |
28 | + | |
26 | 29 | li { |
27 | 30 | height: auto !important; |
28 | 31 | white-space: normal !important; | ... | ... |
... | ... | @@ -16,21 +16,22 @@ |
16 | 16 | @import "~compass-sass-mixins/lib/compass"; |
17 | 17 | |
18 | 18 | .tb-side-menu .md-button.tb-active { |
19 | - background-color: rgba(255, 255, 255, 0.15); | |
20 | 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 | 25 | padding: 0; |
26 | 26 | margin-top: 0; |
27 | + list-style: none; | |
27 | 28 | } |
28 | 29 | |
29 | 30 | .tb-side-menu .tb-menu-toggle-list a.md-button { |
30 | 31 | padding: 0 16px 0 32px; |
32 | + font-weight: 500; | |
31 | 33 | text-transform: none; |
32 | 34 | text-rendering: optimizeLegibility; |
33 | - font-weight: 500; | |
34 | 35 | } |
35 | 36 | |
36 | 37 | .tb-side-menu .tb-menu-toggle-list .md-button { |
... | ... | @@ -43,36 +44,38 @@ |
43 | 44 | } |
44 | 45 | |
45 | 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 | 50 | .tb-side-menu .md-button-toggle .md-toggle-icon { |
50 | - background-size: 100% auto; | |
51 | 51 | display: inline-block; |
52 | - margin: auto 0 auto auto; | |
53 | 52 | width: 15px; |
53 | + margin: auto 0 auto auto; | |
54 | + background-size: 100% auto; | |
55 | + | |
54 | 56 | @include transition(transform .3s, ease-in-out); |
55 | 57 | } |
56 | 58 | |
57 | 59 | .tb-side-menu .md-button { |
58 | 60 | display: flex; |
59 | - border-radius: 0; | |
60 | - color: inherit; | |
61 | - cursor: pointer; | |
62 | - line-height: 40px; | |
63 | - margin: 0; | |
61 | + width: 100%; | |
64 | 62 | max-height: 40px; |
63 | + padding: 0 16px; | |
64 | + margin: 0; | |
65 | 65 | overflow: hidden; |
66 | - padding: 0px 16px; | |
66 | + line-height: 40px; | |
67 | + color: inherit; | |
67 | 68 | text-align: left; |
68 | 69 | text-decoration: none; |
69 | - white-space: nowrap; | |
70 | 70 | text-overflow: ellipsis; |
71 | - width: 100%; | |
71 | + white-space: nowrap; | |
72 | + cursor: pointer; | |
73 | + border-radius: 0; | |
74 | + | |
72 | 75 | span { |
73 | 76 | overflow: hidden; |
74 | - white-space: nowrap; | |
75 | 77 | text-overflow: ellipsis; |
78 | + white-space: nowrap; | |
76 | 79 | } |
77 | 80 | } |
78 | 81 | |
... | ... | @@ -83,10 +86,10 @@ |
83 | 86 | |
84 | 87 | .tb-side-menu ng-md-icon { |
85 | 88 | margin-right: 8px; |
86 | - margin-left: 0px; | |
89 | + margin-left: 0; | |
87 | 90 | } |
88 | 91 | |
89 | 92 | .tb-side-menu md-icon { |
90 | 93 | margin-right: 8px; |
91 | - margin-left: 0px; | |
94 | + margin-left: 0; | |
92 | 95 | } | ... | ... |
... | ... | @@ -13,25 +13,32 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | tb-timeinterval { |
17 | 18 | min-width: 355px; |
19 | + | |
18 | 20 | md-input-container { |
19 | - margin-bottom: 0px; | |
21 | + margin-bottom: 0; | |
22 | + | |
20 | 23 | .md-errors-spacer { |
21 | - min-height: 0px; | |
24 | + min-height: 0; | |
22 | 25 | } |
23 | 26 | } |
27 | + | |
24 | 28 | mdp-date-picker { |
25 | 29 | .md-input { |
26 | 30 | width: 150px; |
27 | 31 | } |
28 | 32 | } |
33 | + | |
29 | 34 | .md-input { |
30 | 35 | width: 70px !important; |
31 | 36 | } |
37 | + | |
32 | 38 | .advanced-switch { |
33 | 39 | margin-top: 0; |
34 | 40 | } |
41 | + | |
35 | 42 | .advanced-label { |
36 | 43 | margin: 5px 0; |
37 | 44 | } | ... | ... |
... | ... | @@ -13,6 +13,7 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .md-panel { |
17 | 18 | &.tb-timewindow-panel { |
18 | 19 | position: absolute; |
... | ... | @@ -20,38 +21,48 @@ |
20 | 21 | } |
21 | 22 | |
22 | 23 | .tb-timewindow-panel { |
23 | - max-height: 440px; | |
24 | 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 | 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 | 36 | height: 100%; |
33 | 37 | } |
38 | + | |
34 | 39 | md-content { |
35 | - background-color: #fff; | |
36 | 40 | overflow: hidden; |
41 | + background-color: #fff; | |
37 | 42 | } |
43 | + | |
38 | 44 | .md-padding { |
39 | 45 | padding: 0 16px; |
40 | 46 | } |
47 | + | |
41 | 48 | .md-radio-interactive { |
42 | - md-select, md-switch { | |
49 | + md-select, | |
50 | + md-switch { | |
43 | 51 | pointer-events: all; |
44 | 52 | } |
45 | 53 | } |
54 | + | |
46 | 55 | md-radio-button { |
47 | 56 | .md-label { |
48 | 57 | width: 100%; |
49 | 58 | } |
59 | + | |
50 | 60 | tb-timeinterval { |
51 | 61 | width: 355px; |
62 | + | |
52 | 63 | .advanced-switch { |
53 | - min-height: 30px; | |
54 | 64 | max-width: 44px; |
65 | + min-height: 30px; | |
55 | 66 | } |
56 | 67 | } |
57 | 68 | } |
... | ... | @@ -59,15 +70,17 @@ |
59 | 70 | |
60 | 71 | tb-timewindow { |
61 | 72 | min-width: 52px; |
73 | + | |
62 | 74 | section.tb-timewindow { |
63 | 75 | min-height: 32px; |
64 | 76 | padding: 0 6px; |
77 | + | |
65 | 78 | span { |
66 | - pointer-events: all; | |
67 | - cursor: pointer; | |
68 | 79 | overflow: hidden; |
69 | 80 | text-overflow: ellipsis; |
70 | 81 | white-space: nowrap; |
82 | + pointer-events: all; | |
83 | + cursor: pointer; | |
71 | 84 | } |
72 | 85 | } |
73 | 86 | } | ... | ... |
... | ... | @@ -13,18 +13,19 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-manage-widget-actions { |
17 | 18 | table.md-table { |
18 | 19 | tbody { |
19 | 20 | tr { |
20 | 21 | td { |
21 | 22 | &.tb-action-cell { |
23 | + width: 100px; | |
24 | + min-width: 100px; | |
25 | + max-width: 100px; | |
22 | 26 | overflow: hidden; |
23 | 27 | text-overflow: ellipsis; |
24 | 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 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-widget-config { |
17 | 18 | md-tab-content.md-active > div { |
18 | 19 | height: 100%; |
19 | 20 | } |
21 | + | |
20 | 22 | .tb-advanced-widget-config { |
21 | 23 | height: 100%; |
22 | 24 | } |
23 | -} | |
\ No newline at end of file | ||
25 | +} | ... | ... |
... | ... | @@ -13,18 +13,21 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-widget { |
17 | 18 | .tb-widget-error { |
18 | 19 | display: flex; |
19 | - justify-content: center; | |
20 | 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 | 24 | span { |
23 | - color: red; | |
25 | + color: #f00; | |
24 | 26 | } |
25 | 27 | } |
28 | + | |
26 | 29 | .tb-widget-loading { |
27 | - background: rgba(255,255,255,0.15); | |
28 | 30 | z-index: 3; |
31 | + background: rgba(255, 255, 255, .15); | |
29 | 32 | } |
30 | 33 | } | ... | ... |
... | ... | @@ -17,9 +17,10 @@ |
17 | 17 | |
18 | 18 | tb-widgets-bundle-select { |
19 | 19 | md-select { |
20 | - margin: 0; | |
21 | 20 | padding: 5px 20px; |
21 | + margin: 0; | |
22 | 22 | } |
23 | + | |
23 | 24 | .tb-bundle-item { |
24 | 25 | height: 24px; |
25 | 26 | line-height: 24px; |
... | ... | @@ -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 | 39 | .md-text { |
38 | 40 | display: block; |
39 | 41 | width: 100%; |
40 | 42 | } |
43 | + | |
41 | 44 | .tb-bundle-item { |
42 | 45 | display: inline-block; |
43 | 46 | width: 100%; |
47 | + | |
44 | 48 | span { |
45 | 49 | display: inline-block; |
46 | 50 | vertical-align: middle; |
47 | 51 | } |
52 | + | |
48 | 53 | .tb-bundle-system { |
49 | - font-size: 0.8rem; | |
50 | - opacity: 0.8; | |
51 | 54 | float: right; |
55 | + font-size: .8rem; | |
56 | + opacity: .8; | |
52 | 57 | } |
53 | 58 | } |
59 | + | |
54 | 60 | md-option { |
55 | 61 | height: auto !important; |
56 | 62 | white-space: normal !important; |
... | ... | @@ -60,19 +66,23 @@ tb-widgets-bundle-select, .tb-widgets-bundle-select { |
60 | 66 | md-toolbar { |
61 | 67 | tb-widgets-bundle-select { |
62 | 68 | md-select { |
63 | - background: rgba(255,255,255,0.2); | |
69 | + background: rgba(255, 255, 255, .2); | |
70 | + | |
64 | 71 | .md-select-value { |
65 | - color: #fff; | |
66 | 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 | 80 | .md-select-value.md-select-placeholder { |
72 | 81 | color: #fff; |
73 | - opacity: 0.8; | |
82 | + opacity: .8; | |
74 | 83 | } |
75 | 84 | } |
85 | + | |
76 | 86 | md-select.ng-invalid.ng-touched { |
77 | 87 | .md-select-value { |
78 | 88 | color: #fff !important; | ... | ... |
... | ... | @@ -13,14 +13,15 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-dashboard-assigned-customers { |
17 | 18 | display: block; |
18 | 19 | display: -webkit-box; |
19 | 20 | height: 34px; |
21 | + margin-bottom: 4px; | |
20 | 22 | overflow: hidden; |
21 | 23 | text-overflow: ellipsis; |
22 | 24 | -webkit-line-clamp: 2; |
23 | 25 | -webkit-box-orient: vertical; |
24 | - margin-bottom: 4px; | |
25 | 26 | } |
26 | 27 | ... | ... |
... | ... | @@ -13,50 +13,54 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -$previewSize: 100px; | |
16 | +$previewSize: 100px !default; | |
17 | 17 | |
18 | 18 | .tb-image-select-container { |
19 | 19 | position: relative; |
20 | - height: $previewSize; | |
21 | 20 | width: 100%; |
21 | + height: $previewSize; | |
22 | 22 | } |
23 | 23 | |
24 | 24 | .tb-image-preview { |
25 | - max-width: $previewSize; | |
26 | - max-height: $previewSize; | |
27 | 25 | width: auto; |
26 | + max-width: $previewSize; | |
28 | 27 | height: auto; |
28 | + max-height: $previewSize; | |
29 | 29 | } |
30 | 30 | |
31 | 31 | .tb-image-preview-container { |
32 | 32 | position: relative; |
33 | + float: left; | |
33 | 34 | width: $previewSize; |
34 | 35 | height: $previewSize; |
35 | 36 | margin-right: 12px; |
36 | - border: solid 1px; | |
37 | 37 | vertical-align: top; |
38 | - float: left; | |
38 | + border: solid 1px; | |
39 | + | |
39 | 40 | div { |
40 | 41 | width: 100%; |
41 | 42 | font-size: 18px; |
42 | 43 | text-align: center; |
43 | 44 | } |
44 | - div, .tb-image-preview { | |
45 | + | |
46 | + div, | |
47 | + .tb-image-preview { | |
45 | 48 | position: absolute; |
46 | 49 | top: 50%; |
47 | 50 | left: 50%; |
48 | - transform: translate(-50%,-50%); | |
51 | + transform: translate(-50%, -50%); | |
49 | 52 | } |
50 | 53 | } |
51 | 54 | |
52 | 55 | .tb-image-clear-container { |
53 | - width: 48px; | |
54 | - height: $previewSize; | |
55 | 56 | position: relative; |
56 | 57 | float: right; |
58 | + width: 48px; | |
59 | + height: $previewSize; | |
57 | 60 | } |
61 | + | |
58 | 62 | .tb-image-clear-btn { |
59 | 63 | position: absolute !important; |
60 | 64 | top: 50%; |
61 | - transform: translate(0%,-50%) !important; | |
65 | + transform: translate(0%, -50%) !important; | |
62 | 66 | } | ... | ... |
... | ... | @@ -14,13 +14,14 @@ |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 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 | 26 | tb-dashboard-toolbar { |
26 | 27 | md-fab-toolbar { |
... | ... | @@ -29,126 +30,156 @@ tb-dashboard-toolbar { |
29 | 30 | .md-button { |
30 | 31 | &.md-fab { |
31 | 32 | opacity: 1; |
33 | + | |
32 | 34 | @include transition(opacity .3s cubic-bezier(.55,0,.55,.2)); |
35 | + | |
33 | 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 | 44 | md-fab-trigger { |
41 | 45 | .md-button { |
42 | 46 | &.md-fab { |
43 | - line-height: 36px; | |
44 | 47 | width: 36px; |
45 | 48 | height: 36px; |
46 | 49 | margin: 4px 0 0 4px; |
47 | - opacity: 0.5; | |
50 | + line-height: 36px; | |
51 | + opacity: .5; | |
52 | + | |
48 | 53 | @include transition(opacity .3s cubic-bezier(.55,0,.55,.2) .2s); |
54 | + | |
49 | 55 | md-icon { |
50 | 56 | position: absolute; |
51 | 57 | top: 25%; |
52 | - margin: 0; | |
53 | - line-height: 18px; | |
54 | - height: 18px; | |
55 | 58 | width: 18px; |
56 | - min-height: 18px; | |
57 | 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 | 69 | &.is-fullscreen { |
63 | 70 | &.md-is-open { |
64 | 71 | md-fab-trigger { |
65 | 72 | .md-button { |
66 | 73 | &.md-fab { |
67 | 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 | 83 | .md-fab-toolbar-wrapper { |
76 | 84 | height: $mobile-toolbar-height-total; |
85 | + | |
77 | 86 | @media (min-width: $layout-breakpoint-sm) { |
78 | 87 | height: $fullscreen-toolbar-height; |
79 | 88 | } |
89 | + | |
80 | 90 | md-toolbar { |
81 | - min-height: $mobile-toolbar-height; | |
82 | 91 | height: $mobile-toolbar-height; |
92 | + min-height: $mobile-toolbar-height; | |
93 | + | |
83 | 94 | @media (min-width: $layout-breakpoint-sm) { |
84 | - min-height: $fullscreen-toolbar-height; | |
85 | 95 | height: $fullscreen-toolbar-height; |
96 | + min-height: $fullscreen-toolbar-height; | |
86 | 97 | } |
87 | 98 | } |
88 | 99 | } |
89 | 100 | } |
101 | + | |
90 | 102 | .md-fab-toolbar-wrapper { |
91 | 103 | height: $mobile-toolbar-height-total; |
104 | + | |
92 | 105 | @media (min-width: $layout-breakpoint-sm) { |
93 | 106 | height: $toolbar-height; |
94 | 107 | } |
108 | + | |
95 | 109 | md-toolbar { |
96 | - min-height: $mobile-toolbar-height; | |
97 | 110 | height: $mobile-toolbar-height; |
111 | + min-height: $mobile-toolbar-height; | |
112 | + | |
98 | 113 | @media (min-width: $layout-breakpoint-sm) { |
99 | - min-height: $toolbar-height; | |
100 | 114 | height: $toolbar-height; |
115 | + min-height: $toolbar-height; | |
101 | 116 | } |
117 | + | |
102 | 118 | md-fab-actions { |
119 | + margin-top: 0; | |
103 | 120 | font-size: 16px; |
104 | - margin-top: 0px; | |
121 | + | |
105 | 122 | @media (max-width: $layout-breakpoint-sm) { |
106 | 123 | height: $mobile-toolbar-height; |
107 | 124 | max-height: $mobile-toolbar-height; |
108 | 125 | } |
126 | + | |
109 | 127 | .close-action { |
110 | 128 | margin-right: -18px; |
111 | 129 | } |
130 | + | |
112 | 131 | .md-fab-action-item { |
113 | 132 | width: 100%; |
114 | 133 | height: $mobile-toolbar-height; |
134 | + | |
115 | 135 | @media (min-width: $layout-breakpoint-sm) { |
116 | 136 | height: 46px; |
117 | 137 | } |
138 | + | |
118 | 139 | .tb-dashboard-action-panels { |
140 | + flex-direction: column-reverse; | |
119 | 141 | height: $mobile-toolbar-height; |
142 | + | |
120 | 143 | @media (min-width: $layout-breakpoint-sm) { |
121 | 144 | height: 46px; |
122 | 145 | } |
123 | - flex-direction: column-reverse; | |
146 | + | |
124 | 147 | @media (min-width: $layout-breakpoint-sm) { |
125 | 148 | flex-direction: row-reverse; |
126 | 149 | } |
150 | + | |
127 | 151 | .tb-dashboard-action-panel { |
128 | - min-width: 0px; | |
152 | + flex-direction: row-reverse; | |
153 | + min-width: 0; | |
129 | 154 | height: $half-mobile-toolbar-height; |
155 | + | |
130 | 156 | @media (min-width: $layout-breakpoint-sm) { |
131 | 157 | height: 46px; |
132 | 158 | } |
133 | - flex-direction: row-reverse; | |
159 | + | |
134 | 160 | div { |
135 | 161 | height: $half-mobile-toolbar-height; |
162 | + | |
136 | 163 | @media (min-width: $layout-breakpoint-sm) { |
137 | 164 | height: 46px; |
138 | 165 | } |
139 | 166 | } |
167 | + | |
140 | 168 | md-select { |
141 | 169 | pointer-events: all; |
142 | 170 | } |
171 | + | |
143 | 172 | tb-states-component { |
144 | 173 | pointer-events: all; |
145 | 174 | } |
175 | + | |
146 | 176 | button.md-icon-button { |
147 | 177 | min-width: 40px; |
178 | + | |
148 | 179 | @media (max-width: $layout-breakpoint-sm) { |
149 | 180 | min-width: 28px; |
150 | - margin: 0px; | |
151 | 181 | padding: 2px; |
182 | + margin: 0; | |
152 | 183 | } |
153 | 184 | } |
154 | 185 | } |
... | ... | @@ -158,4 +189,4 @@ tb-dashboard-toolbar { |
158 | 189 | } |
159 | 190 | } |
160 | 191 | } |
161 | -} | |
\ No newline at end of file | ||
192 | +} | ... | ... |
... | ... | @@ -14,11 +14,12 @@ |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 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 | 24 | section.tb-dashboard-title { |
24 | 25 | position: absolute; |
... | ... | @@ -27,10 +28,10 @@ section.tb-dashboard-title { |
27 | 28 | } |
28 | 29 | |
29 | 30 | input.tb-dashboard-title { |
30 | - font-size: 2.000rem; | |
31 | - font-weight: 500; | |
32 | - letter-spacing: 0.005em; | |
33 | 31 | height: 38px; |
32 | + font-size: 2rem; | |
33 | + font-weight: 500; | |
34 | + letter-spacing: .005em; | |
34 | 35 | } |
35 | 36 | |
36 | 37 | div.tb-padded { |
... | ... | @@ -50,9 +51,11 @@ tb-details-sidenav.tb-widget-details-sidenav { |
50 | 51 | @media (min-width: $layout-breakpoint-sm) { |
51 | 52 | width: 85% !important; |
52 | 53 | } |
54 | + | |
53 | 55 | @media (min-width: $layout-breakpoint-md) { |
54 | 56 | width: 75% !important; |
55 | 57 | } |
58 | + | |
56 | 59 | @media (min-width: $layout-breakpoint-lg) { |
57 | 60 | width: 60% !important; |
58 | 61 | } |
... | ... | @@ -65,47 +68,55 @@ tb-details-sidenav.tb-widget-details-sidenav { |
65 | 68 | |
66 | 69 | section.tb-dashboard-toolbar { |
67 | 70 | position: absolute; |
68 | - top: 0px; | |
69 | - left: 0px; | |
71 | + top: 0; | |
72 | + left: 0; | |
70 | 73 | z-index: 13; |
71 | 74 | pointer-events: none; |
75 | + | |
72 | 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 | 81 | &.tb-dashboard-toolbar-closed { |
77 | 82 | right: 18px; |
83 | + | |
78 | 84 | @include transition(right .3s cubic-bezier(.55,0,.55,.2) .2s); |
79 | 85 | } |
80 | 86 | } |
81 | 87 | |
82 | 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 | 115 | .tb-dashboard-layouts { |
103 | 116 | md-backdrop { |
104 | 117 | z-index: 1; |
105 | 118 | } |
106 | - #tb-main-layout { | |
107 | 119 | |
108 | - } | |
109 | 120 | #tb-right-layout { |
110 | 121 | md-sidenav { |
111 | 122 | z-index: 1; |
... | ... | @@ -124,13 +135,15 @@ section.tb-powered-by-footer { |
124 | 135 | bottom: 5px; |
125 | 136 | z-index: 3; |
126 | 137 | pointer-events: none; |
138 | + | |
127 | 139 | span { |
128 | 140 | font-size: 12px; |
141 | + | |
129 | 142 | a { |
130 | - font-weight: bold; | |
143 | + font-weight: 700; | |
131 | 144 | text-decoration: none; |
132 | - border: none; | |
133 | 145 | pointer-events: all; |
146 | + border: none; | |
134 | 147 | } |
135 | 148 | } |
136 | 149 | } | ... | ... |
... | ... | @@ -13,34 +13,37 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -@import '../../../scss/constants'; | |
16 | +@import "../../../scss/constants"; | |
17 | 17 | |
18 | 18 | tb-states-component { |
19 | - min-width: 0px; | |
19 | + min-width: 0; | |
20 | 20 | } |
21 | 21 | |
22 | 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 | -} | |
\ No newline at end of file | ||
48 | + } | |
49 | +} | ... | ... |
... | ... | @@ -13,21 +13,22 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .manage-dashboard-states { |
17 | 18 | table.md-table { |
18 | 19 | tbody { |
19 | 20 | tr { |
20 | 21 | td { |
21 | 22 | &.tb-action-cell { |
23 | + width: 100px; | |
24 | + min-width: 100px; | |
25 | + max-width: 100px; | |
22 | 26 | overflow: hidden; |
23 | 27 | text-overflow: ellipsis; |
24 | 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 | -} | |
\ No newline at end of file | ||
34 | +} | ... | ... |
... | ... | @@ -13,7 +13,7 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -@import '../../../scss/constants'; | |
16 | +@import "../../../scss/constants"; | |
17 | 17 | |
18 | 18 | tb-aliases-entity-select { |
19 | 19 | min-width: 52px; |
... | ... | @@ -26,18 +26,21 @@ tb-aliases-entity-select { |
26 | 26 | } |
27 | 27 | |
28 | 28 | .tb-aliases-entity-select-panel { |
29 | + min-width: 300px; | |
29 | 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 | 40 | @media (min-height: 350px) { |
31 | 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 | 44 | md-content { |
42 | 45 | background-color: #fff; |
43 | 46 | } |
... | ... | @@ -46,15 +49,17 @@ tb-aliases-entity-select { |
46 | 49 | section.tb-aliases-entity-select { |
47 | 50 | min-height: 32px; |
48 | 51 | padding: 0 6px; |
52 | + | |
49 | 53 | @media (max-width: $layout-breakpoint-sm) { |
50 | - padding: 0px; | |
54 | + padding: 0; | |
51 | 55 | } |
56 | + | |
52 | 57 | span { |
53 | 58 | max-width: 200px; |
54 | - pointer-events: all; | |
55 | - cursor: pointer; | |
56 | 59 | overflow: hidden; |
57 | 60 | text-overflow: ellipsis; |
58 | 61 | white-space: nowrap; |
62 | + pointer-events: all; | |
63 | + cursor: pointer; | |
59 | 64 | } |
60 | 65 | } | ... | ... |
... | ... | @@ -13,14 +13,17 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | + | |
16 | 17 | .tb-entity-alias-dialog { |
17 | 18 | .tb-resolve-multiple-switch { |
18 | 19 | padding-left: 10px; |
20 | + | |
19 | 21 | .resolve-multiple-switch { |
20 | 22 | margin: 0; |
21 | 23 | } |
24 | + | |
22 | 25 | .resolve-multiple-label { |
23 | 26 | margin: 5px 0; |
24 | 27 | } |
25 | 28 | } |
26 | -} | |
\ No newline at end of file | ||
29 | +} | ... | ... |