Commit 344f6045a64dd2f3ff9aaeade9629868fd63754d

Authored by Igor Kulikov
1 parent a5c59e5c

Use ComponentDescriptors in RuleChain UI

@@ -71,6 +71,7 @@ import javax.servlet.http.HttpServletRequest; @@ -71,6 +71,7 @@ import javax.servlet.http.HttpServletRequest;
71 import javax.servlet.http.HttpServletResponse; 71 import javax.servlet.http.HttpServletResponse;
72 import java.util.List; 72 import java.util.List;
73 import java.util.Optional; 73 import java.util.Optional;
  74 +import java.util.Set;
74 import java.util.UUID; 75 import java.util.UUID;
75 76
76 import static org.thingsboard.server.dao.service.Validator.validateId; 77 import static org.thingsboard.server.dao.service.Validator.validateId;
@@ -480,6 +481,15 @@ public abstract class BaseController { @@ -480,6 +481,15 @@ public abstract class BaseController {
480 } 481 }
481 } 482 }
482 483
  484 + List<ComponentDescriptor> checkComponentDescriptorsByTypes(Set<ComponentType> types) throws ThingsboardException {
  485 + try {
  486 + log.debug("[{}] Lookup component descriptors", types);
  487 + return componentDescriptorService.getComponents(types);
  488 + } catch (Exception e) {
  489 + throw handleException(e, false);
  490 + }
  491 + }
  492 +
483 List<ComponentDescriptor> checkPluginActionsByPluginClazz(String pluginClazz) throws ThingsboardException { 493 List<ComponentDescriptor> checkPluginActionsByPluginClazz(String pluginClazz) throws ThingsboardException {
484 try { 494 try {
485 checkComponentDescriptorByClazz(pluginClazz); 495 checkComponentDescriptorByClazz(pluginClazz);
@@ -21,7 +21,9 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor; @@ -21,7 +21,9 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
21 import org.thingsboard.server.common.data.plugin.ComponentType; 21 import org.thingsboard.server.common.data.plugin.ComponentType;
22 import org.thingsboard.server.exception.ThingsboardException; 22 import org.thingsboard.server.exception.ThingsboardException;
23 23
  24 +import java.util.HashSet;
24 import java.util.List; 25 import java.util.List;
  26 +import java.util.Set;
25 27
26 @RestController 28 @RestController
27 @RequestMapping("/api") 29 @RequestMapping("/api")
@@ -52,6 +54,22 @@ public class ComponentDescriptorController extends BaseController { @@ -52,6 +54,22 @@ public class ComponentDescriptorController extends BaseController {
52 } 54 }
53 55
54 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')") 56 @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
  57 + @RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET)
  58 + @ResponseBody
  59 + public List<ComponentDescriptor> getComponentDescriptorsByTypes(@RequestParam("componentTypes") String[] strComponentTypes) throws ThingsboardException {
  60 + checkArrayParameter("componentTypes", strComponentTypes);
  61 + try {
  62 + Set<ComponentType> componentTypes = new HashSet<>();
  63 + for (String strComponentType : strComponentTypes) {
  64 + componentTypes.add(ComponentType.valueOf(strComponentType));
  65 + }
  66 + return checkComponentDescriptorsByTypes(componentTypes);
  67 + } catch (Exception e) {
  68 + throw handleException(e);
  69 + }
  70 + }
  71 +
  72 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
55 @RequestMapping(value = "/components/actions/{pluginClazz:.+}", method = RequestMethod.GET) 73 @RequestMapping(value = "/components/actions/{pluginClazz:.+}", method = RequestMethod.GET)
56 @ResponseBody 74 @ResponseBody
57 public List<ComponentDescriptor> getPluginActionsByPluginClazz(@PathVariable("pluginClazz") String pluginClazz) throws ThingsboardException { 75 public List<ComponentDescriptor> getPluginActionsByPluginClazz(@PathVariable("pluginClazz") String pluginClazz) throws ThingsboardException {
@@ -204,6 +204,15 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe @@ -204,6 +204,15 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
204 } 204 }
205 205
206 @Override 206 @Override
  207 + public List<ComponentDescriptor> getComponents(Set<ComponentType> types) {
  208 + List<ComponentDescriptor> result = new ArrayList<>();
  209 + for (ComponentType type : types) {
  210 + result.addAll(componentsMap.get(type));
  211 + }
  212 + return Collections.unmodifiableList(result);
  213 + }
  214 +
  215 + @Override
207 public Optional<ComponentDescriptor> getComponent(String clazz) { 216 public Optional<ComponentDescriptor> getComponent(String clazz) {
208 return Optional.ofNullable(components.get(clazz)); 217 return Optional.ofNullable(components.get(clazz));
209 } 218 }
@@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType; @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
20 20
21 import java.util.List; 21 import java.util.List;
22 import java.util.Optional; 22 import java.util.Optional;
  23 +import java.util.Set;
23 24
24 /** 25 /**
25 * @author Andrew Shvayka 26 * @author Andrew Shvayka
@@ -30,6 +31,8 @@ public interface ComponentDiscoveryService { @@ -30,6 +31,8 @@ public interface ComponentDiscoveryService {
30 31
31 List<ComponentDescriptor> getComponents(ComponentType type); 32 List<ComponentDescriptor> getComponents(ComponentType type);
32 33
  34 + List<ComponentDescriptor> getComponents(Set<ComponentType> types);
  35 +
33 Optional<ComponentDescriptor> getComponent(String clazz); 36 Optional<ComponentDescriptor> getComponent(String clazz);
34 37
35 List<ComponentDescriptor> getPluginActions(String pluginClazz); 38 List<ComponentDescriptor> getPluginActions(String pluginClazz);
@@ -26,7 +26,8 @@ function ComponentDescriptorService($http, $q) { @@ -26,7 +26,8 @@ function ComponentDescriptorService($http, $q) {
26 var service = { 26 var service = {
27 getComponentDescriptorsByType: getComponentDescriptorsByType, 27 getComponentDescriptorsByType: getComponentDescriptorsByType,
28 getComponentDescriptorByClazz: getComponentDescriptorByClazz, 28 getComponentDescriptorByClazz: getComponentDescriptorByClazz,
29 - getPluginActionsByPluginClazz: getPluginActionsByPluginClazz 29 + getPluginActionsByPluginClazz: getPluginActionsByPluginClazz,
  30 + getComponentDescriptorsByTypes: getComponentDescriptorsByTypes
30 } 31 }
31 32
32 return service; 33 return service;
@@ -52,6 +53,41 @@ function ComponentDescriptorService($http, $q) { @@ -52,6 +53,41 @@ function ComponentDescriptorService($http, $q) {
52 return deferred.promise; 53 return deferred.promise;
53 } 54 }
54 55
  56 + function getComponentDescriptorsByTypes(componentTypes) {
  57 + var deferred = $q.defer();
  58 + var result = [];
  59 + for (var i=componentTypes.length-1;i>=0;i--) {
  60 + var componentType = componentTypes[i];
  61 + if (componentsByType[componentType]) {
  62 + result = result.concat(componentsByType[componentType]);
  63 + componentTypes.splice(i, 1);
  64 + }
  65 + }
  66 + if (!componentTypes.length) {
  67 + deferred.resolve(result);
  68 + } else {
  69 + var url = '/api/components?componentTypes=' + componentTypes.join(',');
  70 + $http.get(url, null).then(function success(response) {
  71 + var components = response.data;
  72 + for (var i = 0; i < components.length; i++) {
  73 + var component = components[i];
  74 + var componentsList = componentsByType[component.type];
  75 + if (!componentsList) {
  76 + componentsList = [];
  77 + componentsByType[component.type] = componentsList;
  78 + }
  79 + componentsList.push(component);
  80 + componentsByClazz[component.clazz] = component;
  81 + }
  82 + result = result.concat(components);
  83 + deferred.resolve(components);
  84 + }, function fail() {
  85 + deferred.reject();
  86 + });
  87 + }
  88 + return deferred.promise;
  89 + }
  90 +
55 function getComponentDescriptorByClazz(componentDescriptorClazz) { 91 function getComponentDescriptorByClazz(componentDescriptorClazz) {
56 var deferred = $q.defer(); 92 var deferred = $q.defer();
57 if (componentsByClazz[componentDescriptorClazz]) { 93 if (componentsByClazz[componentDescriptorClazz]) {
@@ -17,9 +17,9 @@ export default angular.module('thingsboard.api.ruleChain', []) @@ -17,9 +17,9 @@ export default angular.module('thingsboard.api.ruleChain', [])
17 .factory('ruleChainService', RuleChainService).name; 17 .factory('ruleChainService', RuleChainService).name;
18 18
19 /*@ngInject*/ 19 /*@ngInject*/
20 -function RuleChainService($http, $q, $filter, types) { 20 +function RuleChainService($http, $q, $filter, types, componentDescriptorService) {
21 21
22 - var ruleNodeTypes = null; 22 + var ruleNodeComponents = null;
23 23
24 var service = { 24 var service = {
25 getSystemRuleChains: getSystemRuleChains, 25 getSystemRuleChains: getSystemRuleChains,
@@ -30,8 +30,8 @@ function RuleChainService($http, $q, $filter, types) { @@ -30,8 +30,8 @@ function RuleChainService($http, $q, $filter, types) {
30 deleteRuleChain: deleteRuleChain, 30 deleteRuleChain: deleteRuleChain,
31 getRuleChainMetaData: getRuleChainMetaData, 31 getRuleChainMetaData: getRuleChainMetaData,
32 saveRuleChainMetaData: saveRuleChainMetaData, 32 saveRuleChainMetaData: saveRuleChainMetaData,
33 - getRuleNodeTypes: getRuleNodeTypes,  
34 - getRuleNodeComponentType: getRuleNodeComponentType, 33 + getRuleNodeComponents: getRuleNodeComponents,
  34 + getRuleNodeComponentByClazz: getRuleNodeComponentByClazz,
35 getRuleNodeSupportedLinks: getRuleNodeSupportedLinks, 35 getRuleNodeSupportedLinks: getRuleNodeSupportedLinks,
36 resolveTargetRuleChains: resolveTargetRuleChains 36 resolveTargetRuleChains: resolveTargetRuleChains
37 }; 37 };
@@ -165,21 +165,18 @@ function RuleChainService($http, $q, $filter, types) { @@ -165,21 +165,18 @@ function RuleChainService($http, $q, $filter, types) {
165 return deferred.promise; 165 return deferred.promise;
166 } 166 }
167 167
168 - function getRuleNodeTypes() { 168 + function getRuleNodeComponents() {
169 var deferred = $q.defer(); 169 var deferred = $q.defer();
170 - if (ruleNodeTypes) {  
171 - deferred.resolve(ruleNodeTypes); 170 + if (ruleNodeComponents) {
  171 + deferred.resolve(ruleNodeComponents);
172 } else { 172 } else {
173 - loadRuleNodeTypes().then(  
174 - (nodeTypes) => {  
175 - ruleNodeTypes = nodeTypes;  
176 - ruleNodeTypes.push(  
177 - {  
178 - nodeType: types.ruleNodeType.RULE_CHAIN.value,  
179 - type: 'Rule chain'  
180 - } 173 + loadRuleNodeComponents().then(
  174 + (components) => {
  175 + ruleNodeComponents = components;
  176 + ruleNodeComponents.push(
  177 + types.ruleChainNodeComponent
181 ); 178 );
182 - deferred.resolve(ruleNodeTypes); 179 + deferred.resolve(ruleNodeComponents);
183 }, 180 },
184 () => { 181 () => {
185 deferred.reject(); 182 deferred.reject();
@@ -189,10 +186,10 @@ function RuleChainService($http, $q, $filter, types) { @@ -189,10 +186,10 @@ function RuleChainService($http, $q, $filter, types) {
189 return deferred.promise; 186 return deferred.promise;
190 } 187 }
191 188
192 - function getRuleNodeComponentType(type) {  
193 - var res = $filter('filter')(ruleNodeTypes, {type: type}, true); 189 + function getRuleNodeComponentByClazz(clazz) {
  190 + var res = $filter('filter')(ruleNodeComponents, {clazz: clazz}, true);
194 if (res && res.length) { 191 if (res && res.length) {
195 - return res[0].nodeType; 192 + return res[0];
196 } 193 }
197 return null; 194 return null;
198 } 195 }
@@ -222,61 +219,8 @@ function RuleChainService($http, $q, $filter, types) { @@ -222,61 +219,8 @@ function RuleChainService($http, $q, $filter, types) {
222 return deferred.promise; 219 return deferred.promise;
223 } 220 }
224 221
225 - function loadRuleNodeTypes() {  
226 - var deferred = $q.defer();  
227 - deferred.resolve(  
228 - [  
229 - {  
230 - nodeType: types.ruleNodeType.FILTER.value,  
231 - type: 'Filter'  
232 - },  
233 - {  
234 - nodeType: types.ruleNodeType.FILTER.value,  
235 - type: 'Switch'  
236 - },  
237 - {  
238 - nodeType: types.ruleNodeType.ENRICHMENT.value,  
239 - type: 'Self'  
240 - },  
241 - {  
242 - nodeType: types.ruleNodeType.ENRICHMENT.value,  
243 - type: 'Tenant/Customer'  
244 - },  
245 - {  
246 - nodeType: types.ruleNodeType.ENRICHMENT.value,  
247 - type: 'Related Entity'  
248 - },  
249 - {  
250 - nodeType: types.ruleNodeType.ENRICHMENT.value,  
251 - type: 'Last Telemetry'  
252 - },  
253 - {  
254 - nodeType: types.ruleNodeType.TRANSFORMATION.value,  
255 - type: 'Modify'  
256 - },  
257 - {  
258 - nodeType: types.ruleNodeType.TRANSFORMATION.value,  
259 - type: 'New/Update'  
260 - },  
261 - {  
262 - nodeType: types.ruleNodeType.ACTION.value,  
263 - type: 'Telemetry'  
264 - },  
265 - {  
266 - nodeType: types.ruleNodeType.ACTION.value,  
267 - type: 'RPC call'  
268 - },  
269 - {  
270 - nodeType: types.ruleNodeType.ACTION.value,  
271 - type: 'Send email'  
272 - },  
273 - {  
274 - nodeType: types.ruleNodeType.ACTION.value,  
275 - type: 'Alarm'  
276 - }  
277 - ]  
278 - );  
279 - return deferred.promise; 222 + function loadRuleNodeComponents() {
  223 + return componentDescriptorService.getComponentDescriptorsByTypes(types.ruleNodeTypeComponentTypes);
280 } 224 }
281 225
282 226
@@ -457,6 +457,17 @@ export default angular.module('thingsboard.types', []) @@ -457,6 +457,17 @@ export default angular.module('thingsboard.types', [])
457 clientSide: false 457 clientSide: false
458 } 458 }
459 }, 459 },
  460 + ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION"],
  461 + ruleChainNodeComponent: {
  462 + type: 'RULE_CHAIN',
  463 + name: 'Rule chain',
  464 + clazz: 'tb.internal.RuleChain'
  465 + },
  466 + inputNodeComponent: {
  467 + type: 'INPUT',
  468 + name: 'Input',
  469 + clazz: 'tb.internal.Input'
  470 + },
460 ruleNodeType: { 471 ruleNodeType: {
461 FILTER: { 472 FILTER: {
462 value: "FILTER", 473 value: "FILTER",
@@ -1167,7 +1167,8 @@ export default angular.module('thingsboard.locale', []) @@ -1167,7 +1167,8 @@ export default angular.module('thingsboard.locale', [])
1167 "select-rulechain": "Select rule chain", 1167 "select-rulechain": "Select rule chain",
1168 "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.", 1168 "no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
1169 "rulechain-required": "Rule chain is required", 1169 "rulechain-required": "Rule chain is required",
1170 - "management": "Rules management" 1170 + "management": "Rules management",
  1171 + "debug-mode": "Debug mode"
1171 }, 1172 },
1172 "rulenode": { 1173 "rulenode": {
1173 "add": "Add rule node", 1174 "add": "Add rule node",
@@ -1177,6 +1178,7 @@ export default angular.module('thingsboard.locale', []) @@ -1177,6 +1178,7 @@ export default angular.module('thingsboard.locale', [])
1177 "description": "Description", 1178 "description": "Description",
1178 "delete": "Delete rule node", 1179 "delete": "Delete rule node",
1179 "rulenode-details": "Rule node details", 1180 "rulenode-details": "Rule node details",
  1181 + "debug-mode": "Debug mode",
1180 "link-details": "Rule node link details", 1182 "link-details": "Rule node link details",
1181 "add-link": "Add link", 1183 "add-link": "Add link",
1182 "link-label": "Link label", 1184 "link-label": "Link label",
@@ -42,6 +42,11 @@ @@ -42,6 +42,11 @@
42 </div> 42 </div>
43 </md-input-container> 43 </md-input-container>
44 <md-input-container class="md-block"> 44 <md-input-container class="md-block">
  45 + <md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulechain.debug-mode' | translate }}"
  46 + ng-model="ruleChain.debugMode">{{ 'rulechain.debug-mode' | translate }}
  47 + </md-checkbox>
  48 + </md-input-container>
  49 + <md-input-container class="md-block">
45 <label translate>rulechain.description</label> 50 <label translate>rulechain.description</label>
46 <textarea ng-model="ruleChain.additionalInfo.description" rows="2"></textarea> 51 <textarea ng-model="ruleChain.additionalInfo.description" rows="2"></textarea>
47 </md-input-container> 52 </md-input-container>
@@ -54,6 +54,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -54,6 +54,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
54 }; 54 };
55 55
56 vm.ruleNodeTypesModel = {}; 56 vm.ruleNodeTypesModel = {};
  57 + vm.ruleChainLibraryLoaded = false;
57 for (var type in types.ruleNodeType) { 58 for (var type in types.ruleNodeType) {
58 if (!types.ruleNodeType[type].special) { 59 if (!types.ruleNodeType[type].special) {
59 vm.ruleNodeTypesModel[type] = { 60 vm.ruleNodeTypesModel[type] = {
@@ -141,8 +142,8 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -141,8 +142,8 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
141 vm.editCallbacks = { 142 vm.editCallbacks = {
142 edgeDoubleClick: function (event, edge) { 143 edgeDoubleClick: function (event, edge) {
143 var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source); 144 var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source);
144 - if (sourceNode.nodeType != types.ruleNodeType.INPUT.value) {  
145 - ruleChainService.getRuleNodeSupportedLinks(sourceNode.type).then( 145 + if (sourceNode.component.type != types.ruleNodeType.INPUT.value) {
  146 + ruleChainService.getRuleNodeSupportedLinks(sourceNode.component.clazz).then(
146 (labels) => { 147 (labels) => {
147 vm.isEditingRuleNode = false; 148 vm.isEditingRuleNode = false;
148 vm.editingRuleNode = null; 149 vm.editingRuleNode = null;
@@ -156,7 +157,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -156,7 +157,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
156 }, 157 },
157 nodeCallbacks: { 158 nodeCallbacks: {
158 'doubleClick': function (event, node) { 159 'doubleClick': function (event, node) {
159 - if (node.nodeType != types.ruleNodeType.INPUT.value) { 160 + if (node.component.type != types.ruleNodeType.INPUT.value) {
160 vm.isEditingRuleNodeLink = false; 161 vm.isEditingRuleNodeLink = false;
161 vm.editingRuleNodeLink = null; 162 vm.editingRuleNodeLink = null;
162 vm.isEditingRuleNode = true; 163 vm.isEditingRuleNode = true;
@@ -171,9 +172,9 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -171,9 +172,9 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
171 createEdge: function (event, edge) { 172 createEdge: function (event, edge) {
172 var deferred = $q.defer(); 173 var deferred = $q.defer();
173 var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source); 174 var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source);
174 - if (sourceNode.nodeType == types.ruleNodeType.INPUT.value) { 175 + if (sourceNode.component.type == types.ruleNodeType.INPUT.value) {
175 var destNode = vm.modelservice.nodes.getNodeByConnectorId(edge.destination); 176 var destNode = vm.modelservice.nodes.getNodeByConnectorId(edge.destination);
176 - if (destNode.nodeType == types.ruleNodeType.RULE_CHAIN.value) { 177 + if (destNode.component.type == types.ruleNodeType.RULE_CHAIN.value) {
177 deferred.reject(); 178 deferred.reject();
178 } else { 179 } else {
179 var res = $filter('filter')(vm.ruleChainModel.edges, {source: vm.inputConnectorId}); 180 var res = $filter('filter')(vm.ruleChainModel.edges, {source: vm.inputConnectorId});
@@ -183,7 +184,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -183,7 +184,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
183 deferred.resolve(edge); 184 deferred.resolve(edge);
184 } 185 }
185 } else { 186 } else {
186 - ruleChainService.getRuleNodeSupportedLinks(sourceNode.type).then( 187 + ruleChainService.getRuleNodeSupportedLinks(sourceNode.component.clazz).then(
187 (labels) => { 188 (labels) => {
188 addRuleNodeLink(event, edge, labels).then( 189 addRuleNodeLink(event, edge, labels).then(
189 (link) => { 190 (link) => {
@@ -209,24 +210,23 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -209,24 +210,23 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
209 loadRuleChainLibrary(); 210 loadRuleChainLibrary();
210 211
211 function loadRuleChainLibrary() { 212 function loadRuleChainLibrary() {
212 - ruleChainService.getRuleNodeTypes().then(  
213 - (ruleNodeTypes) => {  
214 - for (var i=0;i<ruleNodeTypes.length;i++) {  
215 - var ruleNodeType = ruleNodeTypes[i];  
216 - var nodeType = ruleNodeType.nodeType;  
217 - var model = vm.ruleNodeTypesModel[nodeType].model; 213 + ruleChainService.getRuleNodeComponents().then(
  214 + (ruleNodeComponents) => {
  215 + for (var i=0;i<ruleNodeComponents.length;i++) {
  216 + var ruleNodeComponent = ruleNodeComponents[i];
  217 + var componentType = ruleNodeComponent.type;
  218 + var model = vm.ruleNodeTypesModel[componentType].model;
218 var node = { 219 var node = {
219 id: model.nodes.length, 220 id: model.nodes.length,
220 - nodeType: nodeType,  
221 - type: ruleNodeType.type, 221 + component: ruleNodeComponent,
222 name: '', 222 name: '',
223 - nodeClass: vm.types.ruleNodeType[nodeType].nodeClass,  
224 - icon: vm.types.ruleNodeType[nodeType].icon, 223 + nodeClass: vm.types.ruleNodeType[componentType].nodeClass,
  224 + icon: vm.types.ruleNodeType[componentType].icon,
225 x: 30, 225 x: 30,
226 y: 10+50*model.nodes.length, 226 y: 10+50*model.nodes.length,
227 connectors: [] 227 connectors: []
228 }; 228 };
229 - if (nodeType == types.ruleNodeType.RULE_CHAIN.value) { 229 + if (componentType == types.ruleNodeType.RULE_CHAIN.value) {
230 node.connectors.push( 230 node.connectors.push(
231 { 231 {
232 type: flowchartConstants.leftConnectorType, 232 type: flowchartConstants.leftConnectorType,
@@ -249,6 +249,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -249,6 +249,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
249 } 249 }
250 model.nodes.push(node); 250 model.nodes.push(node);
251 } 251 }
  252 + vm.ruleChainLibraryLoaded = true;
252 prepareRuleChain(); 253 prepareRuleChain();
253 } 254 }
254 ); 255 );
@@ -273,9 +274,8 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -273,9 +274,8 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
273 vm.ruleChainModel.nodes.push( 274 vm.ruleChainModel.nodes.push(
274 { 275 {
275 id: vm.nextNodeID++, 276 id: vm.nextNodeID++,
276 - type: "Input", 277 + component: types.inputNodeComponent,
277 name: "", 278 name: "",
278 - nodeType: types.ruleNodeType.INPUT.value,  
279 nodeClass: types.ruleNodeType.INPUT.nodeClass, 279 nodeClass: types.ruleNodeType.INPUT.nodeClass,
280 icon: types.ruleNodeType.INPUT.icon, 280 icon: types.ruleNodeType.INPUT.icon,
281 readonly: true, 281 readonly: true,
@@ -301,20 +301,20 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -301,20 +301,20 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
301 var nodes = []; 301 var nodes = [];
302 for (var i=0;i<vm.ruleChainMetaData.nodes.length;i++) { 302 for (var i=0;i<vm.ruleChainMetaData.nodes.length;i++) {
303 var ruleNode = vm.ruleChainMetaData.nodes[i]; 303 var ruleNode = vm.ruleChainMetaData.nodes[i];
304 - var nodeType = ruleChainService.getRuleNodeComponentType(ruleNode.type);  
305 - if (nodeType) { 304 + var component = ruleChainService.getRuleNodeComponentByClazz(ruleNode.type);
  305 + if (component) {
306 var node = { 306 var node = {
307 id: vm.nextNodeID++, 307 id: vm.nextNodeID++,
308 ruleNodeId: ruleNode.id, 308 ruleNodeId: ruleNode.id,
309 additionalInfo: ruleNode.additionalInfo, 309 additionalInfo: ruleNode.additionalInfo,
310 configuration: ruleNode.configuration, 310 configuration: ruleNode.configuration,
  311 + debugMode: ruleNode.debugMode,
311 x: ruleNode.additionalInfo.layoutX, 312 x: ruleNode.additionalInfo.layoutX,
312 y: ruleNode.additionalInfo.layoutY, 313 y: ruleNode.additionalInfo.layoutY,
313 - type: ruleNode.type, 314 + component: component,
314 name: ruleNode.name, 315 name: ruleNode.name,
315 - nodeType: nodeType,  
316 - nodeClass: vm.types.ruleNodeType[nodeType].nodeClass,  
317 - icon: vm.types.ruleNodeType[nodeType].icon, 316 + nodeClass: vm.types.ruleNodeType[component.type].nodeClass,
  317 + icon: vm.types.ruleNodeType[component.type].icon,
318 connectors: [ 318 connectors: [
319 { 319 {
320 type: flowchartConstants.leftConnectorType, 320 type: flowchartConstants.leftConnectorType,
@@ -347,7 +347,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -347,7 +347,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
347 347
348 if (vm.ruleChainMetaData.connections) { 348 if (vm.ruleChainMetaData.connections) {
349 for (i = 0; i < vm.ruleChainMetaData.connections.length; i++) { 349 for (i = 0; i < vm.ruleChainMetaData.connections.length; i++) {
350 - var connection = vm.ruleChainMetaData.connections[0]; 350 + var connection = vm.ruleChainMetaData.connections[i];
351 var sourceNode = nodes[connection.fromIndex]; 351 var sourceNode = nodes[connection.fromIndex];
352 destNode = nodes[connection.toIndex]; 352 destNode = nodes[connection.toIndex];
353 if (sourceNode && destNode) { 353 if (sourceNode && destNode) {
@@ -379,9 +379,8 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -379,9 +379,8 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
379 targetRuleChainId: ruleChainConnection.targetRuleChainId.id, 379 targetRuleChainId: ruleChainConnection.targetRuleChainId.id,
380 x: ruleChainConnection.additionalInfo.layoutX, 380 x: ruleChainConnection.additionalInfo.layoutX,
381 y: ruleChainConnection.additionalInfo.layoutY, 381 y: ruleChainConnection.additionalInfo.layoutY,
382 - type: 'Rule chain', 382 + component: types.ruleChainNodeComponent,
383 name: ruleChain.name, 383 name: ruleChain.name,
384 - nodeType: vm.types.ruleNodeType.RULE_CHAIN.value,  
385 nodeClass: vm.types.ruleNodeType.RULE_CHAIN.nodeClass, 384 nodeClass: vm.types.ruleNodeType.RULE_CHAIN.nodeClass,
386 icon: vm.types.ruleNodeType.RULE_CHAIN.icon, 385 icon: vm.types.ruleNodeType.RULE_CHAIN.icon,
387 connectors: [ 386 connectors: [
@@ -410,7 +409,9 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -410,7 +409,9 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
410 } 409 }
411 } 410 }
412 411
413 - vm.canvasControl.adjustCanvasSize(); 412 + if (vm.canvasControl.adjustCanvasSize) {
  413 + vm.canvasControl.adjustCanvasSize();
  414 + }
414 415
415 vm.isDirty = false; 416 vm.isDirty = false;
416 417
@@ -437,15 +438,16 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -437,15 +438,16 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
437 438
438 for (var i=0;i<vm.ruleChainModel.nodes.length;i++) { 439 for (var i=0;i<vm.ruleChainModel.nodes.length;i++) {
439 var node = vm.ruleChainModel.nodes[i]; 440 var node = vm.ruleChainModel.nodes[i];
440 - if (node.nodeType != types.ruleNodeType.INPUT.value && node.nodeType != types.ruleNodeType.RULE_CHAIN.value) { 441 + if (node.component.type != types.ruleNodeType.INPUT.value && node.component.type != types.ruleNodeType.RULE_CHAIN.value) {
441 var ruleNode = {}; 442 var ruleNode = {};
442 if (node.ruleNodeId) { 443 if (node.ruleNodeId) {
443 ruleNode.id = node.ruleNodeId; 444 ruleNode.id = node.ruleNodeId;
444 } 445 }
445 - ruleNode.type = node.type; 446 + ruleNode.type = node.component.clazz;
446 ruleNode.name = node.name; 447 ruleNode.name = node.name;
447 ruleNode.configuration = node.configuration; 448 ruleNode.configuration = node.configuration;
448 ruleNode.additionalInfo = node.additionalInfo; 449 ruleNode.additionalInfo = node.additionalInfo;
  450 + ruleNode.debugMode = node.debugMode;
449 if (!ruleNode.additionalInfo) { 451 if (!ruleNode.additionalInfo) {
450 ruleNode.additionalInfo = {}; 452 ruleNode.additionalInfo = {};
451 } 453 }
@@ -465,9 +467,9 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -465,9 +467,9 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
465 var edge = vm.ruleChainModel.edges[i]; 467 var edge = vm.ruleChainModel.edges[i];
466 var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source); 468 var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source);
467 var destNode = vm.modelservice.nodes.getNodeByConnectorId(edge.destination); 469 var destNode = vm.modelservice.nodes.getNodeByConnectorId(edge.destination);
468 - if (sourceNode.nodeType != types.ruleNodeType.INPUT.value) { 470 + if (sourceNode.component.type != types.ruleNodeType.INPUT.value) {
469 var fromIndex = nodes.indexOf(sourceNode); 471 var fromIndex = nodes.indexOf(sourceNode);
470 - if (destNode.nodeType == types.ruleNodeType.RULE_CHAIN.value) { 472 + if (destNode.component.type == types.ruleNodeType.RULE_CHAIN.value) {
471 var ruleChainConnection = { 473 var ruleChainConnection = {
472 fromIndex: fromIndex, 474 fromIndex: fromIndex,
473 targetRuleChainId: {entityType: vm.types.entityType.rulechain, id: destNode.targetRuleChainId}, 475 targetRuleChainId: {entityType: vm.types.entityType.rulechain, id: destNode.targetRuleChainId},
@@ -522,7 +524,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans @@ -522,7 +524,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
522 type: flowchartConstants.leftConnectorType 524 type: flowchartConstants.leftConnectorType
523 } 525 }
524 ); 526 );
525 - if (ruleNode.nodeType != types.ruleNodeType.RULE_CHAIN.value) { 527 + if (ruleNode.component.type != types.ruleNodeType.RULE_CHAIN.value) {
526 ruleNode.connectors.push( 528 ruleNode.connectors.push(
527 { 529 {
528 id: vm.nextConnectorID++, 530 id: vm.nextConnectorID++,
@@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
21 <section class="tb-rulechain-container" flex layout="column"> 21 <section class="tb-rulechain-container" flex layout="column">
22 <div class="tb-rulechain-layout" flex layout="row"> 22 <div class="tb-rulechain-layout" flex layout="row">
23 <div class="tb-rulechain-library"> 23 <div class="tb-rulechain-library">
24 - <md-expansion-panel-group class="tb-rulechain-library-panel-group" md-component-id="libraryPanelGroup" auto-expand="true" multiple> 24 + <md-expansion-panel-group ng-if="vm.ruleChainLibraryLoaded" class="tb-rulechain-library-panel-group" md-component-id="libraryPanelGroup" auto-expand="true" multiple>
25 <md-expansion-panel md-component-id="{{typeId}}" id="{{typeId}}" ng-repeat="(typeId, typeModel) in vm.ruleNodeTypesModel"> 25 <md-expansion-panel md-component-id="{{typeId}}" id="{{typeId}}" ng-repeat="(typeId, typeModel) in vm.ruleNodeTypesModel">
26 <md-expansion-panel-collapsed> 26 <md-expansion-panel-collapsed>
27 <div class="tb-panel-title" translate>{{vm.types.ruleNodeType[typeId].name}}</div> 27 <div class="tb-panel-title" translate>{{vm.types.ruleNodeType[typeId].name}}</div>
@@ -23,9 +23,9 @@ @@ -23,9 +23,9 @@
23 <fieldset ng-disabled="$root.loading || !isEdit || isReadOnly"> 23 <fieldset ng-disabled="$root.loading || !isEdit || isReadOnly">
24 <md-input-container class="md-block"> 24 <md-input-container class="md-block">
25 <label translate>rulenode.type</label> 25 <label translate>rulenode.type</label>
26 - <input readonly name="type" ng-model="ruleNode.type"> 26 + <input readonly name="type" ng-model="ruleNode.component.name">
27 </md-input-container> 27 </md-input-container>
28 - <section ng-if="ruleNode.nodeType != types.ruleNodeType.RULE_CHAIN.value"> 28 + <section ng-if="ruleNode.component.type != types.ruleNodeType.RULE_CHAIN.value">
29 <md-input-container class="md-block"> 29 <md-input-container class="md-block">
30 <label translate>rulenode.name</label> 30 <label translate>rulenode.name</label>
31 <input required name="name" ng-model="ruleNode.name"> 31 <input required name="name" ng-model="ruleNode.name">
@@ -34,11 +34,16 @@ @@ -34,11 +34,16 @@
34 </div> 34 </div>
35 </md-input-container> 35 </md-input-container>
36 <md-input-container class="md-block"> 36 <md-input-container class="md-block">
  37 + <md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulenode.debug-mode' | translate }}"
  38 + ng-model="ruleNode.debugMode">{{ 'rulenode.debug-mode' | translate }}
  39 + </md-checkbox>
  40 + </md-input-container>
  41 + <md-input-container class="md-block">
37 <label translate>rulenode.description</label> 42 <label translate>rulenode.description</label>
38 <textarea ng-model="ruleNode.additionalInfo.description" rows="2"></textarea> 43 <textarea ng-model="ruleNode.additionalInfo.description" rows="2"></textarea>
39 </md-input-container> 44 </md-input-container>
40 </section> 45 </section>
41 - <section ng-if="ruleNode.nodeType == types.ruleNodeType.RULE_CHAIN.value"> 46 + <section ng-if="ruleNode.component.type == types.ruleNodeType.RULE_CHAIN.value">
42 <tb-entity-autocomplete the-form="theForm" 47 <tb-entity-autocomplete the-form="theForm"
43 ng-disabled="$root.loading || !isEdit || isReadOnly" 48 ng-disabled="$root.loading || !isEdit || isReadOnly"
44 tb-required="true" 49 tb-required="true"
@@ -33,7 +33,7 @@ export default function RuleNodeDirective($compile, $templateCache, ruleChainSer @@ -33,7 +33,7 @@ export default function RuleNodeDirective($compile, $templateCache, ruleChainSer
33 }; 33 };
34 34
35 scope.$watch('ruleNode', function() { 35 scope.$watch('ruleNode', function() {
36 - if (scope.ruleNode && scope.ruleNode.nodeType == types.ruleNodeType.RULE_CHAIN.value) { 36 + if (scope.ruleNode && scope.ruleNode.component.type == types.ruleNodeType.RULE_CHAIN.value) {
37 scope.params.targetRuleChainId = scope.ruleNode.targetRuleChainId; 37 scope.params.targetRuleChainId = scope.ruleNode.targetRuleChainId;
38 watchTargetRuleChain(); 38 watchTargetRuleChain();
39 } else { 39 } else {
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 <md-icon aria-label="node-type-icon" flex="15" 23 <md-icon aria-label="node-type-icon" flex="15"
24 class="material-icons">{{node.icon}}</md-icon> 24 class="material-icons">{{node.icon}}</md-icon>
25 <div layout="column" flex="85" layout-align="center"> 25 <div layout="column" flex="85" layout-align="center">
26 - <span class="tb-node-type">{{ node.type }}</span> 26 + <span class="tb-node-type">{{ node.component.name }}</span>
27 <span class="tb-node-title" ng-if="node.name">{{ node.name }}</span> 27 <span class="tb-node-title" ng-if="node.name">{{ node.name }}</span>
28 </div> 28 </div>
29 <div class="{{flowchartConstants.leftConnectorClass}}"> 29 <div class="{{flowchartConstants.leftConnectorClass}}">