Filter nodes UI configuration.

This commit is contained in:
Igor Kulikov 2018-03-29 10:57:28 +03:00
parent fd1199ee1c
commit 784de0836f
26 changed files with 123 additions and 140 deletions

View File

@ -35,7 +35,7 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
nodeDetails = "Evaluate incoming Message with configured JS condition. " +
"If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used." +
"Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code>" +
"Message metadata can be accessed via <code>meta</code> property. For example <code>meta.customerName === 'John';</code>",
"Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeScriptConfig")
@ -47,7 +47,7 @@ public class TbJsFilterNode implements TbNode {
@Override
public void init(TbNodeConfiguration configuration, TbNodeState state) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbJsFilterNodeConfiguration.class);
this.jsEngine = new NashornJsEngine(config.getJsScript());
this.jsEngine = new NashornJsEngine(config.getJsScript(), "Filter");
}
@Override

View File

@ -26,7 +26,7 @@ public class TbJsFilterNodeConfiguration implements NodeConfiguration {
@Override
public TbJsFilterNodeConfiguration defaultConfiguration() {
TbJsFilterNodeConfiguration configuration = new TbJsFilterNodeConfiguration();
configuration.setJsScript("msg.passed < 15 && msg.name === 'Vit' && meta.temp == 10 && msg.bigObj.prop == 42;");
configuration.setJsScript("return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 10 && msg.bigObj.prop == 42;");
return configuration;
}
}

View File

@ -36,7 +36,9 @@ import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " +
"If Array is empty - message not routed to next Node. " +
"Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code> " +
"Message metadata can be accessed via <code>meta</code> property. For example <code>meta.customerName === 'John';</code>")
"Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeSwitchConfig")
public class TbJsSwitchNode implements TbNode {
private TbJsSwitchNodeConfiguration config;
@ -45,22 +47,11 @@ public class TbJsSwitchNode implements TbNode {
@Override
public void init(TbNodeConfiguration configuration, TbNodeState state) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbJsSwitchNodeConfiguration.class);
if (config.getAllowedRelations().size() < 1) {
String message = "Switch node should have at least 1 relation";
log.error(message);
throw new IllegalStateException(message);
}
if (!config.isRouteToAllWithNoCheck()) {
this.jsEngine = new NashornJsEngine(config.getJsScript());
}
this.jsEngine = new NashornJsEngine(config.getJsScript(), "Switch");
}
@Override
public void onMsg(TbContext ctx, TbMsg msg) {
if (config.isRouteToAllWithNoCheck()) {
ctx.tellNext(msg, config.getAllowedRelations());
return;
}
ListeningExecutor jsExecutor = ctx.getJsExecutor();
withCallback(jsExecutor.executeAsync(() -> jsEngine.executeSwitch(toBindings(msg))),
result -> processSwitch(ctx, msg, result),
@ -68,15 +59,7 @@ public class TbJsSwitchNode implements TbNode {
}
private void processSwitch(TbContext ctx, TbMsg msg, Set<String> nextRelations) {
if (validateRelations(nextRelations)) {
ctx.tellNext(msg, nextRelations);
} else {
ctx.tellError(msg, new IllegalStateException("Unsupported relation for switch " + nextRelations));
}
}
private boolean validateRelations(Set<String> nextRelations) {
return config.getAllowedRelations().containsAll(nextRelations);
}
private Bindings toBindings(TbMsg msg) {

View File

@ -25,19 +25,15 @@ import java.util.Set;
public class TbJsSwitchNodeConfiguration implements NodeConfiguration {
private String jsScript;
private Set<String> allowedRelations;
private boolean routeToAllWithNoCheck;
@Override
public TbJsSwitchNodeConfiguration defaultConfiguration() {
TbJsSwitchNodeConfiguration configuration = new TbJsSwitchNodeConfiguration();
configuration.setJsScript("function nextRelation(meta, msg) {\n" +
configuration.setJsScript("function nextRelation(metadata, msg) {\n" +
" return ['one','nine'];" +
"};\n" +
"\n" +
"nextRelation(meta, msg);");
configuration.setAllowedRelations(Sets.newHashSet("one", "two"));
configuration.setRouteToAllWithNoCheck(false);
"return nextRelation(metadata, msg);");
return configuration;
}
}

View File

@ -31,7 +31,9 @@ import org.thingsboard.server.common.msg.TbMsg;
configClazz = TbMsgTypeFilterNodeConfiguration.class,
nodeDescription = "Filter incoming messages by Message Type",
nodeDetails = "Evaluate incoming Message with configured JS condition. " +
"If incoming MessageType is expected - send Message via <b>Success</b> chain, otherwise <b>Failure</b> chain is used.")
"If incoming MessageType is expected - send Message via <b>Success</b> chain, otherwise <b>Failure</b> chain is used.",
uiResources = {"static/rulenode/rulenode-core-config.js", "static/rulenode/rulenode-core-config.css"},
configDirective = "tbFilterNodeMessageTypeConfig")
public class TbMsgTypeFilterNode implements TbNode {
TbMsgTypeFilterNodeConfiguration config;

View File

@ -33,7 +33,7 @@ public class TbMsgTypeFilterNodeConfiguration implements NodeConfiguration {
@Override
public TbMsgTypeFilterNodeConfiguration defaultConfiguration() {
TbMsgTypeFilterNodeConfiguration configuration = new TbMsgTypeFilterNodeConfiguration();
configuration.setMessageTypes(Arrays.asList("GET_ATTRIBUTES","POST_ATTRIBUTES","POST_TELEMETRY","RPC_REQUEST"));
configuration.setMessageTypes(Arrays.asList("POST_ATTRIBUTES","POST_TELEMETRY","RPC_REQUEST"));
return configuration;
}
}

View File

@ -34,14 +34,20 @@ import java.util.Set;
@Slf4j
public class NashornJsEngine {
public static final String METADATA = "meta";
public static final String METADATA = "metadata";
public static final String DATA = "msg";
private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msg, metadata) { ";
private static final String JS_WRAPPER_SUFFIX_TEMPLATE = "}\n %s(msg, metadata);";
private static NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
private CompiledScript engine;
public NashornJsEngine(String script) {
engine = compileScript(script);
public NashornJsEngine(String script, String functionName) {
String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, functionName);
String jsWrapperSuffix = String.format(JS_WRAPPER_SUFFIX_TEMPLATE, functionName);
engine = compileScript(jsWrapperPrefix + script + jsWrapperSuffix);
}
private static CompiledScript compileScript(String script) {
@ -58,15 +64,15 @@ public class NashornJsEngine {
public static Bindings bindMsg(TbMsg msg) {
try {
Bindings bindings = new SimpleBindings();
bindings.put(METADATA, msg.getMetaData().getData());
if (ArrayUtils.isNotEmpty(msg.getData())) {
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(msg.getData());
Map map = mapper.treeToValue(jsonNode, Map.class);
bindings.put(DATA, map);
} else {
bindings.put(DATA, Collections.emptyMap());
}
bindings.put(METADATA, msg.getMetaData().getData());
return bindings;
} catch (Throwable th) {
throw new IllegalArgumentException("Cannot bind js args", th);

View File

@ -42,7 +42,7 @@ import static org.thingsboard.server.common.data.DataConstants.*;
nodeDescription = "Add Message Originator Attributes or Latest Telemetry into Message Metadata",
nodeDetails = "If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata " +
"with specific prefix: <i>cs/shared/ss</i>. To access those attributes in other nodes this template can be used " +
"<code>meta.cs.temperature</code> or <code>meta.shared.limit</code> " +
"<code>metadata.cs.temperature</code> or <code>metadata.shared.limit</code> " +
"If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.")
public class TbGetAttributesNode implements TbNode {

View File

@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message Metadata",
nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
"To access those attributes in other nodes this template can be used " +
"<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
"<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> {
@Override

View File

@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
"If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. " +
"If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
"To access those attributes in other nodes this template can be used " +
"<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
"<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> {
private TbGetRelatedAttrNodeConfiguration config;

View File

@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata",
nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
"To access those attributes in other nodes this template can be used " +
"<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
"<code>metadata.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> {
@Override

View File

@ -30,7 +30,7 @@ import javax.script.Bindings;
configClazz = TbTransformMsgNodeConfiguration.class,
nodeDescription = "Change Message payload and Metadata using JavaScript",
nodeDetails = "JavaScript function recieve 2 input parameters that can be changed inside.<br/> " +
"<code>meta</code> - is a Message metadata.<br/>" +
"<code>metadata</code> - is a Message metadata.<br/>" +
"<code>msg</code> - is a Message payload.<br/>Any properties can be changed/removed/added in those objects.")
public class TbTransformMsgNode extends TbAbstractTransformNode {
@ -40,7 +40,7 @@ public class TbTransformMsgNode extends TbAbstractTransformNode {
@Override
public void init(TbNodeConfiguration configuration, TbNodeState state) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbTransformMsgNodeConfiguration.class);
this.jsEngine = new NashornJsEngine(config.getJsScript());
this.jsEngine = new NashornJsEngine(config.getJsScript(), "Transform");
setConfig(config);
}

View File

@ -27,7 +27,7 @@ public class TbTransformMsgNodeConfiguration extends TbTransformNodeConfiguratio
public TbTransformMsgNodeConfiguration defaultConfiguration() {
TbTransformMsgNodeConfiguration configuration = new TbTransformMsgNodeConfiguration();
configuration.setStartNewChain(false);
configuration.setJsScript("msg.passed = msg.passed * meta.temp; msg.bigObj.newProp = 'Ukraine' ");
configuration.setJsScript("return msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine' ");
return configuration;
}
}

View File

@ -0,0 +1,2 @@
.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}
/*# sourceMappingURL=rulenode-core-config.css.map*/

View File

@ -51,7 +51,7 @@ public class TbJsFilterNodeTest {
@Test
public void falseEvaluationDoNotSendMsg() throws TbNodeException {
initWithScript("10 > 15;");
initWithScript("return 10 > 15;");
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), "{}".getBytes());
mockJsExecutor();
@ -64,7 +64,7 @@ public class TbJsFilterNodeTest {
@Test
public void notValidMsgDataThrowsException() throws TbNodeException {
initWithScript("10 > 15;");
initWithScript("return 10 > 15;");
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), new byte[4]);
when(ctx.getJsExecutor()).thenReturn(executor);
@ -77,7 +77,7 @@ public class TbJsFilterNodeTest {
@Test
public void exceptionInJsThrowsException() throws TbNodeException {
initWithScript("meta.temp.curr < 15;");
initWithScript("return metadata.temp.curr < 15;");
TbMsgMetaData metaData = new TbMsgMetaData();
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, "{}".getBytes());
mockJsExecutor();
@ -89,12 +89,12 @@ public class TbJsFilterNodeTest {
@Test(expected = IllegalArgumentException.class)
public void notValidScriptThrowsException() throws TbNodeException {
initWithScript("10 > 15 asdq out");
initWithScript("return 10 > 15 asdq out");
}
@Test
public void metadataConditionCanBeFalse() throws TbNodeException {
initWithScript("meta.humidity < 15;");
initWithScript("return metadata.humidity < 15;");
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "10");
metaData.putValue("humidity", "99");
@ -109,7 +109,7 @@ public class TbJsFilterNodeTest {
@Test
public void metadataConditionCanBeTrue() throws TbNodeException {
initWithScript("meta.temp < 15;");
initWithScript("return metadata.temp < 15;");
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "10");
metaData.putValue("humidity", "99");
@ -123,7 +123,7 @@ public class TbJsFilterNodeTest {
@Test
public void msgJsonParsedAndBinded() throws TbNodeException {
initWithScript("msg.passed < 15 && msg.name === 'Vit' && meta.temp == 10 && msg.bigObj.prop == 42;");
initWithScript("return msg.passed < 15 && msg.name === 'Vit' && metadata.temp == 10 && msg.bigObj.prop == 42;");
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "10");
metaData.putValue("humidity", "99");

View File

@ -52,28 +52,17 @@ public class TbJsSwitchNodeTest {
@Mock
private ListeningExecutor executor;
@Test
public void routeToAllDoNotEvaluatesJs() throws TbNodeException {
HashSet<String> relations = Sets.newHashSet("one", "two");
initWithScript("test qwerty", relations, true);
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, new TbMsgMetaData(), "{}".getBytes());
node.onMsg(ctx, msg);
verify(ctx).tellNext(msg, relations);
verifyNoMoreInteractions(ctx, executor);
}
@Test
public void multipleRoutesAreAllowed() throws TbNodeException {
String jsCode = "function nextRelation(meta, msg) {\n" +
" if(msg.passed == 5 && meta.temp == 10)\n" +
String jsCode = "function nextRelation(metadata, msg) {\n" +
" if(msg.passed == 5 && metadata.temp == 10)\n" +
" return ['three', 'one']\n" +
" else\n" +
" return 'two';\n" +
"};\n" +
"\n" +
"nextRelation(meta, msg);";
initWithScript(jsCode, Sets.newHashSet("one", "two", "three"), false);
"return nextRelation(metadata, msg);";
initWithScript(jsCode);
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "10");
metaData.putValue("humidity", "99");
@ -89,15 +78,15 @@ public class TbJsSwitchNodeTest {
@Test
public void allowedRelationPassed() throws TbNodeException {
String jsCode = "function nextRelation(meta, msg) {\n" +
" if(msg.passed == 5 && meta.temp == 10)\n" +
String jsCode = "function nextRelation(metadata, msg) {\n" +
" if(msg.passed == 5 && metadata.temp == 10)\n" +
" return 'one'\n" +
" else\n" +
" return 'two';\n" +
"};\n" +
"\n" +
"nextRelation(meta, msg);";
initWithScript(jsCode, Sets.newHashSet("one", "two"), false);
"return nextRelation(metadata, msg);";
initWithScript(jsCode);
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "10");
metaData.putValue("humidity", "99");
@ -111,32 +100,9 @@ public class TbJsSwitchNodeTest {
verify(ctx).tellNext(msg, Sets.newHashSet("one"));
}
@Test
public void unknownRelationThrowsException() throws TbNodeException {
String jsCode = "function nextRelation(meta, msg) {\n" +
" return ['one','nine'];" +
"};\n" +
"\n" +
"nextRelation(meta, msg);";
initWithScript(jsCode, Sets.newHashSet("one", "two"), false);
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "10");
metaData.putValue("humidity", "99");
String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", null, metaData, rawJson.getBytes());
mockJsExecutor();
node.onMsg(ctx, msg);
verify(ctx).getJsExecutor();
verifyError(msg, "Unsupported relation for switch [nine, one]", IllegalStateException.class);
}
private void initWithScript(String script, Set<String> relations, boolean routeToAll) throws TbNodeException {
private void initWithScript(String script) throws TbNodeException {
TbJsSwitchNodeConfiguration config = new TbJsSwitchNodeConfiguration();
config.setJsScript(script);
config.setAllowedRelations(relations);
config.setRouteToAllWithNoCheck(routeToAll);
ObjectMapper mapper = new ObjectMapper();
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(mapper.valueToTree(config));

View File

@ -51,7 +51,7 @@ public class TbTransformMsgNodeTest {
@Test
public void metadataCanBeUpdated() throws TbNodeException {
initWithScript("meta.temp = meta.temp * 10;");
initWithScript("return metadata.temp = metadata.temp * 10;");
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "7");
metaData.putValue("humidity", "99");
@ -70,7 +70,7 @@ public class TbTransformMsgNodeTest {
@Test
public void metadataCanBeAdded() throws TbNodeException {
initWithScript("meta.newAttr = meta.humidity - msg.passed;");
initWithScript("return metadata.newAttr = metadata.humidity - msg.passed;");
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "7");
metaData.putValue("humidity", "99");
@ -89,7 +89,7 @@ public class TbTransformMsgNodeTest {
@Test
public void payloadCanBeUpdated() throws TbNodeException {
initWithScript("msg.passed = msg.passed * meta.temp; msg.bigObj.newProp = 'Ukraine' ");
initWithScript("return msg.passed = msg.passed * metadata.temp; msg.bigObj.newProp = 'Ukraine' ");
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("temp", "7");
metaData.putValue("humidity", "99");

View File

@ -32,7 +32,6 @@ const forwardPort = 8080;
const ruleNodeUiforwardHost = 'localhost';
const ruleNodeUiforwardPort = 8080;
//const ruleNodeUiforwardPort = 5000;
const app = express();
const server = http.createServer(app);

View File

@ -84,17 +84,32 @@ function JsonObjectEdit($compile, $templateCache, $document, toast, utils) {
scope.$watch('contentBody', function (newVal, prevVal) {
if (!angular.equals(newVal, prevVal)) {
var object = scope.validate();
ngModelCtrl.$setViewValue(object);
if (scope.objectValid) {
if (object == null) {
scope.object = null;
} else {
if (scope.object == null) {
scope.object = {};
}
Object.keys(scope.object).forEach(function (key) {
delete scope.object[key];
});
Object.keys(object).forEach(function (key) {
scope.object[key] = object[key];
});
}
ngModelCtrl.$setViewValue(scope.object);
}
scope.updateValidity();
}
});
ngModelCtrl.$render = function () {
var object = ngModelCtrl.$viewValue;
scope.object = ngModelCtrl.$viewValue;
var content = '';
try {
if (object) {
content = angular.toJson(object, true);
if (scope.object) {
content = angular.toJson(scope.object, true);
}
} catch (e) {
//

View File

@ -1171,6 +1171,7 @@ export default angular.module('thingsboard.locale', [])
"debug-mode": "Debug mode"
},
"rulenode": {
"details": "Details",
"add": "Add rule node",
"name": "Name",
"name-required": "Name is required.",

View File

@ -256,6 +256,9 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
vm.isEditingRuleNodeLink = true;
vm.editingRuleNodeLinkIndex = vm.ruleChainModel.edges.indexOf(edge);
vm.editingRuleNodeLink = angular.copy(edge);
$mdUtil.nextTick(() => {
vm.ruleNodeLinkForm.$setPristine();
});
}
},
nodeCallbacks: {
@ -266,6 +269,9 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
vm.isEditingRuleNode = true;
vm.editingRuleNodeIndex = vm.ruleChainModel.nodes.indexOf(node);
vm.editingRuleNode = angular.copy(node);
$mdUtil.nextTick(() => {
vm.ruleNodeForm.$setPristine();
});
}
}
},

View File

@ -65,7 +65,8 @@
</div>
<tb-details-sidenav class="tb-rulenode-details-sidenav"
header-title="{{vm.editingRuleNode.name}}"
header-subtitle="{{'rulenode.rulenode-details' | translate}}"
header-subtitle="{{(vm.types.ruleNodeType[vm.editingRuleNode.component.type].name | translate)
+ ' - ' + vm.editingRuleNode.component.name}}"
is-read-only="false"
is-open="vm.isEditingRuleNode"
is-always-edit="true"
@ -76,6 +77,8 @@
<details-buttons tb-help="vm.helpLinkIdForRuleNodeType()" help-container-id="help-container">
<div id="help-container"></div>
</details-buttons>
<md-tabs id="ruleNodeTabs" md-border-bottom flex class="tb-absolute-fill">
<md-tab label="{{ 'rulenode.details' | translate }}">
<form name="vm.ruleNodeForm" ng-if="vm.isEditingRuleNode">
<tb-rule-node
rule-node="vm.editingRuleNode"
@ -86,6 +89,8 @@
the-form="vm.ruleNodeForm">
</tb-rule-node>
</form>
</md-tab>
</md-tabs>
</tb-details-sidenav>
<tb-details-sidenav class="tb-rulenode-link-details-sidenav"
header-title="{{vm.editingRuleNodeLink.label}}"

View File

@ -38,10 +38,15 @@ export default function RuleNodeConfigDirective($compile, $templateCache, $injec
};
scope.useDefinedDirective = function() {
return scope.nodeDefinition.configDirective && !scope.definedDirectiveError;
return scope.nodeDefinition &&
scope.nodeDefinition.configDirective && !scope.definedDirectiveError;
};
scope.$watch('nodeDefinition', () => {
if (scope.nodeDefinition) {
validateDefinedDirective();
}
});
function validateDefinedDirective() {
if (scope.nodeDefinition.uiResourceLoadError && scope.nodeDefinition.uiResourceLoadError.length) {

View File

@ -36,10 +36,14 @@ export default function RuleNodeDefinedConfigDirective($compile) {
};
function loadTemplate() {
if (scope.ruleNodeConfigScope) {
scope.ruleNodeConfigScope.$destroy();
}
var directive = snake_case(attrs.ruleNodeDirective, '-');
var template = `<${directive} ng-model="configuration" ng-required="required" ng-readonly="readonly"></${directive}>`;
element.html(template);
$compile(element.contents())(scope);
scope.ruleNodeConfigScope = scope.$new();
$compile(element.contents())(scope.ruleNodeConfigScope);
}
function snake_case(name, separator) {

View File

@ -21,12 +21,9 @@
<md-content class="md-padding tb-rulenode" layout="column">
<fieldset ng-disabled="$root.loading || !isEdit || isReadOnly">
<md-input-container class="md-block">
<label translate>rulenode.type</label>
<input readonly name="type" ng-model="ruleNode.component.name">
</md-input-container>
<section ng-if="ruleNode.component.type != types.ruleNodeType.RULE_CHAIN.value">
<md-input-container class="md-block">
<section layout="column" layout-gt-sm="row">
<md-input-container flex class="md-block">
<label translate>rulenode.name</label>
<input required name="name" ng-model="ruleNode.name">
<div ng-messages="theForm.name.$error">
@ -38,16 +35,12 @@
ng-model="ruleNode.debugMode">{{ 'rulenode.debug-mode' | translate }}
</md-checkbox>
</md-input-container>
</section>
<tb-rule-node-config ng-model="ruleNode.configuration"
ng-required="true"
node-definition="ruleNode.component.configurationDescriptor.nodeDefinition"
ng-readonly="$root.loading || !isEdit || isReadOnly">
</tb-rule-node-config>
<!--tb-json-object-edit class="tb-rule-node-configuration-json" ng-model="ruleNode.configuration"
label="{{ 'rulenode.configuration' | translate }}"
ng-required="true"
fill-height="true">
</tb-json-object-edit-->
<md-input-container class="md-block">
<label translate>rulenode.description</label>
<textarea ng-model="ruleNode.additionalInfo.description" rows="2"></textarea>