Use output rule node name instead of label

This commit is contained in:
Igor Kulikov 2021-12-08 16:23:54 +02:00
parent 65fb622b21
commit b5da8752c5
7 changed files with 21 additions and 74 deletions

View File

@ -36,10 +36,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.ScriptEngine; import org.thingsboard.rule.engine.api.ScriptEngine;
import org.thingsboard.rule.engine.flow.TbRuleChainOutputNode;
import org.thingsboard.rule.engine.flow.TbRuleChainOutputNodeConfiguration;
import org.thingsboard.server.actors.ActorSystemContext; import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.tenant.DebugTbRateLimits; import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
@ -71,7 +68,6 @@ import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.install.InstallScripts; import org.thingsboard.server.service.install.InstallScripts;
import org.thingsboard.server.service.rule.TbRuleChainService; import org.thingsboard.server.service.rule.TbRuleChainService;

View File

@ -23,7 +23,6 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNode; import org.thingsboard.rule.engine.flow.TbRuleChainInputNode;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration; import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
import org.thingsboard.rule.engine.flow.TbRuleChainOutputNode; import org.thingsboard.rule.engine.flow.TbRuleChainOutputNode;
import org.thingsboard.rule.engine.flow.TbRuleChainOutputNodeConfiguration;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
@ -65,14 +64,7 @@ public class DefaultTbRuleChainService implements TbRuleChainService {
Set<String> outputLabels = new TreeSet<>(); Set<String> outputLabels = new TreeSet<>();
for (RuleNode ruleNode : metaData.getNodes()) { for (RuleNode ruleNode : metaData.getNodes()) {
if (isOutputRuleNode(ruleNode)) { if (isOutputRuleNode(ruleNode)) {
try { outputLabels.add(ruleNode.getName());
var configuration = JacksonUtil.treeToValue(ruleNode.getConfiguration(), TbRuleChainOutputNodeConfiguration.class);
if (StringUtils.isNotEmpty(configuration.getLabel())) {
outputLabels.add(configuration.getLabel());
}
} catch (Exception e) {
log.warn("[{}][{}] Failed to decode rule node configuration", tenantId, ruleChainId, e);
}
} }
} }
return outputLabels; return outputLabels;
@ -130,16 +122,15 @@ public class DefaultTbRuleChainService implements TbRuleChainService {
Set<String> confusedLabels = new HashSet<>(); Set<String> confusedLabels = new HashSet<>();
Map<String, String> updatedLabels = new HashMap<>(); Map<String, String> updatedLabels = new HashMap<>();
for (RuleNodeUpdateResult update : result.getUpdatedRuleNodes()) { for (RuleNodeUpdateResult update : result.getUpdatedRuleNodes()) {
var node = update.getNewRuleNode(); var oldNode = update.getOldRuleNode();
if (isOutputRuleNode(node)) { var newNode = update.getNewRuleNode();
if (isOutputRuleNode(newNode)) {
try { try {
TbRuleChainOutputNodeConfiguration oldConf = JacksonUtil.treeToValue(update.getOldConfiguration(), TbRuleChainOutputNodeConfiguration.class); oldLabels.add(oldNode.getName());
TbRuleChainOutputNodeConfiguration newConf = JacksonUtil.treeToValue(node.getConfiguration(), TbRuleChainOutputNodeConfiguration.class); newLabels.add(newNode.getName());
oldLabels.add(oldConf.getLabel()); if (!oldNode.getName().equals(newNode.getName())) {
newLabels.add(newConf.getLabel()); String oldLabel = oldNode.getName();
if (!oldConf.getLabel().equals(newConf.getLabel())) { String newLabel = newNode.getName();
String oldLabel = oldConf.getLabel();
String newLabel = newConf.getLabel();
if (updatedLabels.containsKey(oldLabel) && !updatedLabels.get(oldLabel).equals(newLabel)) { if (updatedLabels.containsKey(oldLabel) && !updatedLabels.get(oldLabel).equals(newLabel)) {
confusedLabels.add(oldLabel); confusedLabels.add(oldLabel);
log.warn("[{}][{}] Can't automatically rename the label from [{}] to [{}] due to conflict [{}]", tenantId, ruleChainId, oldLabel, newLabel, updatedLabels.get(oldLabel)); log.warn("[{}][{}] Can't automatically rename the label from [{}] to [{}] due to conflict [{}]", tenantId, ruleChainId, oldLabel, newLabel, updatedLabels.get(oldLabel));
@ -149,7 +140,7 @@ public class DefaultTbRuleChainService implements TbRuleChainService {
} }
} catch (Exception e) { } catch (Exception e) {
log.warn("[{}][{}][{}] Failed to decode rule node configuration", tenantId, ruleChainId, node.getId(), e); log.warn("[{}][{}][{}] Failed to decode rule node configuration", tenantId, ruleChainId, newNode.getId(), e);
} }
} }
} }

View File

@ -27,6 +27,7 @@ import java.util.Map;
@Data @Data
public class RuleNodeUpdateResult { public class RuleNodeUpdateResult {
private final JsonNode oldConfiguration; private final RuleNode oldRuleNode;
private final RuleNode newRuleNode; private final RuleNode newRuleNode;
} }

View File

@ -172,10 +172,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
newRuleNode = ruleChainMetaData.getNodes().get(index); newRuleNode = ruleChainMetaData.getNodes().get(index);
toAddOrUpdate.add(newRuleNode); toAddOrUpdate.add(newRuleNode);
} else { } else {
updatedRuleNodes.add(new RuleNodeUpdateResult(existingNode.getConfiguration(), null)); updatedRuleNodes.add(new RuleNodeUpdateResult(existingNode, null));
toDelete.add(existingNode); toDelete.add(existingNode);
} }
updatedRuleNodes.add(new RuleNodeUpdateResult(existingNode.getConfiguration(), newRuleNode)); updatedRuleNodes.add(new RuleNodeUpdateResult(existingNode, newRuleNode));
} }
if (nodes != null) { if (nodes != null) {
for (RuleNode node : toAddOrUpdate) { for (RuleNode node : toAddOrUpdate) {

View File

@ -16,6 +16,7 @@
package org.thingsboard.rule.engine.flow; package org.thingsboard.rule.engine.flow;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
import org.thingsboard.rule.engine.api.RuleNode; import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNode;
@ -30,28 +31,24 @@ import org.thingsboard.server.common.msg.TbMsg;
@RuleNode( @RuleNode(
type = ComponentType.FLOW, type = ComponentType.FLOW,
name = "output", name = "output",
configClazz = TbRuleChainOutputNodeConfiguration.class, configClazz = EmptyNodeConfiguration.class,
nodeDescription = "transfers the message to the caller rule chain", nodeDescription = "transfers the message to the caller rule chain",
nodeDetails = "Produces output of the rule chain processing. " + nodeDetails = "Produces output of the rule chain processing. " +
"The output is forwarded to the caller rule chain, as an output of the corresponding \"input\" rule node. " + "The output is forwarded to the caller rule chain, as an output of the corresponding \"input\" rule node. " +
"The rule node configuration contains the \"label\" parameter. " + "The output rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain. ",
"This parameter corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain. ",
uiResources = {"static/rulenode/rulenode-core-config.js"}, uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFlowNodeRuleChainOutputConfig", configDirective = "tbFlowNodeRuleChainOutputConfig",
outEnabled = false outEnabled = false
) )
public class TbRuleChainOutputNode implements TbNode { public class TbRuleChainOutputNode implements TbNode {
private TbRuleChainOutputNodeConfiguration config;
@Override @Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbRuleChainOutputNodeConfiguration.class);
} }
@Override @Override
public void onMsg(TbContext ctx, TbMsg msg) { public void onMsg(TbContext ctx, TbMsg msg) {
ctx.output(msg, config.getLabel()); ctx.output(msg, ctx.getSelf().getName());
} }
@Override @Override

View File

@ -1,35 +0,0 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.rule.engine.flow;
import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.rule.engine.api.TbRelationTypes;
import org.thingsboard.server.common.data.DataConstants;
@Data
public class TbRuleChainOutputNodeConfiguration implements NodeConfiguration<TbRuleChainOutputNodeConfiguration> {
private String label;
@Override
public TbRuleChainOutputNodeConfiguration defaultConfiguration() {
var result = new TbRuleChainOutputNodeConfiguration();
result.setLabel(TbRelationTypes.SUCCESS);
return result;
}
}

View File

@ -3728,13 +3728,11 @@ class RuleChainOutputComponent extends RuleNodeConfigurationComponent {
return this.ruleChainOutputConfigForm; return this.ruleChainOutputConfigForm;
} }
onConfigurationSet(configuration) { onConfigurationSet(configuration) {
this.ruleChainOutputConfigForm = this.fb.group({ this.ruleChainOutputConfigForm = this.fb.group({});
label: [configuration ? configuration.label : null, [Validators.required]]
});
} }
} }
RuleChainOutputComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.14", ngImport: i0, type: RuleChainOutputComponent, deps: [{ token: i1.Store }, { token: i2.FormBuilder }], target: i0.ɵɵFactoryTarget.Component }); RuleChainOutputComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.14", ngImport: i0, type: RuleChainOutputComponent, deps: [{ token: i1.Store }, { token: i2.FormBuilder }], target: i0.ɵɵFactoryTarget.Component });
RuleChainOutputComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.14", type: RuleChainOutputComponent, selector: "tb-flow-node-rule-chain-output-config", usesInheritance: true, ngImport: i0, template: "<section [formGroup]=\"ruleChainOutputConfigForm\" fxLayout=\"column\">\n <mat-form-field class=\"mat-block\">\n <mat-label translate>tb.rulenode.label</mat-label>\n <input required matInput formControlName=\"label\">\n <mat-error *ngIf=\"ruleChainOutputConfigForm.get('label').hasError('required')\">\n {{ 'tb.rulenode.label-required' | translate }}\n </mat-error>\n </mat-form-field>\n</section>\n", components: [{ type: i3.MatFormField, selector: "mat-form-field", inputs: ["color", "floatLabel", "appearance", "hideRequiredMarker", "hintLabel"], exportAs: ["matFormField"] }], directives: [{ type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i3.MatLabel, selector: "mat-label" }, { type: i4.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { type: i11.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["id", "disabled", "required", "type", "value", "readonly", "placeholder", "errorStateMatcher", "aria-describedby"], exportAs: ["matInput"] }, { type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i2.FormControlName, selector: "[formControlName]", inputs: ["disabled", "formControlName", "ngModel"], outputs: ["ngModelChange"] }, { type: i10.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i3.MatError, selector: "mat-error", inputs: ["id"] }], pipes: { "translate": i4.TranslatePipe } }); RuleChainOutputComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.14", type: RuleChainOutputComponent, selector: "tb-flow-node-rule-chain-output-config", usesInheritance: true, ngImport: i0, template: "<section [formGroup]=\"ruleChainOutputConfigForm\" fxLayout=\"column\">\n <div innerHTML=\"{{ 'tb.rulenode.output-node-name-hint' | translate }}\"></div>\n</section>\n", directives: [{ type: i8.DefaultLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg]", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], pipes: { "translate": i4.TranslatePipe } });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.14", ngImport: i0, type: RuleChainOutputComponent, decorators: [{ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.14", ngImport: i0, type: RuleChainOutputComponent, decorators: [{
type: Component, type: Component,
args: [{ args: [{
@ -4191,8 +4189,7 @@ function addRuleNodeCoreLocaleEnglish(translate) {
'alarm-severity-pattern-hint': 'Hint: use <code><span style="color: #000;">$&#123;</span>metadataKey<span style="color: #000;">&#125;</span></code> ' + 'alarm-severity-pattern-hint': 'Hint: use <code><span style="color: #000;">$&#123;</span>metadataKey<span style="color: #000;">&#125;</span></code> ' +
'for value from metadata, <code><span style="color: #000;">$[</span>messageKey<span style="color: #000;">]</span></code> ' + 'for value from metadata, <code><span style="color: #000;">$[</span>messageKey<span style="color: #000;">]</span></code> ' +
'for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)', 'for value from message body. Alarm severity should be system (CRITICAL, MAJOR etc.)',
label: 'Label', 'output-node-name-hint': 'The <b>rule node name</b> corresponds to the <b>relation type</b> of the output message, and it is used to forward messages to other rule nodes in the caller rule chain.'
'label-required': 'Label is required'
}, },
'key-val': { 'key-val': {
key: 'Key', key: 'Key',