Browse Source

Nested rule chains

pull/5696/head
Andrii Shvaika 5 years ago
parent
commit
c3fa9b9f43
  1. 69
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  2. 157
      application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java
  3. 19
      application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java
  4. 5
      common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
  5. 1
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java
  6. 45
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainOutputLabelsUsage.java
  7. 44
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainUpdateResult.java
  8. 32
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNodeUpdateResult.java
  9. 13
      common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgProcessingCtx.java
  10. 9
      common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgProcessingStackItem.java
  11. 28
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java
  12. 3
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java
  13. 4
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java
  14. 7
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java
  15. 9
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java
  16. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java
  17. 4
      dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java

69
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -5,7 +5,7 @@
* 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
* 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,
@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@ -37,12 +36,16 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
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.flow.TbRuleChainOutputNode;
import org.thingsboard.rule.engine.flow.TbRuleChainOutputNodeConfiguration;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
@ -60,14 +63,18 @@ import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainData;
import org.thingsboard.server.common.data.rule.RuleChainImportResult;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleChainUpdateResult;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
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.service.install.InstallScripts;
import org.thingsboard.server.service.rule.TbRuleChainService;
import org.thingsboard.server.service.script.JsInvokeService;
import org.thingsboard.server.service.script.RuleNodeJsScriptEngine;
import org.thingsboard.server.service.security.permission.Operation;
@ -77,6 +84,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@ -137,6 +145,9 @@ public class RuleChainController extends BaseController {
+ MARKDOWN_CODE_BLOCK_END
+ "\n\n Expected result JSON contains \"output\" and \"error\".";
@Autowired
protected TbRuleChainService tbRuleChainService;
@Autowired
private InstallScripts installScripts;
@ -169,6 +180,44 @@ public class RuleChainController extends BaseController {
}
}
@ApiOperation(value = "Get Rule Chain output labels (getRuleChainOutputLabels)",
notes = "Fetch the unique labels for the \"output\" Rule Nodes that belong to the Rule Chain based on the provided Rule Chain Id. "
+ RULE_CHAIN_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/output/labels", method = RequestMethod.GET)
@ResponseBody
public Set<String> getRuleChainOutputLabels(
@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
checkRuleChain(ruleChainId, Operation.READ);
return tbRuleChainService.getRuleChainOutputLabels(getTenantId(), ruleChainId);
} catch (Exception e) {
throw handleException(e);
}
}
@ApiOperation(value = "Get output labels usage (getRuleChainOutputLabelsUsage)",
notes = "Fetch the list of rule chains and the relation types (labels) they use to process output of the current rule chain based on the provided Rule Chain Id. "
+ RULE_CHAIN_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/output/labels/usage", method = RequestMethod.GET)
@ResponseBody
public List<RuleChainOutputLabelsUsage> getRuleChainOutputLabelsUsage(
@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
try {
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
checkRuleChain(ruleChainId, Operation.READ);
return tbRuleChainService.getOutputLabelUsage(getCurrentUser().getTenantId(), ruleChainId);
} catch (Exception e) {
throw handleException(e);
}
}
@ApiOperation(value = "Get Rule Chain (getRuleChainById)",
notes = "Fetch the Rule Chain Metadata object based on the provided Rule Chain Id. " + RULE_CHAIN_METADATA_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@ -310,7 +359,11 @@ public class RuleChainController extends BaseController {
@ResponseBody
public RuleChainMetaData saveRuleChainMetaData(
@ApiParam(value = "A JSON value representing the rule chain metadata.")
@RequestBody RuleChainMetaData ruleChainMetaData) throws ThingsboardException {
@RequestBody RuleChainMetaData ruleChainMetaData,
@ApiParam(value = "Update related rule nodes.")
//TODO: change default value to "false" before merge
@RequestParam(value = "updateRelated", required = false, defaultValue = "true") boolean updateRelated
) throws ThingsboardException {
try {
TenantId tenantId = getTenantId();
if (debugPerTenantEnabled) {
@ -322,7 +375,13 @@ public class RuleChainController extends BaseController {
}
RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId(), Operation.WRITE);
checkNotNull(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData) ? true : null);
RuleChainUpdateResult result = ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData);
checkNotNull(result.isSuccess() ? true : null);
if (updateRelated && result.isSuccess()) {
tbRuleChainService.updateRelatedRuleChains(tenantId, ruleChainMetaData.getRuleChainId(), result);
}
RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId()));
if (RuleChainType.CORE.equals(ruleChain.getType())) {
@ -681,7 +740,7 @@ public class RuleChainController extends BaseController {
}
@ApiOperation(value = "Get Edge Rule Chains (getEdgeRuleChains)",
notes = "Returns a page of Rule Chains assigned to the specified edge. " + RULE_CHAIN_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
notes = "Returns a page of Rule Chains assigned to the specified edge. " + RULE_CHAIN_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody

157
application/src/main/java/org/thingsboard/server/service/rule/DefaultTbRuleChainService.java

@ -0,0 +1,157 @@
package org.thingsboard.server.service.rule;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNode;
import org.thingsboard.rule.engine.flow.TbRuleChainInputNodeConfiguration;
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.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
import org.thingsboard.server.common.data.rule.RuleChainUpdateResult;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.rule.RuleNodeUpdateResult;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@Service
@TbCoreComponent
@Slf4j
public class DefaultTbRuleChainService implements TbRuleChainService {
private final RuleChainService ruleChainService;
@Override
public Set<String> getRuleChainOutputLabels(TenantId tenantId, RuleChainId ruleChainId) {
RuleChainMetaData metaData = ruleChainService.loadRuleChainMetaData(tenantId, ruleChainId);
Set<String> outputLabels = new TreeSet<>();
for (RuleNode ruleNode : metaData.getNodes()) {
if (isOutputRuleNode(ruleNode)) {
try {
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;
}
@Override
public List<RuleChainOutputLabelsUsage> getOutputLabelUsage(TenantId tenantId, RuleChainId ruleChainId) {
List<RuleNode> ruleNodes = ruleChainService.findRuleNodesByTenantIdAndType(tenantId, TbRuleChainInputNode.class.getName(), ruleChainId.getId().toString());
Map<RuleChainId, String> ruleChainNamesCache = new HashMap<>();
// Additional filter, "just in case" the structure of the JSON configuration will change.
var filteredRuleNodes = ruleNodes.stream().filter(node -> {
try {
TbRuleChainInputNodeConfiguration configuration = JacksonUtil.treeToValue(node.getConfiguration(), TbRuleChainInputNodeConfiguration.class);
return ruleChainId.getId().toString().equals(configuration.getRuleChainId());
} catch (Exception e) {
log.warn("[{}][{}] Failed to decode rule node configuration", tenantId, ruleChainId, e);
return false;
}
}).collect(Collectors.toList());
return filteredRuleNodes.stream()
.map(ruleNode -> {
RuleChainOutputLabelsUsage usage = new RuleChainOutputLabelsUsage();
usage.setRuleNodeId(ruleNode.getId());
usage.setRuleNodeName(ruleNode.getName());
usage.setRuleChainId(ruleNode.getRuleChainId());
List<EntityRelation> relations = ruleChainService.getRuleNodeRelations(tenantId, ruleNode.getId());
if (relations != null && !relations.isEmpty()) {
usage.setLabels(relations.stream().map(EntityRelation::getType).collect(Collectors.toSet()));
}
return usage;
})
.filter(usage -> usage.getLabels() != null)
.peek(usage -> {
String ruleChainName = ruleChainNamesCache.computeIfAbsent(usage.getRuleChainId(),
id -> ruleChainService.findRuleChainById(tenantId, id).getName());
usage.setRuleChainName(ruleChainName);
})
.sorted(Comparator
.comparing(RuleChainOutputLabelsUsage::getRuleChainName)
.thenComparing(RuleChainOutputLabelsUsage::getRuleNodeName))
.collect(Collectors.toList());
}
@Override
public void updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, RuleChainUpdateResult result) {
log.debug("[{}][{}] Going to update links in related rule chains", tenantId, ruleChainId);
if (result.getUpdatedRuleNodes() == null || result.getUpdatedRuleNodes().isEmpty()) {
return;
}
Set<String> oldLabels = new HashSet<>();
Set<String> newLabels = new HashSet<>();
Set<String> confusedLabels = new HashSet<>();
Map<String, String> updatedLabels = new HashMap<>();
for (RuleNodeUpdateResult update : result.getUpdatedRuleNodes()) {
var node = update.getNewRuleNode();
if (isOutputRuleNode(node)) {
try {
TbRuleChainOutputNodeConfiguration oldConf = JacksonUtil.treeToValue(update.getOldConfiguration(), TbRuleChainOutputNodeConfiguration.class);
TbRuleChainOutputNodeConfiguration newConf = JacksonUtil.treeToValue(node.getConfiguration(), TbRuleChainOutputNodeConfiguration.class);
oldLabels.add(oldConf.getLabel());
newLabels.add(newConf.getLabel());
if (!oldConf.getLabel().equals(newConf.getLabel())) {
String oldLabel = oldConf.getLabel();
String newLabel = newConf.getLabel();
if (updatedLabels.containsKey(oldLabel)) {
confusedLabels.add(oldLabel);
} else {
updatedLabels.put(oldLabel, newLabel);
}
}
} catch (Exception e) {
log.warn("[{}][{}][{}] Failed to decode rule node configuration", tenantId, ruleChainId, node.getId(), e);
}
}
}
// Remove all labels that are renamed to two or more different labels, since we don't which new label to use;
confusedLabels.forEach(updatedLabels::remove);
// Remove all labels that are renamed to different new labels;
newLabels.forEach(updatedLabels::remove);
if (!oldLabels.equals(newLabels)) {
for (upda)
}
}
private boolean isOutputRuleNode(RuleNode ruleNode) {
return isRuleNode(ruleNode, TbRuleChainOutputNode.class);
}
private boolean isInputRuleNode(RuleNode ruleNode) {
return isRuleNode(ruleNode, TbRuleChainInputNode.class);
}
private boolean isRuleNode(RuleNode ruleNode, Class<?> clazz) {
return ruleNode.getType().equals(clazz.getName());
}
}

19
application/src/main/java/org/thingsboard/server/service/rule/TbRuleChainService.java

@ -0,0 +1,19 @@
package org.thingsboard.server.service.rule;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
import org.thingsboard.server.common.data.rule.RuleChainUpdateResult;
import java.util.List;
import java.util.Set;
public interface TbRuleChainService {
Set<String> getRuleChainOutputLabels(TenantId tenantId, RuleChainId ruleChainId);
List<RuleChainOutputLabelsUsage> getOutputLabelUsage(TenantId tenantId, RuleChainId ruleChainId);
void updateRelatedRuleChains(TenantId tenantId, RuleChainId ruleChainId, RuleChainUpdateResult result);
}

5
common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java

@ -28,7 +28,9 @@ import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainData;
import org.thingsboard.server.common.data.rule.RuleChainImportResult;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleChainUpdateResult;
import org.thingsboard.server.common.data.rule.RuleNode;
import java.util.List;
@ -42,7 +44,7 @@ public interface RuleChainService {
boolean setRootRuleChain(TenantId tenantId, RuleChainId ruleChainId);
boolean saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData);
RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData);
RuleChainMetaData loadRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId);
@ -88,4 +90,5 @@ public interface RuleChainService {
PageData<RuleChain> findAutoAssignToEdgeRuleChainsByTenantId(TenantId tenantId, PageLink pageLink);
List<RuleNode> findRuleNodesByTenantIdAndType(TenantId tenantId, String name, String toString);
}

1
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainMetaData.java

@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.RuleChainId;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Created by igor on 3/13/18.

45
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainOutputLabelsUsage.java

@ -0,0 +1,45 @@
/**
* 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.server.common.data.rule;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import java.util.Set;
@ApiModel
@Data
@Slf4j
public class RuleChainOutputLabelsUsage {
@ApiModelProperty(position = 1, required = true, value = "Rule Chain Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private RuleChainId ruleChainId;
@ApiModelProperty(position = 2, required = true, value = "Rule Node Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private RuleNodeId ruleNodeId;
@ApiModelProperty(position = 3, required = true, value = "Rule Chain Name", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String ruleChainName;
@ApiModelProperty(position = 4, required = true, value = "Rule Node Name", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private String ruleNodeName;
@ApiModelProperty(position = 5, required = true, value = "Output labels", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private Set<String> labels;
}

44
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChainUpdateResult.java

@ -0,0 +1,44 @@
/**
* 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.server.common.data.rule;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.thingsboard.server.common.data.id.RuleNodeId;
import java.util.List;
import java.util.Map;
/**
* Created by igor on 3/13/18.
*/
@Data
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class RuleChainUpdateResult {
private final boolean success;
private final List<RuleNodeUpdateResult> updatedRuleNodes;
public static RuleChainUpdateResult failed(){
return new RuleChainUpdateResult(false, null);
}
public static RuleChainUpdateResult successful(List<RuleNodeUpdateResult> updatedRuleNodes){
return new RuleChainUpdateResult(true, updatedRuleNodes);
}
}

32
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNodeUpdateResult.java

@ -0,0 +1,32 @@
/**
* 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.server.common.data.rule;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import org.thingsboard.server.common.data.id.RuleNodeId;
import java.util.Map;
/**
* Created by igor on 3/13/18.
*/
@Data
public class RuleNodeUpdateResult {
private final JsonNode oldConfiguration;
private final RuleNode newRuleNode;
}

13
common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgProcessingCtx.java

@ -5,7 +5,7 @@
* 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
* 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,
@ -51,7 +51,11 @@ public final class TbMsgProcessingCtx implements Serializable {
}
public TbMsgProcessingCtx copy() {
return new TbMsgProcessingCtx(ruleNodeExecCounter.get());
if (stack == null || stack.isEmpty()) {
return new TbMsgProcessingCtx(ruleNodeExecCounter.get());
} else {
return new TbMsgProcessingCtx(ruleNodeExecCounter.get(), new LinkedList<>(stack));
}
}
public void push(RuleChainId ruleChainId, RuleNodeId ruleNodeId) {
@ -70,10 +74,7 @@ public final class TbMsgProcessingCtx implements Serializable {
if (ctx.getStackCount() > 0) {
LinkedList<TbMsgProcessingStackItem> stack = new LinkedList<>();
for (MsgProtos.TbMsgProcessingStackItemProto item : ctx.getStackList()) {
stack.add(new TbMsgProcessingStackItem(
new RuleChainId(new UUID(item.getRuleChainIdMSB(), item.getRuleChainIdLSB())),
new RuleNodeId(new UUID(item.getRuleNodeIdMSB(), item.getRuleNodeIdLSB()))
));
stack.add(TbMsgProcessingStackItem.fromProto(item));
}
return new TbMsgProcessingCtx(ruleNodeExecCounter, stack);
} else {

9
common/message/src/main/java/org/thingsboard/server/common/msg/TbMsgProcessingStackItem.java

@ -20,6 +20,8 @@ import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.msg.gen.MsgProtos;
import java.util.UUID;
@Data
public class TbMsgProcessingStackItem {
@ -35,4 +37,11 @@ public class TbMsgProcessingStackItem {
.build();
}
static TbMsgProcessingStackItem fromProto(MsgProtos.TbMsgProcessingStackItemProto item){
return new TbMsgProcessingStackItem(
new RuleChainId(new UUID(item.getRuleChainIdMSB(), item.getRuleChainIdLSB())),
new RuleNodeId(new UUID(item.getRuleNodeIdMSB(), item.getRuleNodeIdLSB()))
);
}
}

28
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java

@ -5,7 +5,7 @@
* 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
* 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,
@ -47,8 +47,11 @@ import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChainData;
import org.thingsboard.server.common.data.rule.RuleChainImportResult;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.common.data.rule.RuleChainUpdateResult;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.common.data.rule.RuleNodeUpdateResult;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
@ -72,6 +75,7 @@ import java.util.stream.Collectors;
import static org.thingsboard.server.common.data.DataConstants.TENANT;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateString;
/**
* Created by igor on 3/12/18.
@ -130,13 +134,14 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
@Override
@Transactional
public boolean saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData) {
public RuleChainUpdateResult saveRuleChainMetaData(TenantId tenantId, RuleChainMetaData ruleChainMetaData) {
Validator.validateId(ruleChainMetaData.getRuleChainId(), "Incorrect rule chain id.");
RuleChain ruleChain = findRuleChainById(tenantId, ruleChainMetaData.getRuleChainId());
if (ruleChain == null) {
return false;
return RuleChainUpdateResult.failed();
}
ConstraintValidator.validateFields(ruleChainMetaData);
List<RuleNodeUpdateResult> updatedRuleNodes = new ArrayList<>();
if (CollectionUtils.isNotEmpty(ruleChainMetaData.getConnections())) {
validateCircles(ruleChainMetaData.getConnections());
@ -161,11 +166,15 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
for (RuleNode existingNode : existingRuleNodes) {
deleteEntityRelations(tenantId, existingNode.getId());
Integer index = ruleNodeIndexMap.get(existingNode.getId());
RuleNode newRuleNode = null;
if (index != null) {
toAddOrUpdate.add(ruleChainMetaData.getNodes().get(index));
newRuleNode = ruleChainMetaData.getNodes().get(index);
toAddOrUpdate.add(newRuleNode);
} else {
updatedRuleNodes.add(new RuleNodeUpdateResult(existingNode.getConfiguration(), null));
toDelete.add(existingNode);
}
updatedRuleNodes.add(new RuleNodeUpdateResult(existingNode.getConfiguration(), newRuleNode));
}
if (nodes != null) {
for (RuleNode node : toAddOrUpdate) {
@ -209,7 +218,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
}
}
return true;
return RuleChainUpdateResult.successful(updatedRuleNodes);
}
private void validateCircles(List<NodeConnectionInfo> connectionInfos) {
@ -652,6 +661,15 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
return ruleChainDao.findAutoAssignToEdgeRuleChainsByTenantId(tenantId.getId(), pageLink);
}
@Override
public List<RuleNode> findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search) {
log.trace("Executing findRuleNodes, tenantId [{}], type {}, search {}", tenantId, type, search);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validateString(type, "Incorrect type of the rule node");
validateString(search, "Incorrect search text");
return ruleNodeDao.findRuleNodesByTenantIdAndType(tenantId, type, search);
}
private void checkRuleNodesAndDelete(TenantId tenantId, RuleChainId ruleChainId) {
try {
ruleChainDao.removeById(tenantId, ruleChainId.getId());

3
dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java

@ -19,11 +19,13 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.TenantEntityDao;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
/**
@ -79,4 +81,5 @@ public interface RuleChainDao extends Dao<RuleChain>, TenantEntityDao {
Collection<RuleChain> findByTenantIdAndTypeAndName(TenantId tenantId, RuleChainType type, String name);
List<RuleChainOutputLabelsUsage> getOutputLabelUsage(UUID id, UUID id1);
}

4
dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java

@ -15,12 +15,16 @@
*/
package org.thingsboard.server.dao.rule;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.Dao;
import java.util.List;
/**
* Created by igor on 3/12/18.
*/
public interface RuleNodeDao extends Dao<RuleNode> {
List<RuleNode> findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search);
}

7
dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java

@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage;
import org.thingsboard.server.common.data.rule.RuleChainType;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.RuleChainEntity;
@ -30,6 +31,7 @@ import org.thingsboard.server.dao.rule.RuleChainDao;
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
@ -103,6 +105,11 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, R
return DaoUtil.convertDataList(ruleChainRepository.findByTenantIdAndTypeAndName(tenantId.getId(), type, name));
}
@Override
public List<RuleChainOutputLabelsUsage> getOutputLabelUsage(UUID tenantId, UUID ruleChainId) {
return null;
}
@Override
public Long countByTenantId(TenantId tenantId) {
return ruleChainRepository.countByTenantId(tenantId.getId());

9
dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java

@ -5,7 +5,7 @@
* 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
* 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,
@ -19,11 +19,14 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.RuleNodeEntity;
import org.thingsboard.server.dao.rule.RuleNodeDao;
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
import java.util.List;
import java.util.UUID;
@Slf4j
@ -43,4 +46,8 @@ public class JpaRuleNodeDao extends JpaAbstractSearchTextDao<RuleNodeEntity, Rul
return ruleNodeRepository;
}
@Override
public List<RuleNode> findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search) {
return DaoUtil.convertDataList(ruleNodeRepository.findRuleNodesByTenantIdAndType(tenantId.getId(), type, search));
}
}

10
dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java

@ -15,11 +15,21 @@
*/
package org.thingsboard.server.dao.sql.rule;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.thingsboard.server.dao.model.sql.RuleNodeEntity;
import java.util.List;
import java.util.UUID;
public interface RuleNodeRepository extends CrudRepository<RuleNodeEntity, UUID> {
@Query("SELECT r FROM RuleNodeEntity r WHERE r.ruleChainId in " +
"(select id from RuleChainEntity rc WHERE rc.tenantId = :tenantId) " +
"AND r.type = :ruleType AND LOWER(r.configuration) LIKE LOWER(CONCAT('%', :searchText, '%')) ")
List<RuleNodeEntity> findRuleNodesByTenantIdAndType(@Param("tenantId") UUID tenantId,
@Param("ruleType") String ruleType,
@Param("searchText") String searchText);
}

4
dao/src/test/java/org/thingsboard/server/dao/service/BaseRuleChainServiceTest.java

@ -293,7 +293,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest {
ruleNodes.set(name3Index, ruleNode4);
Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, savedRuleChainMetaData));
Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, savedRuleChainMetaData).isSuccess());
RuleChainMetaData updatedRuleChainMetaData = ruleChainService.loadRuleChainMetaData(tenantId, savedRuleChainMetaData.getRuleChainId());
Assert.assertEquals(3, updatedRuleChainMetaData.getNodes().size());
@ -406,7 +406,7 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest {
ruleChainMetaData.addConnectionInfo(0,2,"fail");
ruleChainMetaData.addConnectionInfo(1,2,"success");
Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData));
Assert.assertTrue(ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData).isSuccess());
return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId());
}

Loading…
Cancel
Save