Commit 8f3fbc945dd7c33c1eb1305e499e07725d2c0570

Authored by Igor Kulikov
1 parent a8f5b0d1

RuleNode annotation refactoring

Showing 18 changed files with 173 additions and 197 deletions
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 package org.thingsboard.server.service.component; 16 package org.thingsboard.server.service.component;
17 17
18 import com.fasterxml.jackson.databind.ObjectMapper; 18 import com.fasterxml.jackson.databind.ObjectMapper;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
  20 +import com.fasterxml.jackson.databind.JsonNode;
19 import com.google.common.base.Charsets; 21 import com.google.common.base.Charsets;
20 import com.google.common.io.Resources; 22 import com.google.common.io.Resources;
21 import lombok.extern.slf4j.Slf4j; 23 import lombok.extern.slf4j.Slf4j;
@@ -26,16 +28,14 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen @@ -26,16 +28,14 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen
26 import org.springframework.core.env.Environment; 28 import org.springframework.core.env.Environment;
27 import org.springframework.core.type.filter.AnnotationTypeFilter; 29 import org.springframework.core.type.filter.AnnotationTypeFilter;
28 import org.springframework.stereotype.Service; 30 import org.springframework.stereotype.Service;
29 -import org.thingsboard.rule.engine.api.ActionNode;  
30 -import org.thingsboard.rule.engine.api.EnrichmentNode;  
31 -import org.thingsboard.rule.engine.api.FilterNode;  
32 -import org.thingsboard.rule.engine.api.TransformationNode; 31 +import org.thingsboard.rule.engine.api.*;
33 import org.thingsboard.server.common.data.plugin.ComponentDescriptor; 32 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
34 import org.thingsboard.server.common.data.plugin.ComponentType; 33 import org.thingsboard.server.common.data.plugin.ComponentType;
35 import org.thingsboard.server.dao.component.ComponentDescriptorService; 34 import org.thingsboard.server.dao.component.ComponentDescriptorService;
36 import org.thingsboard.server.extensions.api.component.*; 35 import org.thingsboard.server.extensions.api.component.*;
37 36
38 import javax.annotation.PostConstruct; 37 import javax.annotation.PostConstruct;
  38 +import java.io.IOException;
39 import java.lang.annotation.Annotation; 39 import java.lang.annotation.Annotation;
40 import java.util.*; 40 import java.util.*;
41 import java.util.stream.Collectors; 41 import java.util.stream.Collectors;
@@ -70,6 +70,24 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -70,6 +70,24 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
70 } 70 }
71 } 71 }
72 72
  73 + private void registerRuleNodeComponents() {
  74 + Set<BeanDefinition> ruleNodeBeanDefinitions = getBeanDefinitions(RuleNode.class);
  75 + for (BeanDefinition def : ruleNodeBeanDefinitions) {
  76 + try {
  77 + String clazzName = def.getBeanClassName();
  78 + Class<?> clazz = Class.forName(clazzName);
  79 + RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class);
  80 + ComponentType type = ruleNodeAnnotation.type();
  81 + ComponentDescriptor component = scanAndPersistComponent(def, type);
  82 + components.put(component.getClazz(), component);
  83 + componentsMap.computeIfAbsent(type, k -> new ArrayList<>()).add(component);
  84 + } catch (Exception e) {
  85 + log.error("Can't initialize component {}, due to {}", def.getBeanClassName(), e.getMessage(), e);
  86 + throw new RuntimeException(e);
  87 + }
  88 + }
  89 + }
  90 +
73 private void registerComponents(ComponentType type, Class<? extends Annotation> annotation) { 91 private void registerComponents(ComponentType type, Class<? extends Annotation> annotation) {
74 List<ComponentDescriptor> components = persist(getBeanDefinitions(annotation), type); 92 List<ComponentDescriptor> components = persist(getBeanDefinitions(annotation), type);
75 componentsMap.put(type, components); 93 componentsMap.put(type, components);
@@ -97,34 +115,25 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -97,34 +115,25 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
97 String descriptorResourceName; 115 String descriptorResourceName;
98 switch (type) { 116 switch (type) {
99 case ENRICHMENT: 117 case ENRICHMENT:
100 - EnrichmentNode enrichmentAnnotation = clazz.getAnnotation(EnrichmentNode.class);  
101 - scannedComponent.setName(enrichmentAnnotation.name());  
102 - scannedComponent.setScope(enrichmentAnnotation.scope());  
103 - descriptorResourceName = enrichmentAnnotation.descriptor();  
104 - break;  
105 case FILTER: 118 case FILTER:
106 - FilterNode filterAnnotation = clazz.getAnnotation(FilterNode.class);  
107 - scannedComponent.setName(filterAnnotation.name());  
108 - scannedComponent.setScope(filterAnnotation.scope());  
109 - descriptorResourceName = filterAnnotation.descriptor();  
110 - break;  
111 case TRANSFORMATION: 119 case TRANSFORMATION:
112 - TransformationNode trAnnotation = clazz.getAnnotation(TransformationNode.class);  
113 - scannedComponent.setName(trAnnotation.name());  
114 - scannedComponent.setScope(trAnnotation.scope());  
115 - descriptorResourceName = trAnnotation.descriptor();  
116 - break;  
117 case ACTION: 120 case ACTION:
118 - ActionNode actionAnnotation = clazz.getAnnotation(ActionNode.class);  
119 - scannedComponent.setName(actionAnnotation.name());  
120 - scannedComponent.setScope(actionAnnotation.scope());  
121 - descriptorResourceName = actionAnnotation.descriptor(); 121 + RuleNode ruleNodeAnnotation = clazz.getAnnotation(RuleNode.class);
  122 + scannedComponent.setName(ruleNodeAnnotation.name());
  123 + scannedComponent.setScope(ruleNodeAnnotation.scope());
  124 + NodeDefinition nodeDefinition = prepareNodeDefinition(ruleNodeAnnotation);
  125 + ObjectNode configurationDescriptor = mapper.createObjectNode();
  126 + JsonNode node = mapper.valueToTree(nodeDefinition);
  127 + configurationDescriptor.set("nodeDefinition", node);
  128 + scannedComponent.setConfigurationDescriptor(configurationDescriptor);
122 break; 129 break;
123 case OLD_ACTION: 130 case OLD_ACTION:
124 Action oldActionAnnotation = clazz.getAnnotation(Action.class); 131 Action oldActionAnnotation = clazz.getAnnotation(Action.class);
125 scannedComponent.setName(oldActionAnnotation.name()); 132 scannedComponent.setName(oldActionAnnotation.name());
126 scannedComponent.setScope(oldActionAnnotation.scope()); 133 scannedComponent.setScope(oldActionAnnotation.scope());
127 descriptorResourceName = oldActionAnnotation.descriptor(); 134 descriptorResourceName = oldActionAnnotation.descriptor();
  135 + scannedComponent.setConfigurationDescriptor(mapper.readTree(
  136 + Resources.toString(Resources.getResource(descriptorResourceName), Charsets.UTF_8)));
128 break; 137 break;
129 case PLUGIN: 138 case PLUGIN:
130 Plugin pluginAnnotation = clazz.getAnnotation(Plugin.class); 139 Plugin pluginAnnotation = clazz.getAnnotation(Plugin.class);
@@ -143,12 +152,12 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -143,12 +152,12 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
143 } 152 }
144 } 153 }
145 scannedComponent.setActions(Arrays.stream(pluginAnnotation.actions()).map(Class::getName).collect(Collectors.joining(","))); 154 scannedComponent.setActions(Arrays.stream(pluginAnnotation.actions()).map(Class::getName).collect(Collectors.joining(",")));
  155 + scannedComponent.setConfigurationDescriptor(mapper.readTree(
  156 + Resources.toString(Resources.getResource(descriptorResourceName), Charsets.UTF_8)));
146 break; 157 break;
147 default: 158 default:
148 throw new RuntimeException(type + " is not supported yet!"); 159 throw new RuntimeException(type + " is not supported yet!");
149 } 160 }
150 - scannedComponent.setConfigurationDescriptor(mapper.readTree(  
151 - Resources.toString(Resources.getResource(descriptorResourceName), Charsets.UTF_8)));  
152 scannedComponent.setClazz(clazzName); 161 scannedComponent.setClazz(clazzName);
153 log.info("Processing scanned component: {}", scannedComponent); 162 log.info("Processing scanned component: {}", scannedComponent);
154 } catch (Exception e) { 163 } catch (Exception e) {
@@ -171,6 +180,20 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -171,6 +180,20 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
171 return scannedComponent; 180 return scannedComponent;
172 } 181 }
173 182
  183 + private NodeDefinition prepareNodeDefinition(RuleNode nodeAnnotation) throws IOException {
  184 + NodeDefinition nodeDefinition = new NodeDefinition();
  185 + nodeDefinition.setDetails(nodeAnnotation.nodeDetails());
  186 + nodeDefinition.setDescription(nodeAnnotation.nodeDescription());
  187 + nodeDefinition.setInEnabled(nodeAnnotation.inEnabled());
  188 + nodeDefinition.setOutEnabled(nodeAnnotation.outEnabled());
  189 + nodeDefinition.setRelationTypes(nodeAnnotation.relationTypes());
  190 + nodeDefinition.setCustomRelations(nodeAnnotation.customRelations());
  191 + String defaultConfigResourceName = nodeAnnotation.defaultConfigResource();
  192 + nodeDefinition.setDefaultConfiguration(mapper.readTree(
  193 + Resources.toString(Resources.getResource(defaultConfigResourceName), Charsets.UTF_8)));
  194 + return nodeDefinition;
  195 + }
  196 +
174 private Set<BeanDefinition> getBeanDefinitions(Class<? extends Annotation> componentType) { 197 private Set<BeanDefinition> getBeanDefinitions(Class<? extends Annotation> componentType) {
175 ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); 198 ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
176 scanner.addIncludeFilter(new AnnotationTypeFilter(componentType)); 199 scanner.addIncludeFilter(new AnnotationTypeFilter(componentType));
@@ -183,13 +206,8 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -183,13 +206,8 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
183 206
184 @Override 207 @Override
185 public void discoverComponents() { 208 public void discoverComponents() {
186 - registerComponents(ComponentType.ENRICHMENT, EnrichmentNode.class);  
187 -  
188 - registerComponents(ComponentType.FILTER, FilterNode.class);  
189 -  
190 - registerComponents(ComponentType.TRANSFORMATION, TransformationNode.class);  
191 209
192 - registerComponents(ComponentType.ACTION, ActionNode.class); 210 + registerRuleNodeComponents();
193 211
194 registerComponents(ComponentType.OLD_ACTION, Action.class); 212 registerComponents(ComponentType.OLD_ACTION, Action.class);
195 213
@@ -200,15 +218,19 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -200,15 +218,19 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
200 218
201 @Override 219 @Override
202 public List<ComponentDescriptor> getComponents(ComponentType type) { 220 public List<ComponentDescriptor> getComponents(ComponentType type) {
203 - return Collections.unmodifiableList(componentsMap.get(type)); 221 + if (componentsMap.containsKey(type)) {
  222 + return Collections.unmodifiableList(componentsMap.get(type));
  223 + } else {
  224 + return Collections.emptyList();
  225 + }
204 } 226 }
205 227
206 @Override 228 @Override
207 public List<ComponentDescriptor> getComponents(Set<ComponentType> types) { 229 public List<ComponentDescriptor> getComponents(Set<ComponentType> types) {
208 List<ComponentDescriptor> result = new ArrayList<>(); 230 List<ComponentDescriptor> result = new ArrayList<>();
209 - for (ComponentType type : types) { 231 + types.stream().filter(type -> componentsMap.containsKey(type)).forEach(type -> {
210 result.addAll(componentsMap.get(type)); 232 result.addAll(componentsMap.get(type));
211 - } 233 + });
212 return Collections.unmodifiableList(result); 234 return Collections.unmodifiableList(result);
213 } 235 }
214 236
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.rule.engine.api;  
17 -  
18 -import org.thingsboard.server.common.data.plugin.ComponentScope;  
19 -  
20 -import java.lang.annotation.ElementType;  
21 -import java.lang.annotation.Retention;  
22 -import java.lang.annotation.RetentionPolicy;  
23 -import java.lang.annotation.Target;  
24 -  
25 -/**  
26 - * @author Andrew Shvayka  
27 - */  
28 -@Retention(RetentionPolicy.RUNTIME)  
29 -@Target(ElementType.TYPE)  
30 -public @interface FilterNode {  
31 -  
32 - String name();  
33 -  
34 - String nodeDescription();  
35 -  
36 - String nodeDetails();  
37 -  
38 - boolean inEnabled() default true;  
39 -  
40 - boolean outEnabled() default true;  
41 -  
42 - ComponentScope scope() default ComponentScope.TENANT;  
43 -  
44 - String descriptor() default "EmptyNodeDescriptor.json";  
45 -  
46 - String[] relationTypes() default {"Success", "Failure"};  
47 -  
48 - boolean customRelations() default false;  
49 -  
50 -}  
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/NodeDefinition.java renamed from rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ActionNode.java
@@ -13,31 +13,21 @@ @@ -13,31 +13,21 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.rule.engine.api;  
17 -  
18 -import org.thingsboard.server.common.data.plugin.ComponentScope;  
19 -import org.thingsboard.server.extensions.api.component.EmptyComponentConfiguration;  
20 -  
21 -import java.lang.annotation.ElementType;  
22 -import java.lang.annotation.Retention;  
23 -import java.lang.annotation.RetentionPolicy;  
24 -import java.lang.annotation.Target;  
25 -  
26 -/**  
27 - * @author Andrew Shvayka  
28 - */  
29 -@Retention(RetentionPolicy.RUNTIME)  
30 -@Target(ElementType.TYPE)  
31 -public @interface ActionNode {  
32 16
33 - String name();  
34 -  
35 - ComponentScope scope() default ComponentScope.TENANT; 17 +package org.thingsboard.rule.engine.api;
36 18
37 - String descriptor() default "EmptyNodeDescriptor.json"; 19 +import com.fasterxml.jackson.databind.JsonNode;
  20 +import lombok.Data;
38 21
39 - String[] relationTypes() default {"Success","Failure"}; 22 +@Data
  23 +public class NodeDefinition {
40 24
41 - boolean customRelations() default false; 25 + private String details;
  26 + private String description;
  27 + private boolean inEnabled;
  28 + private boolean outEnabled;
  29 + String[] relationTypes;
  30 + boolean customRelations;
  31 + JsonNode defaultConfiguration;
42 32
43 } 33 }
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleNode.java renamed from rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/EnrichmentNode.java
@@ -16,18 +16,18 @@ @@ -16,18 +16,18 @@
16 package org.thingsboard.rule.engine.api; 16 package org.thingsboard.rule.engine.api;
17 17
18 import org.thingsboard.server.common.data.plugin.ComponentScope; 18 import org.thingsboard.server.common.data.plugin.ComponentScope;
  19 +import org.thingsboard.server.common.data.plugin.ComponentType;
19 20
20 import java.lang.annotation.ElementType; 21 import java.lang.annotation.ElementType;
21 import java.lang.annotation.Retention; 22 import java.lang.annotation.Retention;
22 import java.lang.annotation.RetentionPolicy; 23 import java.lang.annotation.RetentionPolicy;
23 import java.lang.annotation.Target; 24 import java.lang.annotation.Target;
24 25
25 -/**  
26 - * @author Andrew Shvayka  
27 - */  
28 @Retention(RetentionPolicy.RUNTIME) 26 @Retention(RetentionPolicy.RUNTIME)
29 @Target(ElementType.TYPE) 27 @Target(ElementType.TYPE)
30 -public @interface EnrichmentNode { 28 +public @interface RuleNode {
  29 +
  30 + ComponentType type();
31 31
32 String name(); 32 String name();
33 33
@@ -41,7 +41,7 @@ public @interface EnrichmentNode { @@ -41,7 +41,7 @@ public @interface EnrichmentNode {
41 41
42 ComponentScope scope() default ComponentScope.TENANT; 42 ComponentScope scope() default ComponentScope.TENANT;
43 43
44 - String descriptor() default "EmptyNodeDescriptor.json"; 44 + String defaultConfigResource() default "EmptyNodeConfig.json";
45 45
46 String[] relationTypes() default {"Success", "Failure"}; 46 String[] relationTypes() default {"Success", "Failure"};
47 47
1 -/**  
2 - * Copyright © 2016-2018 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.rule.engine.api;  
17 -  
18 -import org.thingsboard.server.common.data.plugin.ComponentScope;  
19 -import org.thingsboard.server.extensions.api.component.EmptyComponentConfiguration;  
20 -  
21 -import java.lang.annotation.ElementType;  
22 -import java.lang.annotation.Retention;  
23 -import java.lang.annotation.RetentionPolicy;  
24 -import java.lang.annotation.Target;  
25 -  
26 -/**  
27 - * @author Andrew Shvayka  
28 - */  
29 -@Retention(RetentionPolicy.RUNTIME)  
30 -@Target(ElementType.TYPE)  
31 -public @interface TransformationNode {  
32 -  
33 - String name();  
34 -  
35 - String nodeDescription();  
36 -  
37 - String nodeDetails();  
38 -  
39 - boolean inEnabled() default true;  
40 -  
41 - boolean outEnabled() default true;  
42 -  
43 - ComponentScope scope() default ComponentScope.TENANT;  
44 -  
45 - String descriptor() default "EmptyNodeDescriptor.json";  
46 -  
47 - String[] relationTypes() default {"Success","Failure"};  
48 -  
49 - boolean customRelations() default false;  
50 -  
51 -}  
@@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
19 import org.thingsboard.rule.engine.TbNodeUtils; 19 import org.thingsboard.rule.engine.TbNodeUtils;
20 import org.thingsboard.rule.engine.api.*; 20 import org.thingsboard.rule.engine.api.*;
21 import org.thingsboard.rule.engine.js.NashornJsEngine; 21 import org.thingsboard.rule.engine.js.NashornJsEngine;
  22 +import org.thingsboard.server.common.data.plugin.ComponentType;
22 import org.thingsboard.server.common.msg.TbMsg; 23 import org.thingsboard.server.common.msg.TbMsg;
23 24
24 import javax.script.Bindings; 25 import javax.script.Bindings;
@@ -26,12 +27,14 @@ import javax.script.Bindings; @@ -26,12 +27,14 @@ import javax.script.Bindings;
26 import static org.thingsboard.rule.engine.DonAsynchron.withCallback; 27 import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
27 28
28 @Slf4j 29 @Slf4j
29 -@FilterNode(name = "script", relationTypes = {"True", "False", "Failure"}, 30 +@RuleNode(
  31 + type = ComponentType.FILTER,
  32 + name = "script", relationTypes = {"True", "False", "Failure"},
30 nodeDescription = "Filter incoming messages using JS script", 33 nodeDescription = "Filter incoming messages using JS script",
31 nodeDetails = "Evaluate incoming Message with configured JS condition. " + 34 nodeDetails = "Evaluate incoming Message with configured JS condition. " +
32 - "If 'True' - send Message via 'True' chain, otherwise 'False' chain is used." +  
33 - "Message payload can be accessed via 'msg' property. For example 'msg.temperature < 10;'" +  
34 - "Message metadata can be accessed via 'meta' property. For example 'meta.customerName === 'John';'") 35 + "If <b>True</b> - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used." +
  36 + "Message payload can be accessed via <code>msg</code> property. For example <code>msg.temperature < 10;</code>" +
  37 + "Message metadata can be accessed via <code>meta</code> property. For example <code>meta.customerName === 'John';</code>")
35 public class TbJsFilterNode implements TbNode { 38 public class TbJsFilterNode implements TbNode {
36 39
37 private TbJsFilterNodeConfiguration config; 40 private TbJsFilterNodeConfiguration config;
@@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
19 import org.thingsboard.rule.engine.TbNodeUtils; 19 import org.thingsboard.rule.engine.TbNodeUtils;
20 import org.thingsboard.rule.engine.api.*; 20 import org.thingsboard.rule.engine.api.*;
21 import org.thingsboard.rule.engine.js.NashornJsEngine; 21 import org.thingsboard.rule.engine.js.NashornJsEngine;
  22 +import org.thingsboard.server.common.data.plugin.ComponentType;
22 import org.thingsboard.server.common.msg.TbMsg; 23 import org.thingsboard.server.common.msg.TbMsg;
23 24
24 import javax.script.Bindings; 25 import javax.script.Bindings;
@@ -27,12 +28,14 @@ import java.util.Set; @@ -27,12 +28,14 @@ import java.util.Set;
27 import static org.thingsboard.rule.engine.DonAsynchron.withCallback; 28 import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
28 29
29 @Slf4j 30 @Slf4j
30 -@FilterNode(name = "switch", customRelations = true, 31 +@RuleNode(
  32 + type = ComponentType.FILTER,
  33 + name = "switch", customRelations = true,
31 nodeDescription = "Route incoming Message to one or multiple output chains", 34 nodeDescription = "Route incoming Message to one or multiple output chains",
32 nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " + 35 nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " +
33 "If Array is empty - message not routed to next Node. " + 36 "If Array is empty - message not routed to next Node. " +
34 - "Message payload can be accessed via 'msg' property. For example 'msg.temperature < 10;' " +  
35 - "Message metadata can be accessed via 'meta' property. For example 'meta.customerName === 'John';' ") 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>")
36 public class TbJsSwitchNode implements TbNode { 39 public class TbJsSwitchNode implements TbNode {
37 40
38 private TbJsSwitchNodeConfiguration config; 41 private TbJsSwitchNodeConfiguration config;
@@ -18,16 +18,19 @@ package org.thingsboard.rule.engine.filter; @@ -18,16 +18,19 @@ package org.thingsboard.rule.engine.filter;
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.thingsboard.rule.engine.TbNodeUtils; 19 import org.thingsboard.rule.engine.TbNodeUtils;
20 import org.thingsboard.rule.engine.api.*; 20 import org.thingsboard.rule.engine.api.*;
  21 +import org.thingsboard.server.common.data.plugin.ComponentType;
21 import org.thingsboard.server.common.msg.TbMsg; 22 import org.thingsboard.server.common.msg.TbMsg;
22 23
23 /** 24 /**
24 * Created by ashvayka on 19.01.18. 25 * Created by ashvayka on 19.01.18.
25 */ 26 */
26 @Slf4j 27 @Slf4j
27 -@FilterNode(name = "message type", 28 +@RuleNode(
  29 + type = ComponentType.FILTER,
  30 + name = "message type",
28 nodeDescription = "Filter incoming messages by Message Type", 31 nodeDescription = "Filter incoming messages by Message Type",
29 nodeDetails = "Evaluate incoming Message with configured JS condition. " + 32 nodeDetails = "Evaluate incoming Message with configured JS condition. " +
30 - "If incoming MessageType is expected - send Message via 'Success' chain, otherwise 'Failure' chain is used.") 33 + "If incoming MessageType is expected - send Message via <b>Success</b> chain, otherwise <b>Failure</b> chain is used.")
31 public class TbMsgTypeFilterNode implements TbNode { 34 public class TbMsgTypeFilterNode implements TbNode {
32 35
33 TbMsgTypeFilterNodeConfiguration config; 36 TbMsgTypeFilterNodeConfiguration config;
@@ -24,6 +24,7 @@ import org.thingsboard.rule.engine.TbNodeUtils; @@ -24,6 +24,7 @@ import org.thingsboard.rule.engine.TbNodeUtils;
24 import org.thingsboard.rule.engine.api.*; 24 import org.thingsboard.rule.engine.api.*;
25 import org.thingsboard.server.common.data.kv.AttributeKvEntry; 25 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
26 import org.thingsboard.server.common.data.kv.TsKvEntry; 26 import org.thingsboard.server.common.data.kv.TsKvEntry;
  27 +import org.thingsboard.server.common.data.plugin.ComponentType;
27 import org.thingsboard.server.common.msg.TbMsg; 28 import org.thingsboard.server.common.msg.TbMsg;
28 29
29 import java.util.List; 30 import java.util.List;
@@ -35,11 +36,12 @@ import static org.thingsboard.server.common.data.DataConstants.*; @@ -35,11 +36,12 @@ import static org.thingsboard.server.common.data.DataConstants.*;
35 * Created by ashvayka on 19.01.18. 36 * Created by ashvayka on 19.01.18.
36 */ 37 */
37 @Slf4j 38 @Slf4j
38 -@EnrichmentNode(name = "originator attributes",  
39 - nodeDescription = "Add Message Originator Attributes or Latest Telemetry into Message Metadata",  
40 - nodeDetails = "If Attributes enrichment configured, CLIENT/SHARED/SERVER attributes are added into Message metadata " +  
41 - "with specific prefix: cs/shared/ss. To access those attributes in other nodes this template can be used " +  
42 - "'meta.cs.temperature' or 'meta.shared.limit' " + 39 +@RuleNode(type = ComponentType.ENRICHMENT,
  40 + name = "originator attributes",
  41 + nodeDescription = "Add Message Originator Attributes or Latest Telemetry into Message Metadata",
  42 + nodeDetails = "If Attributes enrichment configured, <b>CLIENT/SHARED/SERVER</b> attributes are added into Message metadata " +
  43 + "with specific prefix: <i>cs/shared/ss</i>. To access those attributes in other nodes this template can be used " +
  44 + "<code>meta.cs.temperature</code> or <code>meta.shared.limit</code> " +
43 "If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.") 45 "If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.")
44 public class TbGetAttributesNode implements TbNode { 46 public class TbGetAttributesNode implements TbNode {
45 47
@@ -16,17 +16,20 @@ @@ -16,17 +16,20 @@
16 package org.thingsboard.rule.engine.metadata; 16 package org.thingsboard.rule.engine.metadata;
17 17
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 -import org.thingsboard.rule.engine.api.EnrichmentNode; 19 +import org.thingsboard.rule.engine.api.RuleNode;
20 import org.thingsboard.rule.engine.api.TbContext; 20 import org.thingsboard.rule.engine.api.TbContext;
21 import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader; 21 import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader;
22 import org.thingsboard.server.common.data.id.CustomerId; 22 import org.thingsboard.server.common.data.id.CustomerId;
23 import org.thingsboard.server.common.data.id.EntityId; 23 import org.thingsboard.server.common.data.id.EntityId;
  24 +import org.thingsboard.server.common.data.plugin.ComponentType;
24 25
25 -@EnrichmentNode(name="customer attributes", 26 +@RuleNode(
  27 + type = ComponentType.ENRICHMENT,
  28 + name="customer attributes",
26 nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message Metadata", 29 nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message Metadata",
27 nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + 30 nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
28 "To access those attributes in other nodes this template can be used " + 31 "To access those attributes in other nodes this template can be used " +
29 - "'meta.temperature'. If Latest Telemetry enrichment configured, latest telemetry added into metadata") 32 + "<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
30 public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> { 33 public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> {
31 34
32 @Override 35 @Override
@@ -17,22 +17,21 @@ package org.thingsboard.rule.engine.metadata; @@ -17,22 +17,21 @@ package org.thingsboard.rule.engine.metadata;
17 17
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.rule.engine.TbNodeUtils; 19 import org.thingsboard.rule.engine.TbNodeUtils;
20 -import org.thingsboard.rule.engine.api.TbContext;  
21 -import org.thingsboard.rule.engine.api.TbNodeConfiguration;  
22 -import org.thingsboard.rule.engine.api.TbNodeException;  
23 -import org.thingsboard.rule.engine.api.TbNodeState;  
24 -import org.thingsboard.rule.engine.api.EnrichmentNode; 20 +import org.thingsboard.rule.engine.api.*;
25 import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader; 21 import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader;
26 22
27 import org.thingsboard.server.common.data.id.EntityId; 23 import org.thingsboard.server.common.data.id.EntityId;
  24 +import org.thingsboard.server.common.data.plugin.ComponentType;
28 25
29 -@EnrichmentNode(name="related attributes", 26 +@RuleNode(
  27 + type = ComponentType.ENRICHMENT,
  28 + name="related attributes",
30 nodeDescription = "Add Originators Related Entity Attributes or Latest Telemetry into Message Metadata", 29 nodeDescription = "Add Originators Related Entity Attributes or Latest Telemetry into Message Metadata",
31 nodeDetails = "Related Entity found using configured relation direction and Relation Type. " + 30 nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
32 "If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. " + 31 "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. " + 32 "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 " + 33 "To access those attributes in other nodes this template can be used " +
35 - "'meta.temperature'. If Latest Telemetry enrichment configured, latest telemetry added into metadata") 34 + "<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
36 public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> { 35 public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> {
37 36
38 private TbGetRelatedAttrNodeConfiguration config; 37 private TbGetRelatedAttrNodeConfiguration config;
@@ -17,18 +17,21 @@ package org.thingsboard.rule.engine.metadata; @@ -17,18 +17,21 @@ package org.thingsboard.rule.engine.metadata;
17 17
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
20 -import org.thingsboard.rule.engine.api.EnrichmentNode; 20 +import org.thingsboard.rule.engine.api.RuleNode;
21 import org.thingsboard.rule.engine.api.TbContext; 21 import org.thingsboard.rule.engine.api.TbContext;
22 import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader; 22 import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader;
23 import org.thingsboard.server.common.data.id.EntityId; 23 import org.thingsboard.server.common.data.id.EntityId;
24 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.common.data.plugin.ComponentType;
25 26
26 @Slf4j 27 @Slf4j
27 -@EnrichmentNode(name="tenant attributes", 28 +@RuleNode(
  29 + type = ComponentType.ENRICHMENT,
  30 + name="tenant attributes",
28 nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata", 31 nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata",
29 nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " + 32 nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
30 "To access those attributes in other nodes this template can be used " + 33 "To access those attributes in other nodes this template can be used " +
31 - "'meta.temperature'. If Latest Telemetry enrichment configured, latest telemetry added into metadata") 34 + "<code>meta.temperature</code>. If Latest Telemetry enrichment configured, latest telemetry added into metadata")
32 public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> { 35 public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> {
33 36
34 @Override 37 @Override
@@ -27,12 +27,15 @@ import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader; @@ -27,12 +27,15 @@ import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader;
27 import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader; 27 import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader;
28 import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader; 28 import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader;
29 import org.thingsboard.server.common.data.id.EntityId; 29 import org.thingsboard.server.common.data.id.EntityId;
  30 +import org.thingsboard.server.common.data.plugin.ComponentType;
30 import org.thingsboard.server.common.msg.TbMsg; 31 import org.thingsboard.server.common.msg.TbMsg;
31 32
32 import java.util.HashSet; 33 import java.util.HashSet;
33 34
34 @Slf4j 35 @Slf4j
35 -@TransformationNode(name="change originator", 36 +@RuleNode(
  37 + type = ComponentType.TRANSFORMATION,
  38 + name="change originator",
36 nodeDescription = "Change Message Originator To Tenant/Customer/Related Entity", 39 nodeDescription = "Change Message Originator To Tenant/Customer/Related Entity",
37 nodeDetails = "Related Entity found using configured relation direction and Relation Type. " + 40 nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
38 "If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded. ") 41 "If multiple Related Entities are found, only first Entity is used as new Originator, other entities are discarded. ")
@@ -19,15 +19,18 @@ import com.google.common.util.concurrent.ListenableFuture; @@ -19,15 +19,18 @@ import com.google.common.util.concurrent.ListenableFuture;
19 import org.thingsboard.rule.engine.TbNodeUtils; 19 import org.thingsboard.rule.engine.TbNodeUtils;
20 import org.thingsboard.rule.engine.api.*; 20 import org.thingsboard.rule.engine.api.*;
21 import org.thingsboard.rule.engine.js.NashornJsEngine; 21 import org.thingsboard.rule.engine.js.NashornJsEngine;
  22 +import org.thingsboard.server.common.data.plugin.ComponentType;
22 import org.thingsboard.server.common.msg.TbMsg; 23 import org.thingsboard.server.common.msg.TbMsg;
23 24
24 import javax.script.Bindings; 25 import javax.script.Bindings;
25 26
26 -@TransformationNode(name = "script", 27 +@RuleNode(
  28 + type = ComponentType.TRANSFORMATION,
  29 + name = "script",
27 nodeDescription = "Change Message payload and Metadata using JavaScript", 30 nodeDescription = "Change Message payload and Metadata using JavaScript",
28 - nodeDetails = "JavaScript function recieve 2 input parameters that can be changed inside. " +  
29 - "'meta' - is a Message metadata. " +  
30 - "'msg' - is a Message payload. Any properties can be changed/removed/added in those objects.") 31 + nodeDetails = "JavaScript function recieve 2 input parameters that can be changed inside.<br/> " +
  32 + "<code>meta</code> - is a Message metadata.<br/>" +
  33 + "<code>msg</code> - is a Message payload.<br/>Any properties can be changed/removed/added in those objects.")
31 public class TbTransformMsgNode extends TbAbstractTransformNode { 34 public class TbTransformMsgNode extends TbAbstractTransformNode {
32 35
33 private TbTransformMsgNodeConfiguration config; 36 private TbTransformMsgNodeConfiguration config;
@@ -460,8 +460,19 @@ export default angular.module('thingsboard.types', []) @@ -460,8 +460,19 @@ export default angular.module('thingsboard.types', [])
460 ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION"], 460 ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION"],
461 ruleChainNodeComponent: { 461 ruleChainNodeComponent: {
462 type: 'RULE_CHAIN', 462 type: 'RULE_CHAIN',
463 - name: 'Rule chain',  
464 - clazz: 'tb.internal.RuleChain' 463 + name: 'rule chain',
  464 + clazz: 'tb.internal.RuleChain',
  465 + configurationDescriptor: {
  466 + nodeDefinition: {
  467 + description: "Forwards incoming messages to specified Rule Chain",
  468 + details: "Forwards incoming messages to specified Rule Chain",
  469 + inEnabled: true,
  470 + outEnabled: false,
  471 + relationTypes: [],
  472 + customRelations: false,
  473 + defaultConfiguration: {}
  474 + }
  475 + }
465 }, 476 },
466 inputNodeComponent: { 477 inputNodeComponent: {
467 type: 'INPUT', 478 type: 'INPUT',
@@ -183,7 +183,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil, @@ -183,7 +183,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
183 '<div class="tb-rule-node-tooltip">' + 183 '<div class="tb-rule-node-tooltip">' +
184 '<div id="tooltip-content" layout="column">' + 184 '<div id="tooltip-content" layout="column">' +
185 '<div class="tb-node-title">' + node.component.name + '</div>' + 185 '<div class="tb-node-title">' + node.component.name + '</div>' +
186 - '<div class="tb-node-description">' + 'Some description of node' + '</div>' + 186 + '<div class="tb-node-description">' + node.component.configurationDescriptor.nodeDefinition.description + '</div>' +
  187 + '<div class="tb-node-details">' + node.component.configurationDescriptor.nodeDefinition.details + '</div>' +
187 '</div>' + 188 '</div>' +
188 '</div>' 189 '</div>'
189 ); 190 );
@@ -260,3 +260,32 @@ @@ -260,3 +260,32 @@
260 stroke-dashoffset: 500; 260 stroke-dashoffset: 500;
261 } 261 }
262 } 262 }
  263 +
  264 +.tb-rule-node-tooltip {
  265 + font-size: 14px;
  266 + width: 300px;
  267 + color: #333;
  268 + #tooltip-content {
  269 + .tb-node-title {
  270 + font-weight: 600;
  271 + }
  272 + .tb-node-description {
  273 + font-style: italic;
  274 + color: #555;
  275 + }
  276 + .tb-node-details {
  277 + padding-top: 10px;
  278 + padding-bottom: 10px;
  279 + }
  280 + code {
  281 + padding: 0px 3px 2px 3px;
  282 + margin: 1px;
  283 + color: #AD1625;
  284 + white-space: nowrap;
  285 + background-color: #f7f7f9;
  286 + border: 1px solid #e1e1e8;
  287 + border-radius: 2px;
  288 + font-size: 12px;
  289 + }
  290 + }
  291 +}