Browse Source

RuleNode annotation refactoring

pull/725/head
Igor Kulikov 9 years ago
parent
commit
8f3fbc945d
  1. 90
      application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java
  2. 43
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ActionNode.java
  3. 49
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EnrichmentNode.java
  4. 33
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NodeDefinition.java
  5. 11
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java
  6. 51
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TransformationNode.java
  7. 2
      rule-engine/rule-engine-api/src/main/resources/EmptyNodeConfig.json
  8. 11
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java
  9. 9
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java
  10. 7
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java
  11. 12
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java
  12. 9
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java
  13. 13
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java
  14. 9
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java
  15. 5
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java
  16. 11
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java
  17. 15
      ui/src/app/common/types.constant.js
  18. 3
      ui/src/app/rulechain/rulechain.controller.js
  19. 29
      ui/src/app/rulechain/rulechain.scss

90
application/src/main/java/org/thingsboard/server/service/component/AnnotationComponentDiscoveryService.java

@ -16,6 +16,8 @@
package org.thingsboard.server.service.component; package org.thingsboard.server.service.component;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -26,16 +28,14 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.rule.engine.api.ActionNode; import org.thingsboard.rule.engine.api.*;
import org.thingsboard.rule.engine.api.EnrichmentNode;
import org.thingsboard.rule.engine.api.FilterNode;
import org.thingsboard.rule.engine.api.TransformationNode;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.dao.component.ComponentDescriptorService; import org.thingsboard.server.dao.component.ComponentDescriptorService;
import org.thingsboard.server.extensions.api.component.*; import org.thingsboard.server.extensions.api.component.*;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.io.IOException;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -70,6 +70,24 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
} }
} }
private void registerRuleNodeComponents() {
Set<BeanDefinition> ruleNodeBeanDefinitions = getBeanDefinitions(RuleNode.class);
for (BeanDefinition def : ruleNodeBeanDefinitions) {
try {
String clazzName = def.getBeanClassName();
Class<?> clazz = Class.forName(clazzName);
RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class);
ComponentType type = ruleNodeAnnotation.type();
ComponentDescriptor component = scanAndPersistComponent(def, type);
components.put(component.getClazz(), component);
componentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
} catch (Exception e) {
log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e);
throw new RuntimeException(e);
}
}
}
private void registerComponents(ComponentType type, Class<? extends Annotation> annotation) { private void registerComponents(ComponentType type, Class<? extends Annotation> annotation) {
List<ComponentDescriptor> components = persist(getBeanDefinitions(annotation), type); List<ComponentDescriptor> components = persist(getBeanDefinitions(annotation), type);
componentsMap.put(type, components); componentsMap.put(type, components);
@ -97,34 +115,25 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
String descriptorResourceName; String descriptorResourceName;
switch (type) { switch (type) {
case ENRICHMENT: case ENRICHMENT:
EnrichmentNode enrichmentAnnotation = clazz.getAnnotation(EnrichmentNode.class);
scannedComponent.setName(enrichmentAnnotation.name());
scannedComponent.setScope(enrichmentAnnotation.scope());
descriptorResourceName = enrichmentAnnotation.descriptor();
break;
case FILTER: case FILTER:
FilterNode filterAnnotation = clazz.getAnnotation(FilterNode.class);
scannedComponent.setName(filterAnnotation.name());
scannedComponent.setScope(filterAnnotation.scope());
descriptorResourceName = filterAnnotation.descriptor();
break;
case TRANSFORMATION: case TRANSFORMATION:
TransformationNode trAnnotation = clazz.getAnnotation(TransformationNode.class);
scannedComponent.setName(trAnnotation.name());
scannedComponent.setScope(trAnnotation.scope());
descriptorResourceName = trAnnotation.descriptor();
break;
case ACTION: case ACTION:
ActionNode actionAnnotation = clazz.getAnnotation(ActionNode.class); RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class);
scannedComponent.setName(actionAnnotation.name()); scannedComponent.setName(ruleNodeAnnotation.name());
scannedComponent.setScope(actionAnnotation.scope()); scannedComponent.setScope(ruleNodeAnnotation.scope());
descriptorResourceName = actionAnnotation.descriptor(); NodeDefinition nodeDefinition = prepareNodeDefinition(ruleNodeAnnotation);
ObjectNode configurationDescriptor = mapper.createObjectNode();
JsonNode node = mapper.valueToTree(nodeDefinition);
configurationDescriptor.set("nodeDefinition", node);
scannedComponent.setConfigurationDescriptor(configurationDescriptor);
break; break;
case OLD_ACTION: case OLD_ACTION:
Action oldActionAnnotation = clazz.getAnnotation(Action.class); Action oldActionAnnotation = clazz.getAnnotation(Action.class);
scannedComponent.setName(oldActionAnnotation.name()); scannedComponent.setName(oldActionAnnotation.name());
scannedComponent.setScope(oldActionAnnotation.scope()); scannedComponent.setScope(oldActionAnnotation.scope());
descriptorResourceName = oldActionAnnotation.descriptor(); descriptorResourceName = oldActionAnnotation.descriptor();
scannedComponent.setConfigurationDescriptor(mapper.readTree(
Resources.toString(Resources.getResource(descriptorResourceName), Charsets.UTF_8)));
break; break;
case PLUGIN: case PLUGIN:
Plugin pluginAnnotation = clazz.getAnnotation(Plugin.class); Plugin pluginAnnotation = clazz.getAnnotation(Plugin.class);
@ -143,12 +152,12 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
} }
} }
scannedComponent.setActions(Arrays.stream(pluginAnnotation.actions()).map(Class::getName).collect(Collectors.joining(","))); scannedComponent.setActions(Arrays.stream(pluginAnnotation.actions()).map(Class::getName).collect(Collectors.joining(",")));
scannedComponent.setConfigurationDescriptor(mapper.readTree(
Resources.toString(Resources.getResource(descriptorResourceName), Charsets.UTF_8)));
break; break;
default: default:
throw new RuntimeException(type + " is not supported yet!"); throw new RuntimeException(type + " is not supported yet!");
} }
scannedComponent.setConfigurationDescriptor(mapper.readTree(
Resources.toString(Resources.getResource(descriptorResourceName), Charsets.UTF_8)));
scannedComponent.setClazz(clazzName); scannedComponent.setClazz(clazzName);
log.info("Processing scanned component: {}", scannedComponent); log.info("Processing scanned component: {}", scannedComponent);
} catch (Exception e) { } catch (Exception e) {
@ -171,6 +180,20 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
return scannedComponent; return scannedComponent;
} }
private NodeDefinition prepareNodeDefinition(RuleNode nodeAnnotation) throws IOException {
NodeDefinition nodeDefinition = new NodeDefinition();
nodeDefinition.setDetails(nodeAnnotation.nodeDetails());
nodeDefinition.setDescription(nodeAnnotation.nodeDescription());
nodeDefinition.setInEnabled(nodeAnnotation.inEnabled());
nodeDefinition.setOutEnabled(nodeAnnotation.outEnabled());
nodeDefinition.setRelationTypes(nodeAnnotation.relationTypes());
nodeDefinition.setCustomRelations(nodeAnnotation.customRelations());
String defaultConfigResourceName = nodeAnnotation.defaultConfigResource();
nodeDefinition.setDefaultConfiguration(mapper.readTree(
Resources.toString(Resources.getResource(defaultConfigResourceName), Charsets.UTF_8)));
return nodeDefinition;
}
private Set<BeanDefinition> getBeanDefinitions(Class<? extends Annotation> componentType) { private Set<BeanDefinition> getBeanDefinitions(Class<? extends Annotation> componentType) {
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(componentType)); scanner.addIncludeFilter(new AnnotationTypeFilter(componentType));
@ -183,13 +206,8 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
@Override @Override
public void discoverComponents() { public void discoverComponents() {
registerComponents(ComponentType.ENRICHMENT, EnrichmentNode.class);
registerComponents(ComponentType.FILTER, FilterNode.class);
registerComponents(ComponentType.TRANSFORMATION, TransformationNode.class);
registerComponents(ComponentType.ACTION, ActionNode.class); registerRuleNodeComponents();
registerComponents(ComponentType.OLD_ACTION, Action.class); registerComponents(ComponentType.OLD_ACTION, Action.class);
@ -200,15 +218,19 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
@Override @Override
public List<ComponentDescriptor> getComponents(ComponentType type) { public List<ComponentDescriptor> getComponents(ComponentType type) {
return Collections.unmodifiableList(componentsMap.get(type)); if (componentsMap.containsKey(type)) {
return Collections.unmodifiableList(componentsMap.get(type));
} else {
return Collections.emptyList();
}
} }
@Override @Override
public List<ComponentDescriptor> getComponents(Set<ComponentType> types) { public List<ComponentDescriptor> getComponents(Set<ComponentType> types) {
List<ComponentDescriptor> result = new ArrayList<>(); List<ComponentDescriptor> result = new ArrayList<>();
for (ComponentType type : types) { types.stream().filter(type -> componentsMap.containsKey(type)).forEach(type -> {
result.addAll(componentsMap.get(type)); result.addAll(componentsMap.get(type));
} });
return Collections.unmodifiableList(result); return Collections.unmodifiableList(result);
} }

43
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ActionNode.java

@ -1,43 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.rule.engine.api;
import org.thingsboard.server.common.data.plugin.ComponentScope;
import org.thingsboard.server.extensions.api.component.EmptyComponentConfiguration;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Andrew Shvayka
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ActionNode {
String name();
ComponentScope scope() default ComponentScope.TENANT;
String descriptor() default "EmptyNodeDescriptor.json";
String[] relationTypes() default {"Success","Failure"};
boolean customRelations() default false;
}

49
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EnrichmentNode.java

@ -1,49 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.rule.engine.api;
import org.thingsboard.server.common.data.plugin.ComponentScope;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Andrew Shvayka
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EnrichmentNode {
String name();
String nodeDescription();
String nodeDetails();
boolean inEnabled() default true;
boolean outEnabled() default true;
ComponentScope scope() default ComponentScope.TENANT;
String descriptor() default "EmptyNodeDescriptor.json";
String[] relationTypes() default {"Success", "Failure"};
boolean customRelations() default false;
}

33
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NodeDefinition.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.rule.engine.api;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
@Data
public class NodeDefinition {
private String details;
private String description;
private boolean inEnabled;
private boolean outEnabled;
String[] relationTypes;
boolean customRelations;
JsonNode defaultConfiguration;
}

11
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/FilterNode.java → rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java

@ -16,18 +16,18 @@
package org.thingsboard.rule.engine.api; package org.thingsboard.rule.engine.api;
import org.thingsboard.server.common.data.plugin.ComponentScope; import org.thingsboard.server.common.data.plugin.ComponentScope;
import org.thingsboard.server.common.data.plugin.ComponentType;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/**
* @author Andrew Shvayka
*/
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface FilterNode { public @interface RuleNode {
ComponentType type();
String name(); String name();
@ -41,10 +41,9 @@ public @interface FilterNode {
ComponentScope scope() default ComponentScope.TENANT; ComponentScope scope() default ComponentScope.TENANT;
String descriptor() default "EmptyNodeDescriptor.json"; String defaultConfigResource() default "EmptyNodeConfig.json";
String[] relationTypes() default {"Success", "Failure"}; String[] relationTypes() default {"Success", "Failure"};
boolean customRelations() default false; boolean customRelations() default false;
} }

51
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TransformationNode.java

@ -1,51 +0,0 @@
/**
* Copyright © 2016-2018 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.rule.engine.api;
import org.thingsboard.server.common.data.plugin.ComponentScope;
import org.thingsboard.server.extensions.api.component.EmptyComponentConfiguration;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Andrew Shvayka
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TransformationNode {
String name();
String nodeDescription();
String nodeDetails();
boolean inEnabled() default true;
boolean outEnabled() default true;
ComponentScope scope() default ComponentScope.TENANT;
String descriptor() default "EmptyNodeDescriptor.json";
String[] relationTypes() default {"Success","Failure"};
boolean customRelations() default false;
}

2
rule-engine/rule-engine-api/src/main/resources/EmptyNodeConfig.json

@ -0,0 +1,2 @@
{
}

11
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java

@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.TbNodeUtils; import org.thingsboard.rule.engine.TbNodeUtils;
import org.thingsboard.rule.engine.api.*; import org.thingsboard.rule.engine.api.*;
import org.thingsboard.rule.engine.js.NashornJsEngine; import org.thingsboard.rule.engine.js.NashornJsEngine;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import javax.script.Bindings; import javax.script.Bindings;
@ -26,12 +27,14 @@ import javax.script.Bindings;
import static org.thingsboard.rule.engine.DonAsynchron.withCallback; import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
@Slf4j @Slf4j
@FilterNode(name = "script", relationTypes = {"True", "False", "Failure"}, @RuleNode(
type = ComponentType.FILTER,
name = "script", relationTypes = {"True", "False", "Failure"},
nodeDescription = "Filter incoming messages using JS script", nodeDescription = "Filter incoming messages using JS script",
nodeDetails = "Evaluate incoming Message with configured JS condition. " + nodeDetails = "Evaluate incoming Message with configured JS condition. " +
"If 'True' - send Message via 'True' chain, otherwise 'False' chain is used." + "If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used." +
"Message payload can be accessed via 'msg' property. For example 'msg.temperature < 10;'" + "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code>" +
"Message metadata can be accessed via 'meta' property. For example 'meta.customerName === 'John';'") "Message metadata can be accessed via <code>meta</code> property. For example <code>meta.customerName === 'John';</code>")
public class TbJsFilterNode implements TbNode { public class TbJsFilterNode implements TbNode {
private TbJsFilterNodeConfiguration config; private TbJsFilterNodeConfiguration config;

9
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java

@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.TbNodeUtils; import org.thingsboard.rule.engine.TbNodeUtils;
import org.thingsboard.rule.engine.api.*; import org.thingsboard.rule.engine.api.*;
import org.thingsboard.rule.engine.js.NashornJsEngine; import org.thingsboard.rule.engine.js.NashornJsEngine;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import javax.script.Bindings; import javax.script.Bindings;
@ -27,12 +28,14 @@ import java.util.Set;
import static org.thingsboard.rule.engine.DonAsynchron.withCallback; import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
@Slf4j @Slf4j
@FilterNode(name = "switch", customRelations = true, @RuleNode(
type = ComponentType.FILTER,
name = "switch", customRelations = true,
nodeDescription = "Route incoming Message to one or multiple output chains", nodeDescription = "Route incoming Message to one or multiple output chains",
nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " + 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. " + "If Array is empty - message not routed to next Node. " +
"Message payload can be accessed via 'msg' property. For example 'msg.temperature < 10;' " + "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code> " +
"Message metadata can be accessed via 'meta' property. For example 'meta.customerName === 'John';' ") "Message metadata can be accessed via <code>meta</code> property. For example <code>meta.customerName === 'John';</code>")
public class TbJsSwitchNode implements TbNode { public class TbJsSwitchNode implements TbNode {
private TbJsSwitchNodeConfiguration config; private TbJsSwitchNodeConfiguration config;

7
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java

@ -18,16 +18,19 @@ package org.thingsboard.rule.engine.filter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.TbNodeUtils; import org.thingsboard.rule.engine.TbNodeUtils;
import org.thingsboard.rule.engine.api.*; import org.thingsboard.rule.engine.api.*;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
/** /**
* Created by ashvayka on 19.01.18. * Created by ashvayka on 19.01.18.
*/ */
@Slf4j @Slf4j
@FilterNode(name = "message type", @RuleNode(
type = ComponentType.FILTER,
name = "message type",
nodeDescription = "Filter incoming messages by Message Type", nodeDescription = "Filter incoming messages by Message Type",
nodeDetails = "Evaluate incoming Message with configured JS condition. " + nodeDetails = "Evaluate incoming Message with configured JS condition. " +
"If incoming MessageType is expected - send Message via 'Success' chain, otherwise 'Failure' chain is used.") "If incoming MessageType is expected - send Message via <b>Success</b> chain, otherwise <b>Failure</b> chain is used.")
public class TbMsgTypeFilterNode implements TbNode { public class TbMsgTypeFilterNode implements TbNode {
TbMsgTypeFilterNodeConfiguration config; TbMsgTypeFilterNodeConfiguration config;

12
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java

@ -24,6 +24,7 @@ import org.thingsboard.rule.engine.TbNodeUtils;
import org.thingsboard.rule.engine.api.*; import org.thingsboard.rule.engine.api.*;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import java.util.List; import java.util.List;
@ -35,11 +36,12 @@ import static org.thingsboard.server.common.data.DataConstants.*;
* Created by ashvayka on 19.01.18. * Created by ashvayka on 19.01.18.
*/ */
@Slf4j @Slf4j
@EnrichmentNode(name = "originator attributes", @RuleNode(type = ComponentType.ENRICHMENT,
nodeDescription = "Add Message Originator Attributes or Latest Telemetry into Message Metadata", name = "originator attributes",
nodeDetails = "If Attributes enrichment configured, CLIENT/SHARED/SERVER attributes are added into Message metadata " + nodeDescription = "Add Message Originator Attributes or Latest Telemetry into Message Metadata",
"with specific prefix: cs/shared/ss. To access those attributes in other nodes this template can be used " + nodeDetails = "If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata " +
"'meta.cs.temperature' or 'meta.shared.limit' " + "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> " +
"If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.") "If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.")
public class TbGetAttributesNode implements TbNode { public class TbGetAttributesNode implements TbNode {

9
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java

@ -16,17 +16,20 @@
package org.thingsboard.rule.engine.metadata; package org.thingsboard.rule.engine.metadata;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.rule.engine.api.EnrichmentNode; import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.plugin.ComponentType;
@EnrichmentNode(name="customer attributes", @RuleNode(
type = ComponentType.ENRICHMENT,
name="customer attributes",
nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message Metadata", nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message Metadata",
nodeDetails = "If Attributes enrichment configured, server scope attributes are added 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 " + "To access those attributes in other nodes this template can be used " +
"'meta.temperature'. If Latest Telemetry enrichment configured, latest telemetry added into metadata") "<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> { public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> {
@Override @Override

13
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java

@ -17,22 +17,21 @@ package org.thingsboard.rule.engine.metadata;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.rule.engine.TbNodeUtils; import org.thingsboard.rule.engine.TbNodeUtils;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.*;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.TbNodeState;
import org.thingsboard.rule.engine.api.EnrichmentNode;
import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.plugin.ComponentType;
@EnrichmentNode(name="related attributes", @RuleNode(
type = ComponentType.ENRICHMENT,
name="related attributes",
nodeDescription = "Add Originators Related Entity Attributes or Latest Telemetry into Message Metadata", nodeDescription = "Add Originators Related Entity Attributes or Latest Telemetry into Message Metadata",
nodeDetails = "Related Entity found using configured relation direction and Relation Type. " + nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
"If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. " + "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. " + "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
"To access those attributes in other nodes this template can be used " + "To access those attributes in other nodes this template can be used " +
"'meta.temperature'. If Latest Telemetry enrichment configured, latest telemetry added into metadata") "<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> { public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> {
private TbGetRelatedAttrNodeConfiguration config; private TbGetRelatedAttrNodeConfiguration config;

9
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java

@ -17,18 +17,21 @@ package org.thingsboard.rule.engine.metadata;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.EnrichmentNode; import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.plugin.ComponentType;
@Slf4j @Slf4j
@EnrichmentNode(name="tenant attributes", @RuleNode(
type = ComponentType.ENRICHMENT,
name="tenant attributes",
nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata", nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata",
nodeDetails = "If Attributes enrichment configured, server scope attributes are added 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 " + "To access those attributes in other nodes this template can be used " +
"'meta.temperature'. If Latest Telemetry enrichment configured, latest telemetry added into metadata") "<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> { public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> {
@Override @Override

5
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java

@ -27,12 +27,15 @@ import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader;
import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader;
import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader; import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import java.util.HashSet; import java.util.HashSet;
@Slf4j @Slf4j
@TransformationNode(name="change originator", @RuleNode(
type = ComponentType.TRANSFORMATION,
name="change originator",
nodeDescription = "Change Message Originator To Tenant/Customer/Related Entity", nodeDescription = "Change Message Originator To Tenant/Customer/Related Entity",
nodeDetails = "Related Entity found using configured relation direction and Relation Type. " + nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
"If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded. ") "If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded. ")

11
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java

@ -19,15 +19,18 @@ import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.rule.engine.TbNodeUtils; import org.thingsboard.rule.engine.TbNodeUtils;
import org.thingsboard.rule.engine.api.*; import org.thingsboard.rule.engine.api.*;
import org.thingsboard.rule.engine.js.NashornJsEngine; import org.thingsboard.rule.engine.js.NashornJsEngine;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import javax.script.Bindings; import javax.script.Bindings;
@TransformationNode(name = "script", @RuleNode(
type = ComponentType.TRANSFORMATION,
name = "script",
nodeDescription = "Change Message payload and Metadata using JavaScript", nodeDescription = "Change Message payload and Metadata using JavaScript",
nodeDetails = "JavaScript function recieve 2 input parameters that can be changed inside. " + nodeDetails = "JavaScript function recieve 2 input parameters that can be changed inside.<br/> " +
"'meta' - is a Message metadata. " + "<code>meta</code> - is a Message metadata.<br/>" +
"'msg' - is a Message payload. Any properties can be changed/removed/added in those objects.") "<code>msg</code> - is a Message payload.<br/>Any properties can be changed/removed/added in those objects.")
public class TbTransformMsgNode extends TbAbstractTransformNode { public class TbTransformMsgNode extends TbAbstractTransformNode {
private TbTransformMsgNodeConfiguration config; private TbTransformMsgNodeConfiguration config;

15
ui/src/app/common/types.constant.js

@ -460,8 +460,19 @@ export default angular.module('thingsboard.types', [])
ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION"], ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION"],
ruleChainNodeComponent: { ruleChainNodeComponent: {
type: 'RULE_CHAIN', type: 'RULE_CHAIN',
name: 'Rule chain', name: 'rule chain',
clazz: 'tb.internal.RuleChain' clazz: 'tb.internal.RuleChain',
configurationDescriptor: {
nodeDefinition: {
description: "Forwards incoming messages to specified Rule Chain",
details: "Forwards incoming messages to specified Rule Chain",
inEnabled: true,
outEnabled: false,
relationTypes: [],
customRelations: false,
defaultConfiguration: {}
}
}
}, },
inputNodeComponent: { inputNodeComponent: {
type: 'INPUT', type: 'INPUT',

3
ui/src/app/rulechain/rulechain.controller.js

@ -183,7 +183,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
'<div class="tb-rule-node-tooltip">' + '<div class="tb-rule-node-tooltip">' +
'<div id="tooltip-content" layout="column">' + '<div id="tooltip-content" layout="column">' +
'<div class="tb-node-title">' + node.component.name + '</div>' + '<div class="tb-node-title">' + node.component.name + '</div>' +
'<div class="tb-node-description">' + 'Some description of node' + '</div>' + '<div class="tb-node-description">' + node.component.configurationDescriptor.nodeDefinition.description + '</div>' +
'<div class="tb-node-details">' + node.component.configurationDescriptor.nodeDefinition.details + '</div>' +
'</div>' + '</div>' +
'</div>' '</div>'
); );

29
ui/src/app/rulechain/rulechain.scss

@ -260,3 +260,32 @@
stroke-dashoffset: 500; stroke-dashoffset: 500;
} }
} }
.tb-rule-node-tooltip {
font-size: 14px;
width: 300px;
color: #333;
#tooltip-content {
.tb-node-title {
font-weight: 600;
}
.tb-node-description {
font-style: italic;
color: #555;
}
.tb-node-details {
padding-top: 10px;
padding-bottom: 10px;
}
code {
padding: 0px 3px 2px 3px;
margin: 1px;
color: #AD1625;
white-space: nowrap;
background-color: #f7f7f9;
border: 1px solid #e1e1e8;
border-radius: 2px;
font-size: 12px;
}
}
}
Loading…
Cancel
Save