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 16 package org.thingsboard.server.service.component;
17 17
18 18 import com.fasterxml.jackson.databind.ObjectMapper;
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
  20 +import com.fasterxml.jackson.databind.JsonNode;
19 21 import com.google.common.base.Charsets;
20 22 import com.google.common.io.Resources;
21 23 import lombok.extern.slf4j.Slf4j;
... ... @@ -26,16 +28,14 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen
26 28 import org.springframework.core.env.Environment;
27 29 import org.springframework.core.type.filter.AnnotationTypeFilter;
28 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 32 import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
34 33 import org.thingsboard.server.common.data.plugin.ComponentType;
35 34 import org.thingsboard.server.dao.component.ComponentDescriptorService;
36 35 import org.thingsboard.server.extensions.api.component.*;
37 36
38 37 import javax.annotation.PostConstruct;
  38 +import java.io.IOException;
39 39 import java.lang.annotation.Annotation;
40 40 import java.util.*;
41 41 import java.util.stream.Collectors;
... ... @@ -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 91 private void registerComponents(ComponentType type, Class<? extends Annotation> annotation) {
74 92 List<ComponentDescriptor> components = persist(getBeanDefinitions(annotation), type);
75 93 componentsMap.put(type, components);
... ... @@ -97,34 +115,25 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
97 115 String descriptorResourceName;
98 116 switch (type) {
99 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 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 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 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 129 break;
123 130 case OLD_ACTION:
124 131 Action oldActionAnnotation = clazz.getAnnotation(Action.class);
125 132 scannedComponent.setName(oldActionAnnotation.name());
126 133 scannedComponent.setScope(oldActionAnnotation.scope());
127 134 descriptorResourceName = oldActionAnnotation.descriptor();
  135 + scannedComponent.setConfigurationDescriptor(mapper.readTree(
  136 + Resources.toString(Resources.getResource(descriptorResourceName), Charsets.UTF_8)));
128 137 break;
129 138 case PLUGIN:
130 139 Plugin pluginAnnotation = clazz.getAnnotation(Plugin.class);
... ... @@ -143,12 +152,12 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
143 152 }
144 153 }
145 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 157 break;
147 158 default:
148 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 161 scannedComponent.setClazz(clazzName);
153 162 log.info("Processing scanned component: {}", scannedComponent);
154 163 } catch (Exception e) {
... ... @@ -171,6 +180,20 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
171 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 197 private Set<BeanDefinition> getBeanDefinitions(Class<? extends Annotation> componentType) {
175 198 ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
176 199 scanner.addIncludeFilter(new AnnotationTypeFilter(componentType));
... ... @@ -183,13 +206,8 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
183 206
184 207 @Override
185 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 212 registerComponents(ComponentType.OLD_ACTION, Action.class);
195 213
... ... @@ -200,15 +218,19 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
200 218
201 219 @Override
202 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 228 @Override
207 229 public List<ComponentDescriptor> getComponents(Set<ComponentType> types) {
208 230 List<ComponentDescriptor> result = new ArrayList<>();
209   - for (ComponentType type : types) {
  231 + types.stream().filter(type -> componentsMap.containsKey(type)).forEach(type -> {
210 232 result.addAll(componentsMap.get(type));
211   - }
  233 + });
212 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 13 * See the License for the specific language governing permissions and
14 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 16 package org.thingsboard.rule.engine.api;
17 17
18 18 import org.thingsboard.server.common.data.plugin.ComponentScope;
  19 +import org.thingsboard.server.common.data.plugin.ComponentType;
19 20
20 21 import java.lang.annotation.ElementType;
21 22 import java.lang.annotation.Retention;
22 23 import java.lang.annotation.RetentionPolicy;
23 24 import java.lang.annotation.Target;
24 25
25   -/**
26   - * @author Andrew Shvayka
27   - */
28 26 @Retention(RetentionPolicy.RUNTIME)
29 27 @Target(ElementType.TYPE)
30   -public @interface EnrichmentNode {
  28 +public @interface RuleNode {
  29 +
  30 + ComponentType type();
31 31
32 32 String name();
33 33
... ... @@ -41,7 +41,7 @@ public @interface EnrichmentNode {
41 41
42 42 ComponentScope scope() default ComponentScope.TENANT;
43 43
44   - String descriptor() default "EmptyNodeDescriptor.json";
  44 + String defaultConfigResource() default "EmptyNodeConfig.json";
45 45
46 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 19 import org.thingsboard.rule.engine.TbNodeUtils;
20 20 import org.thingsboard.rule.engine.api.*;
21 21 import org.thingsboard.rule.engine.js.NashornJsEngine;
  22 +import org.thingsboard.server.common.data.plugin.ComponentType;
22 23 import org.thingsboard.server.common.msg.TbMsg;
23 24
24 25 import javax.script.Bindings;
... ... @@ -26,12 +27,14 @@ import javax.script.Bindings;
26 27 import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
27 28
28 29 @Slf4j
29   -@FilterNode(name = "script", relationTypes = {"True", "False", "Failure"},
  30 +@RuleNode(
  31 + type = ComponentType.FILTER,
  32 + name = "script", relationTypes = {"True", "False", "Failure"},
30 33 nodeDescription = "Filter incoming messages using JS script",
31 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 38 public class TbJsFilterNode implements TbNode {
36 39
37 40 private TbJsFilterNodeConfiguration config;
... ...
... ... @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
19 19 import org.thingsboard.rule.engine.TbNodeUtils;
20 20 import org.thingsboard.rule.engine.api.*;
21 21 import org.thingsboard.rule.engine.js.NashornJsEngine;
  22 +import org.thingsboard.server.common.data.plugin.ComponentType;
22 23 import org.thingsboard.server.common.msg.TbMsg;
23 24
24 25 import javax.script.Bindings;
... ... @@ -27,12 +28,14 @@ import java.util.Set;
27 28 import static org.thingsboard.rule.engine.DonAsynchron.withCallback;
28 29
29 30 @Slf4j
30   -@FilterNode(name = "switch", customRelations = true,
  31 +@RuleNode(
  32 + type = ComponentType.FILTER,
  33 + name = "switch", customRelations = true,
31 34 nodeDescription = "Route incoming Message to one or multiple output chains",
32 35 nodeDetails = "Node executes configured JS script. Script should return array of next Chain names where Message should be routed. " +
33 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 39 public class TbJsSwitchNode implements TbNode {
37 40
38 41 private TbJsSwitchNodeConfiguration config;
... ...
... ... @@ -18,16 +18,19 @@ package org.thingsboard.rule.engine.filter;
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.thingsboard.rule.engine.TbNodeUtils;
20 20 import org.thingsboard.rule.engine.api.*;
  21 +import org.thingsboard.server.common.data.plugin.ComponentType;
21 22 import org.thingsboard.server.common.msg.TbMsg;
22 23
23 24 /**
24 25 * Created by ashvayka on 19.01.18.
25 26 */
26 27 @Slf4j
27   -@FilterNode(name = "message type",
  28 +@RuleNode(
  29 + type = ComponentType.FILTER,
  30 + name = "message type",
28 31 nodeDescription = "Filter incoming messages by Message Type",
29 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 34 public class TbMsgTypeFilterNode implements TbNode {
32 35
33 36 TbMsgTypeFilterNodeConfiguration config;
... ...
... ... @@ -24,6 +24,7 @@ import org.thingsboard.rule.engine.TbNodeUtils;
24 24 import org.thingsboard.rule.engine.api.*;
25 25 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
26 26 import org.thingsboard.server.common.data.kv.TsKvEntry;
  27 +import org.thingsboard.server.common.data.plugin.ComponentType;
27 28 import org.thingsboard.server.common.msg.TbMsg;
28 29
29 30 import java.util.List;
... ... @@ -35,11 +36,12 @@ import static org.thingsboard.server.common.data.DataConstants.*;
35 36 * Created by ashvayka on 19.01.18.
36 37 */
37 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 45 "If Latest Telemetry enrichment configured, latest telemetry added into metadata without prefix.")
44 46 public class TbGetAttributesNode implements TbNode {
45 47
... ...
... ... @@ -16,17 +16,20 @@
16 16 package org.thingsboard.rule.engine.metadata;
17 17
18 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 20 import org.thingsboard.rule.engine.api.TbContext;
21 21 import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader;
22 22 import org.thingsboard.server.common.data.id.CustomerId;
23 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 29 nodeDescription = "Add Originators Customer Attributes or Latest Telemetry into Message Metadata",
27 30 nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
28 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 33 public class TbGetCustomerAttributeNode extends TbEntityGetAttrNode<CustomerId> {
31 34
32 35 @Override
... ...
... ... @@ -17,22 +17,21 @@ package org.thingsboard.rule.engine.metadata;
17 17
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 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 21 import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader;
26 22
27 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 29 nodeDescription = "Add Originators Related Entity Attributes or Latest Telemetry into Message Metadata",
31 30 nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
32 31 "If multiple Related Entities are found, only first Entity is used for attributes enrichment, other entities are discarded. " +
33 32 "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
34 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 35 public class TbGetRelatedAttributeNode extends TbEntityGetAttrNode<EntityId> {
37 36
38 37 private TbGetRelatedAttrNodeConfiguration config;
... ...
... ... @@ -17,18 +17,21 @@ package org.thingsboard.rule.engine.metadata;
17 17
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 19 import lombok.extern.slf4j.Slf4j;
20   -import org.thingsboard.rule.engine.api.EnrichmentNode;
  20 +import org.thingsboard.rule.engine.api.RuleNode;
21 21 import org.thingsboard.rule.engine.api.TbContext;
22 22 import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader;
23 23 import org.thingsboard.server.common.data.id.EntityId;
24 24 import org.thingsboard.server.common.data.id.TenantId;
  25 +import org.thingsboard.server.common.data.plugin.ComponentType;
25 26
26 27 @Slf4j
27   -@EnrichmentNode(name="tenant attributes",
  28 +@RuleNode(
  29 + type = ComponentType.ENRICHMENT,
  30 + name="tenant attributes",
28 31 nodeDescription = "Add Originators Tenant Attributes or Latest Telemetry into Message Metadata",
29 32 nodeDetails = "If Attributes enrichment configured, server scope attributes are added into Message metadata. " +
30 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 35 public class TbGetTenantAttributeNode extends TbEntityGetAttrNode<TenantId> {
33 36
34 37 @Override
... ...
... ... @@ -27,12 +27,15 @@ import org.thingsboard.rule.engine.util.EntitiesCustomerIdAsyncLoader;
27 27 import org.thingsboard.rule.engine.util.EntitiesRelatedEntityIdAsyncLoader;
28 28 import org.thingsboard.rule.engine.util.EntitiesTenantIdAsyncLoader;
29 29 import org.thingsboard.server.common.data.id.EntityId;
  30 +import org.thingsboard.server.common.data.plugin.ComponentType;
30 31 import org.thingsboard.server.common.msg.TbMsg;
31 32
32 33 import java.util.HashSet;
33 34
34 35 @Slf4j
35   -@TransformationNode(name="change originator",
  36 +@RuleNode(
  37 + type = ComponentType.TRANSFORMATION,
  38 + name="change originator",
36 39 nodeDescription = "Change Message Originator To Tenant/Customer/Related Entity",
37 40 nodeDetails = "Related Entity found using configured relation direction and Relation Type. " +
38 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 19 import org.thingsboard.rule.engine.TbNodeUtils;
20 20 import org.thingsboard.rule.engine.api.*;
21 21 import org.thingsboard.rule.engine.js.NashornJsEngine;
  22 +import org.thingsboard.server.common.data.plugin.ComponentType;
22 23 import org.thingsboard.server.common.msg.TbMsg;
23 24
24 25 import javax.script.Bindings;
25 26
26   -@TransformationNode(name = "script",
  27 +@RuleNode(
  28 + type = ComponentType.TRANSFORMATION,
  29 + name = "script",
27 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 34 public class TbTransformMsgNode extends TbAbstractTransformNode {
32 35
33 36 private TbTransformMsgNodeConfiguration config;
... ...
... ... @@ -460,8 +460,19 @@ export default angular.module('thingsboard.types', [])
460 460 ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION"],
461 461 ruleChainNodeComponent: {
462 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 477 inputNodeComponent: {
467 478 type: 'INPUT',
... ...
... ... @@ -183,7 +183,8 @@ export function RuleChainController($stateParams, $scope, $compile, $q, $mdUtil,
183 183 '<div class="tb-rule-node-tooltip">' +
184 184 '<div id="tooltip-content" layout="column">' +
185 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 188 '</div>' +
188 189 '</div>'
189 190 );
... ...
... ... @@ -260,3 +260,32 @@
260 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 +}
\ No newline at end of file
... ...