Showing
26 changed files
with
123 additions
and
140 deletions
@@ -35,7 +35,7 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback; | @@ -35,7 +35,7 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback; | ||
35 | nodeDetails = "Evaluate incoming Message with configured JS condition. " + | 35 | nodeDetails = "Evaluate incoming Message with configured JS condition. " + |
36 | "If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used." + | 36 | "If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used." + |
37 | "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code>" + | 37 | "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code>" + |
38 | - "Message metadata can be accessed via <code>meta</code> property. For example <code>meta.customerName === 'John';</code>", | 38 | + "Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>", |
39 | uiResources = {"static/rulenode/rulenode-core-config.js"}, | 39 | uiResources = {"static/rulenode/rulenode-core-config.js"}, |
40 | configDirective = "tbFilterNodeScriptConfig") | 40 | configDirective = "tbFilterNodeScriptConfig") |
41 | 41 | ||
@@ -47,7 +47,7 @@ public class TbJsFilterNode implements TbNode { | @@ -47,7 +47,7 @@ public class TbJsFilterNode implements TbNode { | ||
47 | @Override | 47 | @Override |
48 | public void init(TbNodeConfiguration configuration, TbNodeState state) throws TbNodeException { | 48 | public void init(TbNodeConfiguration configuration, TbNodeState state) throws TbNodeException { |
49 | this.config = TbNodeUtils.convert(configuration, TbJsFilterNodeConfiguration.class); | 49 | this.config = TbNodeUtils.convert(configuration, TbJsFilterNodeConfiguration.class); |
50 | - this.jsEngine = new NashornJsEngine(config.getJsScript()); | 50 | + this.jsEngine = new NashornJsEngine(config.getJsScript(), "Filter"); |
51 | } | 51 | } |
52 | 52 | ||
53 | @Override | 53 | @Override |
@@ -26,7 +26,7 @@ public class TbJsFilterNodeConfiguration implements NodeConfiguration { | @@ -26,7 +26,7 @@ public class TbJsFilterNodeConfiguration implements NodeConfiguration { | ||
26 | @Override | 26 | @Override |
27 | public TbJsFilterNodeConfiguration defaultConfiguration() { | 27 | public TbJsFilterNodeConfiguration defaultConfiguration() { |
28 | TbJsFilterNodeConfiguration configuration = new TbJsFilterNodeConfiguration(); | 28 | TbJsFilterNodeConfiguration configuration = new TbJsFilterNodeConfiguration(); |
29 | - configuration.setJsScript("msg.passed < 15 && msg.name === 'Vit' && meta.temp == 10 && msg.bigObj.prop == 42;"); | 29 | + configuration.setJsScript("return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 10 && msg.bigObj.prop == 42;"); |
30 | return configuration; | 30 | return configuration; |
31 | } | 31 | } |
32 | } | 32 | } |
@@ -36,7 +36,9 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback; | @@ -36,7 +36,9 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback; | ||
36 | nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " + | 36 | nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " + |
37 | "If Array is empty - message not routed to next Node. " + | 37 | "If Array is empty - message not routed to next Node. " + |
38 | "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code> " + | 38 | "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code> " + |
39 | - "Message metadata can be accessed via <code>meta</code> property. For example <code>meta.customerName === 'John';</code>") | 39 | + "Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>", |
40 | + uiResources = {"static/rulenode/rulenode-core-config.js"}, | ||
41 | + configDirective = "tbFilterNodeSwitchConfig") | ||
40 | public class TbJsSwitchNode implements TbNode { | 42 | public class TbJsSwitchNode implements TbNode { |
41 | 43 | ||
42 | private TbJsSwitchNodeConfiguration config; | 44 | private TbJsSwitchNodeConfiguration config; |
@@ -45,22 +47,11 @@ public class TbJsSwitchNode implements TbNode { | @@ -45,22 +47,11 @@ public class TbJsSwitchNode implements TbNode { | ||
45 | @Override | 47 | @Override |
46 | public void init(TbNodeConfiguration configuration, TbNodeState state) throws TbNodeException { | 48 | public void init(TbNodeConfiguration configuration, TbNodeState state) throws TbNodeException { |
47 | this.config = TbNodeUtils.convert(configuration, TbJsSwitchNodeConfiguration.class); | 49 | this.config = TbNodeUtils.convert(configuration, TbJsSwitchNodeConfiguration.class); |
48 | - if (config.getAllowedRelations().size() < 1) { | ||
49 | - String message = "Switch node should have at least 1 relation"; | ||
50 | - log.error(message); | ||
51 | - throw new IllegalStateException(message); | ||
52 | - } | ||
53 | - if (!config.isRouteToAllWithNoCheck()) { | ||
54 | - this.jsEngine = new NashornJsEngine(config.getJsScript()); | ||
55 | - } | 50 | + this.jsEngine = new NashornJsEngine(config.getJsScript(), "Switch"); |
56 | } | 51 | } |
57 | 52 | ||
58 | @Override | 53 | @Override |
59 | public void onMsg(TbContext ctx, TbMsg msg) { | 54 | public void onMsg(TbContext ctx, TbMsg msg) { |
60 | - if (config.isRouteToAllWithNoCheck()) { | ||
61 | - ctx.tellNext(msg, config.getAllowedRelations()); | ||
62 | - return; | ||
63 | - } | ||
64 | ListeningExecutor jsExecutor = ctx.getJsExecutor(); | 55 | ListeningExecutor jsExecutor = ctx.getJsExecutor(); |
65 | withCallback(jsExecutor.executeAsync(() -> jsEngine.executeSwitch(toBindings(msg))), | 56 | withCallback(jsExecutor.executeAsync(() -> jsEngine.executeSwitch(toBindings(msg))), |
66 | result -> processSwitch(ctx, msg, result), | 57 | result -> processSwitch(ctx, msg, result), |
@@ -68,15 +59,7 @@ public class TbJsSwitchNode implements TbNode { | @@ -68,15 +59,7 @@ public class TbJsSwitchNode implements TbNode { | ||
68 | } | 59 | } |
69 | 60 | ||
70 | private void processSwitch(TbContext ctx, TbMsg msg, Set<String> nextRelations) { | 61 | private void processSwitch(TbContext ctx, TbMsg msg, Set<String> nextRelations) { |
71 | - if (validateRelations(nextRelations)) { | ||
72 | - ctx.tellNext(msg, nextRelations); | ||
73 | - } else { | ||
74 | - ctx.tellError(msg, new IllegalStateException("Unsupported relation for switch " + nextRelations)); | ||
75 | - } | ||
76 | - } | ||
77 | - | ||
78 | - private boolean validateRelations(Set<String> nextRelations) { | ||
79 | - return config.getAllowedRelations().containsAll(nextRelations); | 62 | + ctx.tellNext(msg, nextRelations); |
80 | } | 63 | } |
81 | 64 | ||
82 | private Bindings toBindings(TbMsg msg) { | 65 | private Bindings toBindings(TbMsg msg) { |
@@ -25,19 +25,15 @@ import java.util.Set; | @@ -25,19 +25,15 @@ import java.util.Set; | ||
25 | public class TbJsSwitchNodeConfiguration implements NodeConfiguration { | 25 | public class TbJsSwitchNodeConfiguration implements NodeConfiguration { |
26 | 26 | ||
27 | private String jsScript; | 27 | private String jsScript; |
28 | - private Set<String> allowedRelations; | ||
29 | - private boolean routeToAllWithNoCheck; | ||
30 | 28 | ||
31 | @Override | 29 | @Override |
32 | public TbJsSwitchNodeConfiguration defaultConfiguration() { | 30 | public TbJsSwitchNodeConfiguration defaultConfiguration() { |
33 | TbJsSwitchNodeConfiguration configuration = new TbJsSwitchNodeConfiguration(); | 31 | TbJsSwitchNodeConfiguration configuration = new TbJsSwitchNodeConfiguration(); |
34 | - configuration.setJsScript("function nextRelation(meta, msg) {\n" + | 32 | + configuration.setJsScript("function nextRelation(metadata, msg) {\n" + |
35 | " return ['one','nine'];" + | 33 | " return ['one','nine'];" + |
36 | "};\n" + | 34 | "};\n" + |
37 | "\n" + | 35 | "\n" + |
38 | - "nextRelation(meta, msg);"); | ||
39 | - configuration.setAllowedRelations(Sets.newHashSet("one", "two")); | ||
40 | - configuration.setRouteToAllWithNoCheck(false); | 36 | + "return nextRelation(metadata, msg);"); |
41 | return configuration; | 37 | return configuration; |
42 | } | 38 | } |
43 | } | 39 | } |
@@ -31,7 +31,9 @@ import org.thingsboard.server.common.msg.TbMsg; | @@ -31,7 +31,9 @@ import org.thingsboard.server.common.msg.TbMsg; | ||
31 | configClazz = TbMsgTypeFilterNodeConfiguration.class, | 31 | configClazz = TbMsgTypeFilterNodeConfiguration.class, |
32 | nodeDescription = "Filter incoming messages by Message Type", | 32 | nodeDescription = "Filter incoming messages by Message Type", |
33 | nodeDetails = "Evaluate incoming Message with configured JS condition. " + | 33 | nodeDetails = "Evaluate incoming Message with configured JS condition. " + |
34 | - "If incoming MessageType is expected - send Message via <b>Success</b> chain, otherwise <b>Failure</b> chain is used.") | 34 | + "If incoming MessageType is expected - send Message via <b>Success</b> chain, otherwise <b>Failure</b> chain is used.", |
35 | + uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"}, | ||
36 | + configDirective = "tbFilterNodeMessageTypeConfig") | ||
35 | public class TbMsgTypeFilterNode implements TbNode { | 37 | public class TbMsgTypeFilterNode implements TbNode { |
36 | 38 | ||
37 | TbMsgTypeFilterNodeConfiguration config; | 39 | TbMsgTypeFilterNodeConfiguration config; |
@@ -33,7 +33,7 @@ public class TbMsgTypeFilterNodeConfiguration implements NodeConfiguration { | @@ -33,7 +33,7 @@ public class TbMsgTypeFilterNodeConfiguration implements NodeConfiguration { | ||
33 | @Override | 33 | @Override |
34 | public TbMsgTypeFilterNodeConfiguration defaultConfiguration() { | 34 | public TbMsgTypeFilterNodeConfiguration defaultConfiguration() { |
35 | TbMsgTypeFilterNodeConfiguration configuration = new TbMsgTypeFilterNodeConfiguration(); | 35 | TbMsgTypeFilterNodeConfiguration configuration = new TbMsgTypeFilterNodeConfiguration(); |
36 | - configuration.setMessageTypes(Arrays.asList("GET_ATTRIBUTES","POST_ATTRIBUTES","POST_TELEMETRY","RPC_REQUEST")); | 36 | + configuration.setMessageTypes(Arrays.asList("POST_ATTRIBUTES","POST_TELEMETRY","RPC_REQUEST")); |
37 | return configuration; | 37 | return configuration; |
38 | } | 38 | } |
39 | } | 39 | } |
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/js/NashornJsEngine.java
@@ -34,14 +34,20 @@ import java.util.Set; | @@ -34,14 +34,20 @@ import java.util.Set; | ||
34 | @Slf4j | 34 | @Slf4j |
35 | public class NashornJsEngine { | 35 | public class NashornJsEngine { |
36 | 36 | ||
37 | - public static final String METADATA = "meta"; | 37 | + public static final String METADATA = "metadata"; |
38 | public static final String DATA = "msg"; | 38 | public static final String DATA = "msg"; |
39 | + | ||
40 | + private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msg, metadata) { "; | ||
41 | + private static final String JS_WRAPPER_SUFFIX_TEMPLATE = "}\n %s(msg, metadata);"; | ||
42 | + | ||
39 | private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); | 43 | private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); |
40 | 44 | ||
41 | private CompiledScript engine; | 45 | private CompiledScript engine; |
42 | 46 | ||
43 | - public NashornJsEngine(String script) { | ||
44 | - engine = compileScript(script); | 47 | + public NashornJsEngine(String script, String functionName) { |
48 | + String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, functionName); | ||
49 | + String jsWrapperSuffix = String.format(JS_WRAPPER_SUFFIX_TEMPLATE, functionName); | ||
50 | + engine = compileScript(jsWrapperPrefix + script + jsWrapperSuffix); | ||
45 | } | 51 | } |
46 | 52 | ||
47 | private static CompiledScript compileScript(String script) { | 53 | private static CompiledScript compileScript(String script) { |
@@ -58,15 +64,15 @@ public class NashornJsEngine { | @@ -58,15 +64,15 @@ public class NashornJsEngine { | ||
58 | public static Bindings bindMsg(TbMsg msg) { | 64 | public static Bindings bindMsg(TbMsg msg) { |
59 | try { | 65 | try { |
60 | Bindings bindings = new SimpleBindings(); | 66 | Bindings bindings = new SimpleBindings(); |
61 | - bindings.put(METADATA, msg.getMetaData().getData()); | ||
62 | - | ||
63 | if (ArrayUtils.isNotEmpty(msg.getData())) { | 67 | if (ArrayUtils.isNotEmpty(msg.getData())) { |
64 | ObjectMapper mapper = new ObjectMapper(); | 68 | ObjectMapper mapper = new ObjectMapper(); |
65 | JsonNode jsonNode = mapper.readTree(msg.getData()); | 69 | JsonNode jsonNode = mapper.readTree(msg.getData()); |
66 | Map map = mapper.treeToValue(jsonNode, Map.class); | 70 | Map map = mapper.treeToValue(jsonNode, Map.class); |
67 | bindings.put(DATA, map); | 71 | bindings.put(DATA, map); |
72 | + } else { | ||
73 | + bindings.put(DATA, Collections.emptyMap()); | ||
68 | } | 74 | } |
69 | - | 75 | + bindings.put(METADATA, msg.getMetaData().getData()); |
70 | return bindings; | 76 | return bindings; |
71 | } catch (Throwable th) { | 77 | } catch (Throwable th) { |
72 | throw new IllegalArgumentException("Cannot bind js args", th); | 78 | throw new IllegalArgumentException("Cannot bind js args", th); |
@@ -42,7 +42,7 @@ import static org.thingsboard.server.common.data.DataConstants.*; | @@ -42,7 +42,7 @@ import static org.thingsboard.server.common.data.DataConstants.*; | ||
42 | nodeDescription = "Add Message Originator Attributes or Latest Telemetry into Message Metadata", | 42 | nodeDescription = "Add Message Originator Attributes or Latest Telemetry into Message Metadata", |
43 | nodeDetails = "If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata " + | 43 | nodeDetails = "If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata " + |
44 | "with specific prefix: <i>cs/shared/ss</i>. To access those attributes in other nodes this template can be used " + | 44 | "with specific prefix: <i>cs/shared/ss</i>. To access those attributes in other nodes this template can be used " + |
45 | - "<code>meta.cs.temperature</code> or <code>meta.shared.limit</code> " + | 45 | + "<code>metadata.cs.temperature</code> or <code>metadata.shared.limit</code> " + |
46 | "If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.") | 46 | "If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.") |
47 | public class TbGetAttributesNode implements TbNode { | 47 | public class TbGetAttributesNode implements TbNode { |
48 | 48 |
@@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; | @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; | ||
30 | nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message Metadata", | 30 | nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message Metadata", |
31 | nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + | 31 | nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + |
32 | "To access those attributes in other nodes this template can be used " + | 32 | "To access those attributes in other nodes this template can be used " + |
33 | - "<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata") | 33 | + "<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata") |
34 | public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> { | 34 | public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> { |
35 | 35 | ||
36 | @Override | 36 | @Override |
@@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; | @@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; | ||
32 | "If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. " + | 32 | "If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. " + |
33 | "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + | 33 | "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + |
34 | "To access those attributes in other nodes this template can be used " + | 34 | "To access those attributes in other nodes this template can be used " + |
35 | - "<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata") | 35 | + "<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata") |
36 | public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> { | 36 | public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> { |
37 | 37 | ||
38 | private TbGetRelatedAttrNodeConfiguration config; | 38 | private TbGetRelatedAttrNodeConfiguration config; |
@@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; | @@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; | ||
32 | nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata", | 32 | nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata", |
33 | nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + | 33 | nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + |
34 | "To access those attributes in other nodes this template can be used " + | 34 | "To access those attributes in other nodes this template can be used " + |
35 | - "<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata") | 35 | + "<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata") |
36 | public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> { | 36 | public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> { |
37 | 37 | ||
38 | @Override | 38 | @Override |
@@ -30,7 +30,7 @@ import javax.script.Bindings; | @@ -30,7 +30,7 @@ import javax.script.Bindings; | ||
30 | configClazz = TbTransformMsgNodeConfiguration.class, | 30 | configClazz = TbTransformMsgNodeConfiguration.class, |
31 | nodeDescription = "Change Message payload and Metadata using JavaScript", | 31 | nodeDescription = "Change Message payload and Metadata using JavaScript", |
32 | nodeDetails = "JavaScript function recieve 2 input parameters that can be changed inside.<br/> " + | 32 | nodeDetails = "JavaScript function recieve 2 input parameters that can be changed inside.<br/> " + |
33 | - "<code>meta</code> - is a Message metadata.<br/>" + | 33 | + "<code>metadata</code> - is a Message metadata.<br/>" + |
34 | "<code>msg</code> - is a Message payload.<br/>Any properties can be changed/removed/added in those objects.") | 34 | "<code>msg</code> - is a Message payload.<br/>Any properties can be changed/removed/added in those objects.") |
35 | public class TbTransformMsgNode extends TbAbstractTransformNode { | 35 | public class TbTransformMsgNode extends TbAbstractTransformNode { |
36 | 36 | ||
@@ -40,7 +40,7 @@ public class TbTransformMsgNode extends TbAbstractTransformNode { | @@ -40,7 +40,7 @@ public class TbTransformMsgNode extends TbAbstractTransformNode { | ||
40 | @Override | 40 | @Override |
41 | public void init(TbNodeConfiguration configuration, TbNodeState state) throws TbNodeException { | 41 | public void init(TbNodeConfiguration configuration, TbNodeState state) throws TbNodeException { |
42 | this.config = TbNodeUtils.convert(configuration, TbTransformMsgNodeConfiguration.class); | 42 | this.config = TbNodeUtils.convert(configuration, TbTransformMsgNodeConfiguration.class); |
43 | - this.jsEngine = new NashornJsEngine(config.getJsScript()); | 43 | + this.jsEngine = new NashornJsEngine(config.getJsScript(), "Transform"); |
44 | setConfig(config); | 44 | setConfig(config); |
45 | } | 45 | } |
46 | 46 |
@@ -27,7 +27,7 @@ public class TbTransformMsgNodeConfiguration extends TbTransformNodeConfiguratio | @@ -27,7 +27,7 @@ public class TbTransformMsgNodeConfiguration extends TbTransformNodeConfiguratio | ||
27 | public TbTransformMsgNodeConfiguration defaultConfiguration() { | 27 | public TbTransformMsgNodeConfiguration defaultConfiguration() { |
28 | TbTransformMsgNodeConfiguration configuration = new TbTransformMsgNodeConfiguration(); | 28 | TbTransformMsgNodeConfiguration configuration = new TbTransformMsgNodeConfiguration(); |
29 | configuration.setStartNewChain(false); | 29 | configuration.setStartNewChain(false); |
30 | - configuration.setJsScript("msg.passed = msg.passed * meta.temp; msg.bigObj.newProp = 'Ukraine' "); | 30 | + configuration.setJsScript("return msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine' "); |
31 | return configuration; | 31 | return configuration; |
32 | } | 32 | } |
33 | } | 33 | } |
1 | +.tb-message-type-autocomplete .tb-not-found{display:block;line-height:1.5;height:48px}.tb-message-type-autocomplete .tb-not-found .tb-no-entries{line-height:48px}.tb-message-type-autocomplete li{height:auto!important;white-space:normal!important} | ||
2 | +/*# sourceMappingURL=rulenode-core-config.css.map*/ |
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 u=n[r]={exports:{},id:r,loaded:!1};return e[r].call(u.exports,u,u.exports,t),u.loaded=!0,u.exports}var n={};return t.m=e,t.c=n,t.p="/static/",t(0)}([function(e,t,n){e.exports=n(3)},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\'] }}" no-validate=true> </tb-js-func> </section> '},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function u(e){var t=function(t,n,r,u){var o=i.default;n.html(o),t.$watch("configuration",function(e,n){angular.equals(e,n)||u.$setViewValue(t.configuration)}),u.$render=function(){t.configuration=u.$viewValue},e(n.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}u.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=u;var o=n(1),i=r(o)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var u=n(2),o=r(u),i=n(5),a=r(i);t.default=angular.module("thingsboard.ruleChain.config",[]).directive("tbFilterNodeScriptConfig",o.default).config(a.default).name},function(e,t){"use strict";function n(e){var t={tb:{rulenode:{filter:"Filter"}}};angular.merge(e.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 u(e,t){(0,i.default)(t);for(var n in t){var r=t[n];e.translations(n,r)}}u.$inject=["$translateProvider","locales"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=u;var o=n(4),i=r(o)}]); | 1 | +!function(e){function t(s){if(a[s])return a[s].exports;var n=a[s]={exports:{},id:s,loaded:!1};return e[s].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a={};return t.m=e,t.c=a,t.p="/static/",t(0)}([function(e,t,a){e.exports=a(8)},function(e,t){},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\">tb.rulenode.filter</label> <tb-js-func ng-model=configuration.jsScript function-name=Filter function-args=\"{{ ['msg', 'metadata'] }}\" no-validate=true> </tb-js-func> </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'] }}\" no-validate=true> </tb-js-func> </section> "},function(e,t,a){"use strict";function s(e){return e&&e.__esModule?e:{default:e}}function n(e,t,a){var s=function(s,n,r,l){function u(){if(l.$viewValue){for(var e=[],t=0;t<s.messageTypes.length;t++)e.push(s.messageTypes[t].value);l.$viewValue.messageTypes=e,o()}}function o(){if(s.required){var e=!(!l.$viewValue.messageTypes||!l.$viewValue.messageTypes.length);l.$setValidity("messageTypes",e)}else l.$setValidity("messageTypes",!0)}var c=i.default;n.html(c),s.selectedMessageType=null,s.messageTypeSearchText=null,s.ngModelCtrl=l;var d=[];for(var p in a.messageType){var m={name:a.messageType[p].name,value:a.messageType[p].value};d.push(m)}s.transformMessageTypeChip=function(e){var a,s=t("filter")(d,{name:e},!0);return a=s&&s.length?angular.copy(s[0]):{name:e,value:e}},s.messageTypesSearch=function(e){var a=e?t("filter")(d,{name:e}):d;return a.map(function(e){return e.name})},s.createMessageType=function(e,t){var a=angular.element(t,n)[0].firstElementChild,s=angular.element(a),r=s.scope().$mdChipsCtrl.getChipBuffer();e.preventDefault(),e.stopPropagation(),s.scope().$mdChipsCtrl.appendChip(r.trim()),s.scope().$mdChipsCtrl.resetChipBuffer()},l.$render=function(){var e=l.$viewValue,t=[];if(e&&e.messageTypes)for(var n=0;n<e.messageTypes.length;n++){var r=e.messageTypes[n];a.messageType[r]?t.push(angular.copy(a.messageType[r])):t.push({name:r,value:r})}s.messageTypes=t,s.$watch("messageTypes",function(e,t){angular.equals(e,t)||u()},!0)},e(n.contents())(s)};return{restrict:"E",require:"^ngModel",scope:{required:"=ngRequired",readonly:"=ngReadonly"},link:s}}n.$inject=["$compile","$filter","ruleNodeTypes"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n,a(1);var r=a(2),i=s(r)},function(e,t,a){"use strict";function s(e){return e&&e.__esModule?e:{default:e}}function n(e){var t=function(t,a,s,n){var r=i.default;a.html(r),t.$watch("configuration",function(e,a){angular.equals(e,a)||n.$setViewValue(t.configuration)}),n.$render=function(){t.configuration=n.$viewValue},e(a.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n;var r=a(3),i=s(r)},function(e,t,a){"use strict";function s(e){return e&&e.__esModule?e:{default:e}}function n(e){var t=function(t,a,s,n){var r=i.default;a.html(r),t.$watch("configuration",function(e,a){angular.equals(e,a)||n.$setViewValue(t.configuration)}),n.$render=function(){t.configuration=n.$viewValue},e(a.contents())(t)};return{restrict:"E",require:"^ngModel",scope:{},link:t}}n.$inject=["$compile"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n;var r=a(4),i=s(r)},function(e,t,a){"use strict";function s(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var n=a(11),r=s(n),i=a(6),l=s(i),u=a(5),o=s(u),c=a(7),d=s(c),p=a(10),m=s(p);t.default=angular.module("thingsboard.ruleChain.config",[r.default]).directive("tbFilterNodeScriptConfig",l.default).directive("tbFilterNodeMessageTypeConfig",o.default).directive("tbFilterNodeSwitchConfig",d.default).config(m.default).name},function(e,t){"use strict";function a(e){var t={tb:{rulenode:{filter:"Filter",switch:"Switch","message-type":"Message type","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."}}};angular.merge(e.en_US,t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=a},function(e,t,a){"use strict";function s(e){return e&&e.__esModule?e:{default:e}}function n(e,t){(0,i.default)(t);for(var a in t){var s=t[a];e.translations(a,s)}}n.$inject=["$translateProvider","locales"],Object.defineProperty(t,"__esModule",{value:!0}),t.default=n;var r=a(9),i=s(r)},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=angular.module("thingsboard.ruleChain.config.types",[]).constant("ruleNodeTypes",{messageType:{POST_ATTRIBUTES:{name:"Post attributes",value:"POST_ATTRIBUTES"},POST_TELEMETRY:{name:"Post telemetry",value:"POST_TELEMETRY"},RPC_REQUEST:{name:"RPC Request",value:"RPC_REQUEST"}}}).name}]); |
2 | //# sourceMappingURL=rulenode-core-config.js.map | 2 | //# sourceMappingURL=rulenode-core-config.js.map |
@@ -51,7 +51,7 @@ public class TbJsFilterNodeTest { | @@ -51,7 +51,7 @@ public class TbJsFilterNodeTest { | ||
51 | 51 | ||
52 | @Test | 52 | @Test |
53 | public void falseEvaluationDoNotSendMsg() throws TbNodeException { | 53 | public void falseEvaluationDoNotSendMsg() throws TbNodeException { |
54 | - initWithScript("10 > 15;"); | 54 | + initWithScript("return 10 > 15;"); |
55 | TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), "{}".getBytes()); | 55 | TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), "{}".getBytes()); |
56 | 56 | ||
57 | mockJsExecutor(); | 57 | mockJsExecutor(); |
@@ -64,7 +64,7 @@ public class TbJsFilterNodeTest { | @@ -64,7 +64,7 @@ public class TbJsFilterNodeTest { | ||
64 | 64 | ||
65 | @Test | 65 | @Test |
66 | public void notValidMsgDataThrowsException() throws TbNodeException { | 66 | public void notValidMsgDataThrowsException() throws TbNodeException { |
67 | - initWithScript("10 > 15;"); | 67 | + initWithScript("return 10 > 15;"); |
68 | TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), new byte[4]); | 68 | TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), new byte[4]); |
69 | 69 | ||
70 | when(ctx.getJsExecutor()).thenReturn(executor); | 70 | when(ctx.getJsExecutor()).thenReturn(executor); |
@@ -77,7 +77,7 @@ public class TbJsFilterNodeTest { | @@ -77,7 +77,7 @@ public class TbJsFilterNodeTest { | ||
77 | 77 | ||
78 | @Test | 78 | @Test |
79 | public void exceptionInJsThrowsException() throws TbNodeException { | 79 | public void exceptionInJsThrowsException() throws TbNodeException { |
80 | - initWithScript("meta.temp.curr < 15;"); | 80 | + initWithScript("return metadata.temp.curr < 15;"); |
81 | TbMsgMetaData metaData = new TbMsgMetaData(); | 81 | TbMsgMetaData metaData = new TbMsgMetaData(); |
82 | TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}".getBytes()); | 82 | TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}".getBytes()); |
83 | mockJsExecutor(); | 83 | mockJsExecutor(); |
@@ -89,12 +89,12 @@ public class TbJsFilterNodeTest { | @@ -89,12 +89,12 @@ public class TbJsFilterNodeTest { | ||
89 | 89 | ||
90 | @Test(expected = IllegalArgumentException.class) | 90 | @Test(expected = IllegalArgumentException.class) |
91 | public void notValidScriptThrowsException() throws TbNodeException { | 91 | public void notValidScriptThrowsException() throws TbNodeException { |
92 | - initWithScript("10 > 15 asdq out"); | 92 | + initWithScript("return 10 > 15 asdq out"); |
93 | } | 93 | } |
94 | 94 | ||
95 | @Test | 95 | @Test |
96 | public void metadataConditionCanBeFalse() throws TbNodeException { | 96 | public void metadataConditionCanBeFalse() throws TbNodeException { |
97 | - initWithScript("meta.humidity < 15;"); | 97 | + initWithScript("return metadata.humidity < 15;"); |
98 | TbMsgMetaData metaData = new TbMsgMetaData(); | 98 | TbMsgMetaData metaData = new TbMsgMetaData(); |
99 | metaData.putValue("temp", "10"); | 99 | metaData.putValue("temp", "10"); |
100 | metaData.putValue("humidity", "99"); | 100 | metaData.putValue("humidity", "99"); |
@@ -109,7 +109,7 @@ public class TbJsFilterNodeTest { | @@ -109,7 +109,7 @@ public class TbJsFilterNodeTest { | ||
109 | 109 | ||
110 | @Test | 110 | @Test |
111 | public void metadataConditionCanBeTrue() throws TbNodeException { | 111 | public void metadataConditionCanBeTrue() throws TbNodeException { |
112 | - initWithScript("meta.temp < 15;"); | 112 | + initWithScript("return metadata.temp < 15;"); |
113 | TbMsgMetaData metaData = new TbMsgMetaData(); | 113 | TbMsgMetaData metaData = new TbMsgMetaData(); |
114 | metaData.putValue("temp", "10"); | 114 | metaData.putValue("temp", "10"); |
115 | metaData.putValue("humidity", "99"); | 115 | metaData.putValue("humidity", "99"); |
@@ -123,7 +123,7 @@ public class TbJsFilterNodeTest { | @@ -123,7 +123,7 @@ public class TbJsFilterNodeTest { | ||
123 | 123 | ||
124 | @Test | 124 | @Test |
125 | public void msgJsonParsedAndBinded() throws TbNodeException { | 125 | public void msgJsonParsedAndBinded() throws TbNodeException { |
126 | - initWithScript("msg.passed < 15 && msg.name === 'Vit' && meta.temp == 10 && msg.bigObj.prop == 42;"); | 126 | + initWithScript("return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 10 && msg.bigObj.prop == 42;"); |
127 | TbMsgMetaData metaData = new TbMsgMetaData(); | 127 | TbMsgMetaData metaData = new TbMsgMetaData(); |
128 | metaData.putValue("temp", "10"); | 128 | metaData.putValue("temp", "10"); |
129 | metaData.putValue("humidity", "99"); | 129 | metaData.putValue("humidity", "99"); |
@@ -53,27 +53,16 @@ public class TbJsSwitchNodeTest { | @@ -53,27 +53,16 @@ public class TbJsSwitchNodeTest { | ||
53 | private ListeningExecutor executor; | 53 | private ListeningExecutor executor; |
54 | 54 | ||
55 | @Test | 55 | @Test |
56 | - public void routeToAllDoNotEvaluatesJs() throws TbNodeException { | ||
57 | - HashSet<String> relations = Sets.newHashSet("one", "two"); | ||
58 | - initWithScript("test qwerty", relations, true); | ||
59 | - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), "{}".getBytes()); | ||
60 | - | ||
61 | - node.onMsg(ctx, msg); | ||
62 | - verify(ctx).tellNext(msg, relations); | ||
63 | - verifyNoMoreInteractions(ctx, executor); | ||
64 | - } | ||
65 | - | ||
66 | - @Test | ||
67 | public void multipleRoutesAreAllowed() throws TbNodeException { | 56 | public void multipleRoutesAreAllowed() throws TbNodeException { |
68 | - String jsCode = "function nextRelation(meta, msg) {\n" + | ||
69 | - " if(msg.passed == 5 && meta.temp == 10)\n" + | 57 | + String jsCode = "function nextRelation(metadata, msg) {\n" + |
58 | + " if(msg.passed == 5 && metadata.temp == 10)\n" + | ||
70 | " return ['three', 'one']\n" + | 59 | " return ['three', 'one']\n" + |
71 | " else\n" + | 60 | " else\n" + |
72 | " return 'two';\n" + | 61 | " return 'two';\n" + |
73 | "};\n" + | 62 | "};\n" + |
74 | "\n" + | 63 | "\n" + |
75 | - "nextRelation(meta, msg);"; | ||
76 | - initWithScript(jsCode, Sets.newHashSet("one", "two", "three"), false); | 64 | + "return nextRelation(metadata, msg);"; |
65 | + initWithScript(jsCode); | ||
77 | TbMsgMetaData metaData = new TbMsgMetaData(); | 66 | TbMsgMetaData metaData = new TbMsgMetaData(); |
78 | metaData.putValue("temp", "10"); | 67 | metaData.putValue("temp", "10"); |
79 | metaData.putValue("humidity", "99"); | 68 | metaData.putValue("humidity", "99"); |
@@ -89,15 +78,15 @@ public class TbJsSwitchNodeTest { | @@ -89,15 +78,15 @@ public class TbJsSwitchNodeTest { | ||
89 | 78 | ||
90 | @Test | 79 | @Test |
91 | public void allowedRelationPassed() throws TbNodeException { | 80 | public void allowedRelationPassed() throws TbNodeException { |
92 | - String jsCode = "function nextRelation(meta, msg) {\n" + | ||
93 | - " if(msg.passed == 5 && meta.temp == 10)\n" + | 81 | + String jsCode = "function nextRelation(metadata, msg) {\n" + |
82 | + " if(msg.passed == 5 && metadata.temp == 10)\n" + | ||
94 | " return 'one'\n" + | 83 | " return 'one'\n" + |
95 | " else\n" + | 84 | " else\n" + |
96 | " return 'two';\n" + | 85 | " return 'two';\n" + |
97 | "};\n" + | 86 | "};\n" + |
98 | "\n" + | 87 | "\n" + |
99 | - "nextRelation(meta, msg);"; | ||
100 | - initWithScript(jsCode, Sets.newHashSet("one", "two"), false); | 88 | + "return nextRelation(metadata, msg);"; |
89 | + initWithScript(jsCode); | ||
101 | TbMsgMetaData metaData = new TbMsgMetaData(); | 90 | TbMsgMetaData metaData = new TbMsgMetaData(); |
102 | metaData.putValue("temp", "10"); | 91 | metaData.putValue("temp", "10"); |
103 | metaData.putValue("humidity", "99"); | 92 | metaData.putValue("humidity", "99"); |
@@ -111,32 +100,9 @@ public class TbJsSwitchNodeTest { | @@ -111,32 +100,9 @@ public class TbJsSwitchNodeTest { | ||
111 | verify(ctx).tellNext(msg, Sets.newHashSet("one")); | 100 | verify(ctx).tellNext(msg, Sets.newHashSet("one")); |
112 | } | 101 | } |
113 | 102 | ||
114 | - @Test | ||
115 | - public void unknownRelationThrowsException() throws TbNodeException { | ||
116 | - String jsCode = "function nextRelation(meta, msg) {\n" + | ||
117 | - " return ['one','nine'];" + | ||
118 | - "};\n" + | ||
119 | - "\n" + | ||
120 | - "nextRelation(meta, msg);"; | ||
121 | - initWithScript(jsCode, Sets.newHashSet("one", "two"), false); | ||
122 | - TbMsgMetaData metaData = new TbMsgMetaData(); | ||
123 | - metaData.putValue("temp", "10"); | ||
124 | - metaData.putValue("humidity", "99"); | ||
125 | - String rawJson = "{\"name\": \"Vit\", \"passed\": 5}"; | ||
126 | - | ||
127 | - TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes()); | ||
128 | - mockJsExecutor(); | ||
129 | - | ||
130 | - node.onMsg(ctx, msg); | ||
131 | - verify(ctx).getJsExecutor(); | ||
132 | - verifyError(msg, "Unsupported relation for switch [nine, one]", IllegalStateException.class); | ||
133 | - } | ||
134 | - | ||
135 | - private void initWithScript(String script, Set<String> relations, boolean routeToAll) throws TbNodeException { | 103 | + private void initWithScript(String script) throws TbNodeException { |
136 | TbJsSwitchNodeConfiguration config = new TbJsSwitchNodeConfiguration(); | 104 | TbJsSwitchNodeConfiguration config = new TbJsSwitchNodeConfiguration(); |
137 | config.setJsScript(script); | 105 | config.setJsScript(script); |
138 | - config.setAllowedRelations(relations); | ||
139 | - config.setRouteToAllWithNoCheck(routeToAll); | ||
140 | ObjectMapper mapper = new ObjectMapper(); | 106 | ObjectMapper mapper = new ObjectMapper(); |
141 | TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); | 107 | TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config)); |
142 | 108 |
@@ -51,7 +51,7 @@ public class TbTransformMsgNodeTest { | @@ -51,7 +51,7 @@ public class TbTransformMsgNodeTest { | ||
51 | 51 | ||
52 | @Test | 52 | @Test |
53 | public void metadataCanBeUpdated() throws TbNodeException { | 53 | public void metadataCanBeUpdated() throws TbNodeException { |
54 | - initWithScript("meta.temp = meta.temp * 10;"); | 54 | + initWithScript("return metadata.temp = metadata.temp * 10;"); |
55 | TbMsgMetaData metaData = new TbMsgMetaData(); | 55 | TbMsgMetaData metaData = new TbMsgMetaData(); |
56 | metaData.putValue("temp", "7"); | 56 | metaData.putValue("temp", "7"); |
57 | metaData.putValue("humidity", "99"); | 57 | metaData.putValue("humidity", "99"); |
@@ -70,7 +70,7 @@ public class TbTransformMsgNodeTest { | @@ -70,7 +70,7 @@ public class TbTransformMsgNodeTest { | ||
70 | 70 | ||
71 | @Test | 71 | @Test |
72 | public void metadataCanBeAdded() throws TbNodeException { | 72 | public void metadataCanBeAdded() throws TbNodeException { |
73 | - initWithScript("meta.newAttr = meta.humidity - msg.passed;"); | 73 | + initWithScript("return metadata.newAttr = metadata.humidity - msg.passed;"); |
74 | TbMsgMetaData metaData = new TbMsgMetaData(); | 74 | TbMsgMetaData metaData = new TbMsgMetaData(); |
75 | metaData.putValue("temp", "7"); | 75 | metaData.putValue("temp", "7"); |
76 | metaData.putValue("humidity", "99"); | 76 | metaData.putValue("humidity", "99"); |
@@ -89,7 +89,7 @@ public class TbTransformMsgNodeTest { | @@ -89,7 +89,7 @@ public class TbTransformMsgNodeTest { | ||
89 | 89 | ||
90 | @Test | 90 | @Test |
91 | public void payloadCanBeUpdated() throws TbNodeException { | 91 | public void payloadCanBeUpdated() throws TbNodeException { |
92 | - initWithScript("msg.passed = msg.passed * meta.temp; msg.bigObj.newProp = 'Ukraine' "); | 92 | + initWithScript("return msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine' "); |
93 | TbMsgMetaData metaData = new TbMsgMetaData(); | 93 | TbMsgMetaData metaData = new TbMsgMetaData(); |
94 | metaData.putValue("temp", "7"); | 94 | metaData.putValue("temp", "7"); |
95 | metaData.putValue("humidity", "99"); | 95 | metaData.putValue("humidity", "99"); |
@@ -32,7 +32,6 @@ const forwardPort = 8080; | @@ -32,7 +32,6 @@ const forwardPort = 8080; | ||
32 | 32 | ||
33 | const ruleNodeUiforwardHost = 'localhost'; | 33 | const ruleNodeUiforwardHost = 'localhost'; |
34 | const ruleNodeUiforwardPort = 8080; | 34 | const ruleNodeUiforwardPort = 8080; |
35 | -//const ruleNodeUiforwardPort = 5000; | ||
36 | 35 | ||
37 | const app = express(); | 36 | const app = express(); |
38 | const server = http.createServer(app); | 37 | const server = http.createServer(app); |
@@ -84,17 +84,32 @@ function JsonObjectEdit($compile, $templateCache, $document, toast, utils) { | @@ -84,17 +84,32 @@ function JsonObjectEdit($compile, $templateCache, $document, toast, utils) { | ||
84 | scope.$watch('contentBody', function (newVal, prevVal) { | 84 | scope.$watch('contentBody', function (newVal, prevVal) { |
85 | if (!angular.equals(newVal, prevVal)) { | 85 | if (!angular.equals(newVal, prevVal)) { |
86 | var object = scope.validate(); | 86 | var object = scope.validate(); |
87 | - ngModelCtrl.$setViewValue(object); | 87 | + if (scope.objectValid) { |
88 | + if (object == null) { | ||
89 | + scope.object = null; | ||
90 | + } else { | ||
91 | + if (scope.object == null) { | ||
92 | + scope.object = {}; | ||
93 | + } | ||
94 | + Object.keys(scope.object).forEach(function (key) { | ||
95 | + delete scope.object[key]; | ||
96 | + }); | ||
97 | + Object.keys(object).forEach(function (key) { | ||
98 | + scope.object[key] = object[key]; | ||
99 | + }); | ||
100 | + } | ||
101 | + ngModelCtrl.$setViewValue(scope.object); | ||
102 | + } | ||
88 | scope.updateValidity(); | 103 | scope.updateValidity(); |
89 | } | 104 | } |
90 | }); | 105 | }); |
91 | 106 | ||
92 | ngModelCtrl.$render = function () { | 107 | ngModelCtrl.$render = function () { |
93 | - var object = ngModelCtrl.$viewValue; | 108 | + scope.object = ngModelCtrl.$viewValue; |
94 | var content = ''; | 109 | var content = ''; |
95 | try { | 110 | try { |
96 | - if (object) { | ||
97 | - content = angular.toJson(object, true); | 111 | + if (scope.object) { |
112 | + content = angular.toJson(scope.object, true); | ||
98 | } | 113 | } |
99 | } catch (e) { | 114 | } catch (e) { |
100 | // | 115 | // |
@@ -1171,6 +1171,7 @@ export default angular.module('thingsboard.locale', []) | @@ -1171,6 +1171,7 @@ export default angular.module('thingsboard.locale', []) | ||
1171 | "debug-mode": "Debug mode" | 1171 | "debug-mode": "Debug mode" |
1172 | }, | 1172 | }, |
1173 | "rulenode": { | 1173 | "rulenode": { |
1174 | + "details": "Details", | ||
1174 | "add": "Add rule node", | 1175 | "add": "Add rule node", |
1175 | "name": "Name", | 1176 | "name": "Name", |
1176 | "name-required": "Name is required.", | 1177 | "name-required": "Name is required.", |
@@ -256,6 +256,9 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, | @@ -256,6 +256,9 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, | ||
256 | vm.isEditingRuleNodeLink = true; | 256 | vm.isEditingRuleNodeLink = true; |
257 | vm.editingRuleNodeLinkIndex = vm.ruleChainModel.edges.indexOf(edge); | 257 | vm.editingRuleNodeLinkIndex = vm.ruleChainModel.edges.indexOf(edge); |
258 | vm.editingRuleNodeLink = angular.copy(edge); | 258 | vm.editingRuleNodeLink = angular.copy(edge); |
259 | + $mdUtil.nextTick(() => { | ||
260 | + vm.ruleNodeLinkForm.$setPristine(); | ||
261 | + }); | ||
259 | } | 262 | } |
260 | }, | 263 | }, |
261 | nodeCallbacks: { | 264 | nodeCallbacks: { |
@@ -266,6 +269,9 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, | @@ -266,6 +269,9 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, | ||
266 | vm.isEditingRuleNode = true; | 269 | vm.isEditingRuleNode = true; |
267 | vm.editingRuleNodeIndex = vm.ruleChainModel.nodes.indexOf(node); | 270 | vm.editingRuleNodeIndex = vm.ruleChainModel.nodes.indexOf(node); |
268 | vm.editingRuleNode = angular.copy(node); | 271 | vm.editingRuleNode = angular.copy(node); |
272 | + $mdUtil.nextTick(() => { | ||
273 | + vm.ruleNodeForm.$setPristine(); | ||
274 | + }); | ||
269 | } | 275 | } |
270 | } | 276 | } |
271 | }, | 277 | }, |
@@ -65,7 +65,8 @@ | @@ -65,7 +65,8 @@ | ||
65 | </div> | 65 | </div> |
66 | <tb-details-sidenav class="tb-rulenode-details-sidenav" | 66 | <tb-details-sidenav class="tb-rulenode-details-sidenav" |
67 | header-title="{{vm.editingRuleNode.name}}" | 67 | header-title="{{vm.editingRuleNode.name}}" |
68 | - header-subtitle="{{'rulenode.rulenode-details' | translate}}" | 68 | + header-subtitle="{{(vm.types.ruleNodeType[vm.editingRuleNode.component.type].name | translate) |
69 | + + ' - ' + vm.editingRuleNode.component.name}}" | ||
69 | is-read-only="false" | 70 | is-read-only="false" |
70 | is-open="vm.isEditingRuleNode" | 71 | is-open="vm.isEditingRuleNode" |
71 | is-always-edit="true" | 72 | is-always-edit="true" |
@@ -76,16 +77,20 @@ | @@ -76,16 +77,20 @@ | ||
76 | <details-buttons tb-help="vm.helpLinkIdForRuleNodeType()" help-container-id="help-container"> | 77 | <details-buttons tb-help="vm.helpLinkIdForRuleNodeType()" help-container-id="help-container"> |
77 | <div id="help-container"></div> | 78 | <div id="help-container"></div> |
78 | </details-buttons> | 79 | </details-buttons> |
79 | - <form name="vm.ruleNodeForm" ng-if="vm.isEditingRuleNode"> | ||
80 | - <tb-rule-node | ||
81 | - rule-node="vm.editingRuleNode" | ||
82 | - rule-chain-id="vm.ruleChain.id.id" | ||
83 | - is-edit="true" | ||
84 | - is-read-only="false" | ||
85 | - on-delete-rule-node="vm.deleteRuleNode(event, vm.editingRuleNode)" | ||
86 | - the-form="vm.ruleNodeForm"> | ||
87 | - </tb-rule-node> | ||
88 | - </form> | 80 | + <md-tabs id="ruleNodeTabs" md-border-bottom flex class="tb-absolute-fill"> |
81 | + <md-tab label="{{ 'rulenode.details' | translate }}"> | ||
82 | + <form name="vm.ruleNodeForm" ng-if="vm.isEditingRuleNode"> | ||
83 | + <tb-rule-node | ||
84 | + rule-node="vm.editingRuleNode" | ||
85 | + rule-chain-id="vm.ruleChain.id.id" | ||
86 | + is-edit="true" | ||
87 | + is-read-only="false" | ||
88 | + on-delete-rule-node="vm.deleteRuleNode(event, vm.editingRuleNode)" | ||
89 | + the-form="vm.ruleNodeForm"> | ||
90 | + </tb-rule-node> | ||
91 | + </form> | ||
92 | + </md-tab> | ||
93 | + </md-tabs> | ||
89 | </tb-details-sidenav> | 94 | </tb-details-sidenav> |
90 | <tb-details-sidenav class="tb-rulenode-link-details-sidenav" | 95 | <tb-details-sidenav class="tb-rulenode-link-details-sidenav" |
91 | header-title="{{vm.editingRuleNodeLink.label}}" | 96 | header-title="{{vm.editingRuleNodeLink.label}}" |
@@ -38,10 +38,15 @@ export default function RuleNodeConfigDirective($compile, $templateCache, $injec | @@ -38,10 +38,15 @@ export default function RuleNodeConfigDirective($compile, $templateCache, $injec | ||
38 | }; | 38 | }; |
39 | 39 | ||
40 | scope.useDefinedDirective = function() { | 40 | scope.useDefinedDirective = function() { |
41 | - return scope.nodeDefinition.configDirective && !scope.definedDirectiveError; | 41 | + return scope.nodeDefinition && |
42 | + scope.nodeDefinition.configDirective && !scope.definedDirectiveError; | ||
42 | }; | 43 | }; |
43 | 44 | ||
44 | - validateDefinedDirective(); | 45 | + scope.$watch('nodeDefinition', () => { |
46 | + if (scope.nodeDefinition) { | ||
47 | + validateDefinedDirective(); | ||
48 | + } | ||
49 | + }); | ||
45 | 50 | ||
46 | function validateDefinedDirective() { | 51 | function validateDefinedDirective() { |
47 | if (scope.nodeDefinition.uiResourceLoadError && scope.nodeDefinition.uiResourceLoadError.length) { | 52 | if (scope.nodeDefinition.uiResourceLoadError && scope.nodeDefinition.uiResourceLoadError.length) { |
@@ -36,10 +36,14 @@ export default function RuleNodeDefinedConfigDirective($compile) { | @@ -36,10 +36,14 @@ export default function RuleNodeDefinedConfigDirective($compile) { | ||
36 | }; | 36 | }; |
37 | 37 | ||
38 | function loadTemplate() { | 38 | function loadTemplate() { |
39 | + if (scope.ruleNodeConfigScope) { | ||
40 | + scope.ruleNodeConfigScope.$destroy(); | ||
41 | + } | ||
39 | var directive = snake_case(attrs.ruleNodeDirective, '-'); | 42 | var directive = snake_case(attrs.ruleNodeDirective, '-'); |
40 | var template = `<${directive} ng-model="configuration" ng-required="required" ng-readonly="readonly"></${directive}>`; | 43 | var template = `<${directive} ng-model="configuration" ng-required="required" ng-readonly="readonly"></${directive}>`; |
41 | element.html(template); | 44 | element.html(template); |
42 | - $compile(element.contents())(scope); | 45 | + scope.ruleNodeConfigScope = scope.$new(); |
46 | + $compile(element.contents())(scope.ruleNodeConfigScope); | ||
43 | } | 47 | } |
44 | 48 | ||
45 | function snake_case(name, separator) { | 49 | function snake_case(name, separator) { |
@@ -21,33 +21,26 @@ | @@ -21,33 +21,26 @@ | ||
21 | 21 | ||
22 | <md-content class="md-padding tb-rulenode" layout="column"> | 22 | <md-content class="md-padding tb-rulenode" layout="column"> |
23 | <fieldset ng-disabled="$root.loading || !isEdit || isReadOnly"> | 23 | <fieldset ng-disabled="$root.loading || !isEdit || isReadOnly"> |
24 | - <md-input-container class="md-block"> | ||
25 | - <label translate>rulenode.type</label> | ||
26 | - <input readonly name="type" ng-model="ruleNode.component.name"> | ||
27 | - </md-input-container> | ||
28 | <section ng-if="ruleNode.component.type != types.ruleNodeType.RULE_CHAIN.value"> | 24 | <section ng-if="ruleNode.component.type != types.ruleNodeType.RULE_CHAIN.value"> |
29 | - <md-input-container class="md-block"> | ||
30 | - <label translate>rulenode.name</label> | ||
31 | - <input required name="name" ng-model="ruleNode.name"> | ||
32 | - <div ng-messages="theForm.name.$error"> | ||
33 | - <div translate ng-message="required">rulenode.name-required</div> | ||
34 | - </div> | ||
35 | - </md-input-container> | ||
36 | - <md-input-container class="md-block"> | ||
37 | - <md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulenode.debug-mode' | translate }}" | ||
38 | - ng-model="ruleNode.debugMode">{{ 'rulenode.debug-mode' | translate }} | ||
39 | - </md-checkbox> | ||
40 | - </md-input-container> | 25 | + <section layout="column" layout-gt-sm="row"> |
26 | + <md-input-container flex class="md-block"> | ||
27 | + <label translate>rulenode.name</label> | ||
28 | + <input required name="name" ng-model="ruleNode.name"> | ||
29 | + <div ng-messages="theForm.name.$error"> | ||
30 | + <div translate ng-message="required">rulenode.name-required</div> | ||
31 | + </div> | ||
32 | + </md-input-container> | ||
33 | + <md-input-container class="md-block"> | ||
34 | + <md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulenode.debug-mode' | translate }}" | ||
35 | + ng-model="ruleNode.debugMode">{{ 'rulenode.debug-mode' | translate }} | ||
36 | + </md-checkbox> | ||
37 | + </md-input-container> | ||
38 | + </section> | ||
41 | <tb-rule-node-config ng-model="ruleNode.configuration" | 39 | <tb-rule-node-config ng-model="ruleNode.configuration" |
42 | ng-required="true" | 40 | ng-required="true" |
43 | node-definition="ruleNode.component.configurationDescriptor.nodeDefinition" | 41 | node-definition="ruleNode.component.configurationDescriptor.nodeDefinition" |
44 | ng-readonly="$root.loading || !isEdit || isReadOnly"> | 42 | ng-readonly="$root.loading || !isEdit || isReadOnly"> |
45 | </tb-rule-node-config> | 43 | </tb-rule-node-config> |
46 | - <!--tb-json-object-edit class="tb-rule-node-configuration-json" ng-model="ruleNode.configuration" | ||
47 | - label="{{ 'rulenode.configuration' | translate }}" | ||
48 | - ng-required="true" | ||
49 | - fill-height="true"> | ||
50 | - </tb-json-object-edit--> | ||
51 | <md-input-container class="md-block"> | 44 | <md-input-container class="md-block"> |
52 | <label translate>rulenode.description</label> | 45 | <label translate>rulenode.description</label> |
53 | <textarea ng-model="ruleNode.additionalInfo.description" rows="2"></textarea> | 46 | <textarea ng-model="ruleNode.additionalInfo.description" rows="2"></textarea> |