Use ComponentDescriptors in RuleChain UI

This commit is contained in:
Igor Kulikov 2018-03-21 17:34:07 +02:00
parent a5c59e5c88
commit 344f6045a6
14 changed files with 161 additions and 116 deletions

View File

@ -71,6 +71,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import static org.thingsboard.server.dao.service.Validator.validateId;
@ -480,6 +481,15 @@ public abstract class BaseController {
}
}
List<ComponentDescriptor> checkComponentDescriptorsByTypes(Set<ComponentType> types) throws ThingsboardException {
try {
log.debug("[{}] Lookup component descriptors", types);
return componentDescriptorService.getComponents(types);
} catch (Exception e) {
throw handleException(e, false);
}
}
List<ComponentDescriptor> checkPluginActionsByPluginClazz(String pluginClazz) throws ThingsboardException {
try {
checkComponentDescriptorByClazz(pluginClazz);

View File

@ -21,7 +21,9 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.exception.ThingsboardException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@RestController
@RequestMapping("/api")
@ -51,6 +53,22 @@ public class ComponentDescriptorController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
@RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET)
@ResponseBody
public List<ComponentDescriptor> getComponentDescriptorsByTypes(@RequestParam("componentTypes") String[] strComponentTypes) throws ThingsboardException {
checkArrayParameter("componentTypes", strComponentTypes);
try {
Set<ComponentType> componentTypes = new HashSet<>();
for (String strComponentType : strComponentTypes) {
componentTypes.add(ComponentType.valueOf(strComponentType));
}
return checkComponentDescriptorsByTypes(componentTypes);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN')")
@RequestMapping(value = "/components/actions/{pluginClazz:.+}", method = RequestMethod.GET)
@ResponseBody

View File

@ -203,6 +203,15 @@ public class AnnotationComponentDiscoveryService implements ComponentDiscoverySe
return Collections.unmodifiableList(componentsMap.get(type));
}
@Override
public List<ComponentDescriptor> getComponents(Set<ComponentType> types) {
List<ComponentDescriptor> result = new ArrayList<>();
for (ComponentType type : types) {
result.addAll(componentsMap.get(type));
}
return Collections.unmodifiableList(result);
}
@Override
public Optional<ComponentDescriptor> getComponent(String clazz) {
return Optional.ofNullable(components.get(clazz));

View File

@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* @author Andrew Shvayka
@ -30,6 +31,8 @@ public interface ComponentDiscoveryService {
List<ComponentDescriptor> getComponents(ComponentType type);
List<ComponentDescriptor> getComponents(Set<ComponentType> types);
Optional<ComponentDescriptor> getComponent(String clazz);
List<ComponentDescriptor> getPluginActions(String pluginClazz);

View File

@ -26,7 +26,8 @@ function ComponentDescriptorService($http, $q) {
var service = {
getComponentDescriptorsByType: getComponentDescriptorsByType,
getComponentDescriptorByClazz: getComponentDescriptorByClazz,
getPluginActionsByPluginClazz: getPluginActionsByPluginClazz
getPluginActionsByPluginClazz: getPluginActionsByPluginClazz,
getComponentDescriptorsByTypes: getComponentDescriptorsByTypes
}
return service;
@ -52,6 +53,41 @@ function ComponentDescriptorService($http, $q) {
return deferred.promise;
}
function getComponentDescriptorsByTypes(componentTypes) {
var deferred = $q.defer();
var result = [];
for (var i=componentTypes.length-1;i>=0;i--) {
var componentType = componentTypes[i];
if (componentsByType[componentType]) {
result = result.concat(componentsByType[componentType]);
componentTypes.splice(i, 1);
}
}
if (!componentTypes.length) {
deferred.resolve(result);
} else {
var url = '/api/components?componentTypes=' + componentTypes.join(',');
$http.get(url, null).then(function success(response) {
var components = response.data;
for (var i = 0; i < components.length; i++) {
var component = components[i];
var componentsList = componentsByType[component.type];
if (!componentsList) {
componentsList = [];
componentsByType[component.type] = componentsList;
}
componentsList.push(component);
componentsByClazz[component.clazz] = component;
}
result = result.concat(components);
deferred.resolve(components);
}, function fail() {
deferred.reject();
});
}
return deferred.promise;
}
function getComponentDescriptorByClazz(componentDescriptorClazz) {
var deferred = $q.defer();
if (componentsByClazz[componentDescriptorClazz]) {

View File

@ -17,9 +17,9 @@ export default angular.module('thingsboard.api.ruleChain', [])
.factory('ruleChainService', RuleChainService).name;
/*@ngInject*/
function RuleChainService($http, $q, $filter, types) {
function RuleChainService($http, $q, $filter, types, componentDescriptorService) {
var ruleNodeTypes = null;
var ruleNodeComponents = null;
var service = {
getSystemRuleChains: getSystemRuleChains,
@ -30,8 +30,8 @@ function RuleChainService($http, $q, $filter, types) {
deleteRuleChain: deleteRuleChain,
getRuleChainMetaData: getRuleChainMetaData,
saveRuleChainMetaData: saveRuleChainMetaData,
getRuleNodeTypes: getRuleNodeTypes,
getRuleNodeComponentType: getRuleNodeComponentType,
getRuleNodeComponents: getRuleNodeComponents,
getRuleNodeComponentByClazz: getRuleNodeComponentByClazz,
getRuleNodeSupportedLinks: getRuleNodeSupportedLinks,
resolveTargetRuleChains: resolveTargetRuleChains
};
@ -165,21 +165,18 @@ function RuleChainService($http, $q, $filter, types) {
return deferred.promise;
}
function getRuleNodeTypes() {
function getRuleNodeComponents() {
var deferred = $q.defer();
if (ruleNodeTypes) {
deferred.resolve(ruleNodeTypes);
if (ruleNodeComponents) {
deferred.resolve(ruleNodeComponents);
} else {
loadRuleNodeTypes().then(
(nodeTypes) => {
ruleNodeTypes = nodeTypes;
ruleNodeTypes.push(
{
nodeType: types.ruleNodeType.RULE_CHAIN.value,
type: 'Rule chain'
}
loadRuleNodeComponents().then(
(components) => {
ruleNodeComponents = components;
ruleNodeComponents.push(
types.ruleChainNodeComponent
);
deferred.resolve(ruleNodeTypes);
deferred.resolve(ruleNodeComponents);
},
() => {
deferred.reject();
@ -189,10 +186,10 @@ function RuleChainService($http, $q, $filter, types) {
return deferred.promise;
}
function getRuleNodeComponentType(type) {
var res = $filter('filter')(ruleNodeTypes, {type: type}, true);
function getRuleNodeComponentByClazz(clazz) {
var res = $filter('filter')(ruleNodeComponents, {clazz: clazz}, true);
if (res && res.length) {
return res[0].nodeType;
return res[0];
}
return null;
}
@ -222,61 +219,8 @@ function RuleChainService($http, $q, $filter, types) {
return deferred.promise;
}
function loadRuleNodeTypes() {
var deferred = $q.defer();
deferred.resolve(
[
{
nodeType: types.ruleNodeType.FILTER.value,
type: 'Filter'
},
{
nodeType: types.ruleNodeType.FILTER.value,
type: 'Switch'
},
{
nodeType: types.ruleNodeType.ENRICHMENT.value,
type: 'Self'
},
{
nodeType: types.ruleNodeType.ENRICHMENT.value,
type: 'Tenant/Customer'
},
{
nodeType: types.ruleNodeType.ENRICHMENT.value,
type: 'Related Entity'
},
{
nodeType: types.ruleNodeType.ENRICHMENT.value,
type: 'Last Telemetry'
},
{
nodeType: types.ruleNodeType.TRANSFORMATION.value,
type: 'Modify'
},
{
nodeType: types.ruleNodeType.TRANSFORMATION.value,
type: 'New/Update'
},
{
nodeType: types.ruleNodeType.ACTION.value,
type: 'Telemetry'
},
{
nodeType: types.ruleNodeType.ACTION.value,
type: 'RPC call'
},
{
nodeType: types.ruleNodeType.ACTION.value,
type: 'Send email'
},
{
nodeType: types.ruleNodeType.ACTION.value,
type: 'Alarm'
}
]
);
return deferred.promise;
function loadRuleNodeComponents() {
return componentDescriptorService.getComponentDescriptorsByTypes(types.ruleNodeTypeComponentTypes);
}

View File

@ -457,6 +457,17 @@ export default angular.module('thingsboard.types', [])
clientSide: false
}
},
ruleNodeTypeComponentTypes: ["FILTER", "ENRICHMENT", "TRANSFORMATION", "ACTION"],
ruleChainNodeComponent: {
type: 'RULE_CHAIN',
name: 'Rule chain',
clazz: 'tb.internal.RuleChain'
},
inputNodeComponent: {
type: 'INPUT',
name: 'Input',
clazz: 'tb.internal.Input'
},
ruleNodeType: {
FILTER: {
value: "FILTER",

View File

@ -1167,7 +1167,8 @@ export default angular.module('thingsboard.locale', [])
"select-rulechain": "Select rule chain",
"no-rulechains-matching": "No rule chains matching '{{entity}}' were found.",
"rulechain-required": "Rule chain is required",
"management": "Rules management"
"management": "Rules management",
"debug-mode": "Debug mode"
},
"rulenode": {
"add": "Add rule node",
@ -1177,6 +1178,7 @@ export default angular.module('thingsboard.locale', [])
"description": "Description",
"delete": "Delete rule node",
"rulenode-details": "Rule node details",
"debug-mode": "Debug mode",
"link-details": "Rule node link details",
"add-link": "Add link",
"link-label": "Link label",

View File

@ -41,6 +41,11 @@
<div translate ng-message="required">rulechain.name-required</div>
</div>
</md-input-container>
<md-input-container class="md-block">
<md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulechain.debug-mode' | translate }}"
ng-model="ruleChain.debugMode">{{ 'rulechain.debug-mode' | translate }}
</md-checkbox>
</md-input-container>
<md-input-container class="md-block">
<label translate>rulechain.description</label>
<textarea ng-model="ruleChain.additionalInfo.description" rows="2"></textarea>

View File

@ -54,6 +54,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
};
vm.ruleNodeTypesModel = {};
vm.ruleChainLibraryLoaded = false;
for (var type in types.ruleNodeType) {
if (!types.ruleNodeType[type].special) {
vm.ruleNodeTypesModel[type] = {
@ -141,8 +142,8 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
vm.editCallbacks = {
edgeDoubleClick: function (event, edge) {
var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source);
if (sourceNode.nodeType != types.ruleNodeType.INPUT.value) {
ruleChainService.getRuleNodeSupportedLinks(sourceNode.type).then(
if (sourceNode.component.type != types.ruleNodeType.INPUT.value) {
ruleChainService.getRuleNodeSupportedLinks(sourceNode.component.clazz).then(
(labels) => {
vm.isEditingRuleNode = false;
vm.editingRuleNode = null;
@ -156,7 +157,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
},
nodeCallbacks: {
'doubleClick': function (event, node) {
if (node.nodeType != types.ruleNodeType.INPUT.value) {
if (node.component.type != types.ruleNodeType.INPUT.value) {
vm.isEditingRuleNodeLink = false;
vm.editingRuleNodeLink = null;
vm.isEditingRuleNode = true;
@ -171,9 +172,9 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
createEdge: function (event, edge) {
var deferred = $q.defer();
var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source);
if (sourceNode.nodeType == types.ruleNodeType.INPUT.value) {
if (sourceNode.component.type == types.ruleNodeType.INPUT.value) {
var destNode = vm.modelservice.nodes.getNodeByConnectorId(edge.destination);
if (destNode.nodeType == types.ruleNodeType.RULE_CHAIN.value) {
if (destNode.component.type == types.ruleNodeType.RULE_CHAIN.value) {
deferred.reject();
} else {
var res = $filter('filter')(vm.ruleChainModel.edges, {source: vm.inputConnectorId});
@ -183,7 +184,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
deferred.resolve(edge);
}
} else {
ruleChainService.getRuleNodeSupportedLinks(sourceNode.type).then(
ruleChainService.getRuleNodeSupportedLinks(sourceNode.component.clazz).then(
(labels) => {
addRuleNodeLink(event, edge, labels).then(
(link) => {
@ -209,24 +210,23 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
loadRuleChainLibrary();
function loadRuleChainLibrary() {
ruleChainService.getRuleNodeTypes().then(
(ruleNodeTypes) => {
for (var i=0;i<ruleNodeTypes.length;i++) {
var ruleNodeType = ruleNodeTypes[i];
var nodeType = ruleNodeType.nodeType;
var model = vm.ruleNodeTypesModel[nodeType].model;
ruleChainService.getRuleNodeComponents().then(
(ruleNodeComponents) => {
for (var i=0;i<ruleNodeComponents.length;i++) {
var ruleNodeComponent = ruleNodeComponents[i];
var componentType = ruleNodeComponent.type;
var model = vm.ruleNodeTypesModel[componentType].model;
var node = {
id: model.nodes.length,
nodeType: nodeType,
type: ruleNodeType.type,
component: ruleNodeComponent,
name: '',
nodeClass: vm.types.ruleNodeType[nodeType].nodeClass,
icon: vm.types.ruleNodeType[nodeType].icon,
nodeClass: vm.types.ruleNodeType[componentType].nodeClass,
icon: vm.types.ruleNodeType[componentType].icon,
x: 30,
y: 10+50*model.nodes.length,
connectors: []
};
if (nodeType == types.ruleNodeType.RULE_CHAIN.value) {
if (componentType == types.ruleNodeType.RULE_CHAIN.value) {
node.connectors.push(
{
type: flowchartConstants.leftConnectorType,
@ -249,6 +249,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
}
model.nodes.push(node);
}
vm.ruleChainLibraryLoaded = true;
prepareRuleChain();
}
);
@ -273,9 +274,8 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
vm.ruleChainModel.nodes.push(
{
id: vm.nextNodeID++,
type: "Input",
component: types.inputNodeComponent,
name: "",
nodeType: types.ruleNodeType.INPUT.value,
nodeClass: types.ruleNodeType.INPUT.nodeClass,
icon: types.ruleNodeType.INPUT.icon,
readonly: true,
@ -301,20 +301,20 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
var nodes = [];
for (var i=0;i<vm.ruleChainMetaData.nodes.length;i++) {
var ruleNode = vm.ruleChainMetaData.nodes[i];
var nodeType = ruleChainService.getRuleNodeComponentType(ruleNode.type);
if (nodeType) {
var component = ruleChainService.getRuleNodeComponentByClazz(ruleNode.type);
if (component) {
var node = {
id: vm.nextNodeID++,
ruleNodeId: ruleNode.id,
additionalInfo: ruleNode.additionalInfo,
configuration: ruleNode.configuration,
debugMode: ruleNode.debugMode,
x: ruleNode.additionalInfo.layoutX,
y: ruleNode.additionalInfo.layoutY,
type: ruleNode.type,
component: component,
name: ruleNode.name,
nodeType: nodeType,
nodeClass: vm.types.ruleNodeType[nodeType].nodeClass,
icon: vm.types.ruleNodeType[nodeType].icon,
nodeClass: vm.types.ruleNodeType[component.type].nodeClass,
icon: vm.types.ruleNodeType[component.type].icon,
connectors: [
{
type: flowchartConstants.leftConnectorType,
@ -347,7 +347,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
if (vm.ruleChainMetaData.connections) {
for (i = 0; i < vm.ruleChainMetaData.connections.length; i++) {
var connection = vm.ruleChainMetaData.connections[0];
var connection = vm.ruleChainMetaData.connections[i];
var sourceNode = nodes[connection.fromIndex];
destNode = nodes[connection.toIndex];
if (sourceNode && destNode) {
@ -379,9 +379,8 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
targetRuleChainId: ruleChainConnection.targetRuleChainId.id,
x: ruleChainConnection.additionalInfo.layoutX,
y: ruleChainConnection.additionalInfo.layoutY,
type: 'Rule chain',
component: types.ruleChainNodeComponent,
name: ruleChain.name,
nodeType: vm.types.ruleNodeType.RULE_CHAIN.value,
nodeClass: vm.types.ruleNodeType.RULE_CHAIN.nodeClass,
icon: vm.types.ruleNodeType.RULE_CHAIN.icon,
connectors: [
@ -410,7 +409,9 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
}
}
if (vm.canvasControl.adjustCanvasSize) {
vm.canvasControl.adjustCanvasSize();
}
vm.isDirty = false;
@ -437,15 +438,16 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
for (var i=0;i<vm.ruleChainModel.nodes.length;i++) {
var node = vm.ruleChainModel.nodes[i];
if (node.nodeType != types.ruleNodeType.INPUT.value && node.nodeType != types.ruleNodeType.RULE_CHAIN.value) {
if (node.component.type != types.ruleNodeType.INPUT.value && node.component.type != types.ruleNodeType.RULE_CHAIN.value) {
var ruleNode = {};
if (node.ruleNodeId) {
ruleNode.id = node.ruleNodeId;
}
ruleNode.type = node.type;
ruleNode.type = node.component.clazz;
ruleNode.name = node.name;
ruleNode.configuration = node.configuration;
ruleNode.additionalInfo = node.additionalInfo;
ruleNode.debugMode = node.debugMode;
if (!ruleNode.additionalInfo) {
ruleNode.additionalInfo = {};
}
@ -465,9 +467,9 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
var edge = vm.ruleChainModel.edges[i];
var sourceNode = vm.modelservice.nodes.getNodeByConnectorId(edge.source);
var destNode = vm.modelservice.nodes.getNodeByConnectorId(edge.destination);
if (sourceNode.nodeType != types.ruleNodeType.INPUT.value) {
if (sourceNode.component.type != types.ruleNodeType.INPUT.value) {
var fromIndex = nodes.indexOf(sourceNode);
if (destNode.nodeType == types.ruleNodeType.RULE_CHAIN.value) {
if (destNode.component.type == types.ruleNodeType.RULE_CHAIN.value) {
var ruleChainConnection = {
fromIndex: fromIndex,
targetRuleChainId: {entityType: vm.types.entityType.rulechain, id: destNode.targetRuleChainId},
@ -522,7 +524,7 @@ export function RuleChainController($stateParams, $scope, $q, $mdUtil, $mdExpans
type: flowchartConstants.leftConnectorType
}
);
if (ruleNode.nodeType != types.ruleNodeType.RULE_CHAIN.value) {
if (ruleNode.component.type != types.ruleNodeType.RULE_CHAIN.value) {
ruleNode.connectors.push(
{
id: vm.nextConnectorID++,

View File

@ -21,7 +21,7 @@
<section class="tb-rulechain-container" flex layout="column">
<div class="tb-rulechain-layout" flex layout="row">
<div class="tb-rulechain-library">
<md-expansion-panel-group class="tb-rulechain-library-panel-group" md-component-id="libraryPanelGroup" auto-expand="true" multiple>
<md-expansion-panel-group ng-if="vm.ruleChainLibraryLoaded" class="tb-rulechain-library-panel-group" md-component-id="libraryPanelGroup" auto-expand="true" multiple>
<md-expansion-panel md-component-id="{{typeId}}" id="{{typeId}}" ng-repeat="(typeId, typeModel) in vm.ruleNodeTypesModel">
<md-expansion-panel-collapsed>
<div class="tb-panel-title" translate>{{vm.types.ruleNodeType[typeId].name}}</div>

View File

@ -23,9 +23,9 @@
<fieldset ng-disabled="$root.loading || !isEdit || isReadOnly">
<md-input-container class="md-block">
<label translate>rulenode.type</label>
<input readonly name="type" ng-model="ruleNode.type">
<input readonly name="type" ng-model="ruleNode.component.name">
</md-input-container>
<section ng-if="ruleNode.nodeType != types.ruleNodeType.RULE_CHAIN.value">
<section ng-if="ruleNode.component.type != types.ruleNodeType.RULE_CHAIN.value">
<md-input-container class="md-block">
<label translate>rulenode.name</label>
<input required name="name" ng-model="ruleNode.name">
@ -33,12 +33,17 @@
<div translate ng-message="required">rulenode.name-required</div>
</div>
</md-input-container>
<md-input-container class="md-block">
<md-checkbox ng-disabled="$root.loading || !isEdit" aria-label="{{ 'rulenode.debug-mode' | translate }}"
ng-model="ruleNode.debugMode">{{ 'rulenode.debug-mode' | translate }}
</md-checkbox>
</md-input-container>
<md-input-container class="md-block">
<label translate>rulenode.description</label>
<textarea ng-model="ruleNode.additionalInfo.description" rows="2"></textarea>
</md-input-container>
</section>
<section ng-if="ruleNode.nodeType == types.ruleNodeType.RULE_CHAIN.value">
<section ng-if="ruleNode.component.type == types.ruleNodeType.RULE_CHAIN.value">
<tb-entity-autocomplete the-form="theForm"
ng-disabled="$root.loading || !isEdit || isReadOnly"
tb-required="true"

View File

@ -33,7 +33,7 @@ export default function RuleNodeDirective($compile, $templateCache, ruleChainSer
};
scope.$watch('ruleNode', function() {
if (scope.ruleNode && scope.ruleNode.nodeType == types.ruleNodeType.RULE_CHAIN.value) {
if (scope.ruleNode && scope.ruleNode.component.type == types.ruleNodeType.RULE_CHAIN.value) {
scope.params.targetRuleChainId = scope.ruleNode.targetRuleChainId;
watchTargetRuleChain();
} else {

View File

@ -23,7 +23,7 @@
<md-icon aria-label="node-type-icon" flex="15"
class="material-icons">{{node.icon}}</md-icon>
<div layout="column" flex="85" layout-align="center">
<span class="tb-node-type">{{ node.type }}</span>
<span class="tb-node-type">{{ node.component.name }}</span>
<span class="tb-node-title" ng-if="node.name">{{ node.name }}</span>
</div>
<div class="{{flowchartConstants.leftConnectorClass}}">