|
|
|
@ -16,34 +16,34 @@ |
|
|
|
package org.thingsboard.server.service.edge; |
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.junit.Assert; |
|
|
|
import org.junit.jupiter.api.Assertions; |
|
|
|
import org.junit.jupiter.api.BeforeAll; |
|
|
|
import org.junit.jupiter.api.DisplayName; |
|
|
|
import org.junit.jupiter.api.TestInstance; |
|
|
|
import org.junit.jupiter.params.ParameterizedTest; |
|
|
|
import org.junit.jupiter.params.provider.EnumSource; |
|
|
|
import org.junit.jupiter.params.provider.MethodSource; |
|
|
|
import org.thingsboard.common.util.JacksonUtil; |
|
|
|
import org.thingsboard.rule.engine.action.TbSaveToCustomCassandraTableNode; |
|
|
|
import org.thingsboard.rule.engine.action.TbSaveToCustomCassandraTableNodeConfiguration; |
|
|
|
import org.thingsboard.rule.engine.api.NodeConfiguration; |
|
|
|
import org.thingsboard.rule.engine.api.TbNode; |
|
|
|
import org.thingsboard.rule.engine.aws.lambda.TbAwsLambdaNode; |
|
|
|
import org.thingsboard.rule.engine.aws.lambda.TbAwsLambdaNodeConfiguration; |
|
|
|
import org.thingsboard.rule.engine.filter.TbCheckRelationNode; |
|
|
|
import org.thingsboard.rule.engine.flow.TbAckNode; |
|
|
|
import org.thingsboard.rule.engine.math.TbMathNode; |
|
|
|
import org.thingsboard.rule.engine.metadata.CalculateDeltaNode; |
|
|
|
import org.thingsboard.rule.engine.metadata.TbGetTelemetryNode; |
|
|
|
import org.thingsboard.rule.engine.rest.TbSendRestApiCallReplyNode; |
|
|
|
import org.thingsboard.rule.engine.rest.TbSendRestApiCallReplyNodeConfiguration; |
|
|
|
import org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode; |
|
|
|
import org.thingsboard.rule.engine.telemetry.TbMsgAttributesNodeConfiguration; |
|
|
|
import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode; |
|
|
|
import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNodeConfiguration; |
|
|
|
import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
|
|
|
import org.thingsboard.server.common.data.rule.RuleNode; |
|
|
|
import org.thingsboard.server.common.data.util.TbPair; |
|
|
|
import org.thingsboard.server.gen.edge.v1.EdgeVersion; |
|
|
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.lang.reflect.Constructor; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Optional; |
|
|
|
import java.util.stream.Stream; |
|
|
|
|
|
|
|
import static org.thingsboard.server.service.edge.EdgeMsgConstructorUtils.EXCLUDED_NODES_BY_EDGE_VERSION; |
|
|
|
import static org.thingsboard.server.service.edge.EdgeMsgConstructorUtils.IGNORED_PARAMS_BY_EDGE_VERSION; |
|
|
|
@ -51,109 +51,106 @@ import static org.thingsboard.server.service.edge.EdgeMsgConstructorUtils.IGNORE |
|
|
|
@Slf4j |
|
|
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS) |
|
|
|
public class EdgeMsgConstructorUtilsTest { |
|
|
|
private static final int CONFIGURATION_VERSION = 5; |
|
|
|
|
|
|
|
private static final Map<NodeConfiguration, String> NODE_CONFIG_TO_NAME_MAP = Map.of( |
|
|
|
new TbMsgTimeseriesNodeConfiguration(), TbMsgTimeseriesNode.class.getName(), |
|
|
|
new TbMsgAttributesNodeConfiguration(), TbMsgAttributesNode.class.getName(), |
|
|
|
new TbSaveToCustomCassandraTableNodeConfiguration(), TbSaveToCustomCassandraTableNode.class.getName() |
|
|
|
); |
|
|
|
|
|
|
|
private static final Map<String, Integer> NODE_NAME_TO_CONFIG_PARAM_COUNT_MAP = Map.of( |
|
|
|
TbMsgTimeseriesNode.class.getName(), 3, |
|
|
|
TbMsgAttributesNode.class.getName(), 5, |
|
|
|
TbSaveToCustomCassandraTableNode.class.getName(), 3 |
|
|
|
); |
|
|
|
private static final int CONFIGURATION_VERSION = 5; |
|
|
|
|
|
|
|
static Stream<EdgeVersion> provideEdgeVersions() { |
|
|
|
return Stream.of( |
|
|
|
EdgeVersion.V_4_0_0, |
|
|
|
EdgeVersion.V_3_9_0, |
|
|
|
EdgeVersion.V_3_8_0, |
|
|
|
EdgeVersion.V_3_7_0 |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
private static final Map<NodeConfiguration, String> MISSING_NODE_CONFIGS_FOR_OLD_EDGES = Map.of( |
|
|
|
new TbSendRestApiCallReplyNodeConfiguration(), TbSendRestApiCallReplyNode.class.getName(), |
|
|
|
new TbAwsLambdaNodeConfiguration(), TbAwsLambdaNode.class.getName() |
|
|
|
); |
|
|
|
private static final RuleChainMetaData RULE_CHAIN_META_DATA = new RuleChainMetaData(); |
|
|
|
private static final List<TbNode> TEST_NODES = |
|
|
|
List.of( |
|
|
|
new TbSaveToCustomCassandraTableNode(), |
|
|
|
new TbMsgAttributesNode(), |
|
|
|
new TbMsgTimeseriesNode(), |
|
|
|
new TbSendRestApiCallReplyNode(), |
|
|
|
new TbAwsLambdaNode(), |
|
|
|
|
|
|
|
new TbMathNode(), |
|
|
|
new CalculateDeltaNode(), |
|
|
|
new TbAckNode(), |
|
|
|
new TbCheckRelationNode(), |
|
|
|
new TbGetTelemetryNode() |
|
|
|
); |
|
|
|
|
|
|
|
@ParameterizedTest(name = "Testing metadata update for EdgeVersion: {0}") |
|
|
|
@EnumSource(value = EdgeVersion.class, names = {"V_4_0_0", "V_3_9_0", "V_3_8_0", "V_3_7_0"}) |
|
|
|
@DisplayName("Test RuleChain Metadata Update for Supported Edge Versions") |
|
|
|
public void testRuleChainMetadataUpdateForSupportedEdgeVersions(EdgeVersion edgeVersion) { |
|
|
|
// GIVEN
|
|
|
|
RuleChainMetaData metaData = createMetadataWithNodes(NODE_CONFIG_TO_NAME_MAP); |
|
|
|
@BeforeAll |
|
|
|
static void setUp() { |
|
|
|
List<RuleNode> ruleNodes = TEST_NODES.stream() |
|
|
|
.map(node -> { |
|
|
|
RuleNode ruleNode = new RuleNode(); |
|
|
|
ruleNode.setName(node.getClass().getName()); |
|
|
|
ruleNode.setType(node.getClass().getName()); |
|
|
|
ruleNode.setConfigurationVersion(CONFIGURATION_VERSION); |
|
|
|
ruleNode.setConfiguration(JacksonUtil.valueToTree(createDefaultConfiguration(node))); |
|
|
|
return ruleNode; |
|
|
|
}) |
|
|
|
.toList(); |
|
|
|
|
|
|
|
RULE_CHAIN_META_DATA.setFirstNodeIndex(0); |
|
|
|
RULE_CHAIN_META_DATA.setNodes(ruleNodes); |
|
|
|
} |
|
|
|
|
|
|
|
// WHEN
|
|
|
|
List<RuleNode> ruleNodes = extractRuleNodesFromMetadata(metaData, edgeVersion); |
|
|
|
private static NodeConfiguration<?> createDefaultConfiguration(TbNode node) { |
|
|
|
try { |
|
|
|
org.thingsboard.rule.engine.api.RuleNode annotation = node.getClass().getAnnotation(org.thingsboard.rule.engine.api.RuleNode.class); |
|
|
|
Constructor<?> constructor = annotation.configClazz().getConstructor(); |
|
|
|
NodeConfiguration<?> configInstance = (NodeConfiguration<?>) constructor.newInstance(); |
|
|
|
|
|
|
|
// THEN
|
|
|
|
verifyRuleNodeConfigurations(ruleNodes, edgeVersion); |
|
|
|
return configInstance.defaultConfiguration(); |
|
|
|
} catch (Exception e) { |
|
|
|
throw new RuntimeException("Exception during creating RuleNodeConfiguration for node - " + node, e); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ParameterizedTest(name = "Testing metadata with missing nodes for EdgeVersion: {0}") |
|
|
|
@EnumSource(value = EdgeVersion.class, names = {"V_4_0_0", "V_3_9_0", "V_3_8_0", "V_3_7_0"}) |
|
|
|
@DisplayName("Test RuleChain Metadata with Missing Nodes for Old Edge Versions") |
|
|
|
public void testRuleChainMetadataWithMissingNodesForOldEdgeVersions(EdgeVersion edgeVersion) { |
|
|
|
// GIVEN
|
|
|
|
RuleChainMetaData metaData = createMetadataWithNodes(MISSING_NODE_CONFIGS_FOR_OLD_EDGES); |
|
|
|
|
|
|
|
@ParameterizedTest(name = "Test Sanitize Metadata For Edge: {0}") |
|
|
|
@MethodSource("provideEdgeVersions") |
|
|
|
@DisplayName("Test Sanitize Metadata For Legacy Edge Version") |
|
|
|
public void testSanitizeMetadataForLegacyEdgeVersion(EdgeVersion edgeVersion) { |
|
|
|
// WHEN
|
|
|
|
List<RuleNode> ruleNodes = extractRuleNodesFromMetadata(metaData, edgeVersion); |
|
|
|
List<RuleNode> ruleNodes = sanitizeMetadataForLegacyEdgeVersion(edgeVersion); |
|
|
|
|
|
|
|
// THEN
|
|
|
|
int expectedNodeCount = EXCLUDED_NODES_BY_EDGE_VERSION.containsKey(edgeVersion) ? |
|
|
|
MISSING_NODE_CONFIGS_FOR_OLD_EDGES.size() - EXCLUDED_NODES_BY_EDGE_VERSION.get(edgeVersion).size() : |
|
|
|
MISSING_NODE_CONFIGS_FOR_OLD_EDGES.size(); |
|
|
|
Assertions.assertEquals( |
|
|
|
expectedNodeCount, |
|
|
|
ruleNodes.size(), |
|
|
|
String.format("EdgeVersion '%s' should have %d nodes, but found %d.", edgeVersion, expectedNodeCount, ruleNodes.size()) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
private RuleChainMetaData createMetadataWithNodes(Map<NodeConfiguration, String> nodeConfigMap) { |
|
|
|
RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); |
|
|
|
List<RuleNode> ruleNodes = new ArrayList<>(); |
|
|
|
|
|
|
|
nodeConfigMap.forEach((config, nodeName) -> { |
|
|
|
RuleNode ruleNode = new RuleNode(); |
|
|
|
ruleNode.setName(nodeName); |
|
|
|
ruleNode.setType(nodeName); |
|
|
|
ruleNode.setConfigurationVersion(CONFIGURATION_VERSION); |
|
|
|
ruleNode.setConfiguration(JacksonUtil.valueToTree(config.defaultConfiguration())); |
|
|
|
ruleNodes.add(ruleNode); |
|
|
|
ruleNodes.forEach(ruleNode -> { |
|
|
|
checkUpdateNodeConfigurationsForLegacyEdge(ruleNode, edgeVersion); |
|
|
|
checkRemoveExcludedNodesForLegacyEdge(ruleNode, edgeVersion); |
|
|
|
}); |
|
|
|
|
|
|
|
ruleChainMetaData.setFirstNodeIndex(0); |
|
|
|
ruleChainMetaData.setNodes(ruleNodes); |
|
|
|
return ruleChainMetaData; |
|
|
|
} |
|
|
|
|
|
|
|
private List<RuleNode> extractRuleNodesFromMetadata(RuleChainMetaData metaData, EdgeVersion edgeVersion) { |
|
|
|
private List<RuleNode> sanitizeMetadataForLegacyEdgeVersion(EdgeVersion edgeVersion) { |
|
|
|
String metadataUpdateMsg = EdgeMsgConstructorUtils.constructRuleChainMetadataUpdatedMsg( |
|
|
|
UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, |
|
|
|
metaData, |
|
|
|
RULE_CHAIN_META_DATA, |
|
|
|
edgeVersion |
|
|
|
).getEntity(); |
|
|
|
|
|
|
|
RuleChainMetaData updatedMetaData = JacksonUtil.fromString(metadataUpdateMsg, RuleChainMetaData.class, true); |
|
|
|
Assertions.assertNotNull(updatedMetaData, "RuleChainMetaData should not be null after update."); |
|
|
|
|
|
|
|
return updatedMetaData.getNodes(); |
|
|
|
} |
|
|
|
|
|
|
|
private void verifyRuleNodeConfigurations(List<RuleNode> ruleNodes, EdgeVersion edgeVersion) { |
|
|
|
ruleNodes.forEach(ruleNode -> { |
|
|
|
String nodeType = ruleNode.getType(); |
|
|
|
int expectedParamCount = NODE_NAME_TO_CONFIG_PARAM_COUNT_MAP.getOrDefault(nodeType, 0); |
|
|
|
private void checkUpdateNodeConfigurationsForLegacyEdge(RuleNode ruleNode, EdgeVersion edgeVersion) { |
|
|
|
if (IGNORED_PARAMS_BY_EDGE_VERSION.containsKey(edgeVersion) && IGNORED_PARAMS_BY_EDGE_VERSION.get(edgeVersion).containsKey(ruleNode.getType())) { |
|
|
|
String ignoredParam = IGNORED_PARAMS_BY_EDGE_VERSION.get(edgeVersion).get(ruleNode.getType()); |
|
|
|
|
|
|
|
boolean isRuleNodeModified = IGNORED_PARAMS_BY_EDGE_VERSION |
|
|
|
.getOrDefault(edgeVersion, Map.of()) |
|
|
|
.containsKey(nodeType); |
|
|
|
Assertions.assertFalse(ruleNode.getConfiguration().has(ignoredParam), |
|
|
|
String.format("RuleNode '%s' for EdgeVersion '%s' should ignore '%s' config parameter.", ruleNode.getName(), edgeVersion, ignoredParam)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int actualParamCount = isRuleNodeModified ? expectedParamCount - 1 : expectedParamCount; |
|
|
|
private void checkRemoveExcludedNodesForLegacyEdge(RuleNode ruleNode, EdgeVersion edgeVersion) { |
|
|
|
boolean isNodeExcluded = Optional.ofNullable(EXCLUDED_NODES_BY_EDGE_VERSION.get(edgeVersion)) |
|
|
|
.map(excludedNodes -> !excludedNodes.contains(ruleNode.getType())) |
|
|
|
.orElse(true); |
|
|
|
|
|
|
|
Assertions.assertEquals( |
|
|
|
actualParamCount, |
|
|
|
ruleNode.getConfiguration().size(), |
|
|
|
String.format("RuleNode '%s' for EdgeVersion '%s' should have %d config parameters.", |
|
|
|
ruleNode.getName(), edgeVersion, actualParamCount) |
|
|
|
); |
|
|
|
}); |
|
|
|
Assertions.assertTrue(isNodeExcluded, |
|
|
|
String.format("For EdgeVersion '%s', ruleNode '%s' should not be included.", edgeVersion, ruleNode.getType())); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|