Browse Source
Rule Chain node: added ability to transfer msg to originator's default rule chainpull/10740/head
committed by
GitHub
5 changed files with 382 additions and 12 deletions
@ -0,0 +1,299 @@ |
|||
/** |
|||
* Copyright © 2016-2024 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 org.assertj.core.api.Assertions; |
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.junit.jupiter.api.extension.ExtendWith; |
|||
import org.junit.jupiter.params.ParameterizedTest; |
|||
import org.junit.jupiter.params.provider.Arguments; |
|||
import org.junit.jupiter.params.provider.MethodSource; |
|||
import org.junit.jupiter.params.provider.ValueSource; |
|||
import org.mockito.ArgumentCaptor; |
|||
import org.mockito.Mock; |
|||
import org.mockito.junit.jupiter.MockitoExtension; |
|||
import org.springframework.test.util.ReflectionTestUtils; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest; |
|||
import org.thingsboard.rule.engine.api.RuleEngineAssetProfileCache; |
|||
import org.thingsboard.rule.engine.api.RuleEngineDeviceProfileCache; |
|||
import org.thingsboard.rule.engine.api.TbContext; |
|||
import org.thingsboard.rule.engine.api.TbNode; |
|||
import org.thingsboard.rule.engine.api.TbNodeConfiguration; |
|||
import org.thingsboard.rule.engine.api.TbNodeException; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.asset.AssetProfile; |
|||
import org.thingsboard.server.common.data.id.AssetId; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.RuleChainId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.msg.TbMsgType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
import org.thingsboard.server.common.msg.TbMsgMetaData; |
|||
|
|||
import java.util.UUID; |
|||
import java.util.stream.Stream; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
import static org.assertj.core.api.Assertions.assertThatCode; |
|||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
|||
import static org.mockito.ArgumentMatchers.eq; |
|||
import static org.mockito.Mockito.any; |
|||
import static org.mockito.Mockito.spy; |
|||
import static org.mockito.Mockito.verify; |
|||
import static org.mockito.Mockito.when; |
|||
|
|||
@ExtendWith(MockitoExtension.class) |
|||
public class TbRuleChainInputNodeTest extends AbstractRuleNodeUpgradeTest { |
|||
|
|||
private final TenantId TENANT_ID = new TenantId(UUID.fromString("4ba69ea5-6b27-42df-ab66-e7a727a67027")); |
|||
private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("97731954-2147-4176-8f1a-d14f1b73e4e6")); |
|||
private final AssetId ASSET_ID = new AssetId(UUID.fromString("841a47bd-4e8e-4ea5-88e6-420da0d70e51")); |
|||
|
|||
private TbRuleChainInputNode node; |
|||
private TbRuleChainInputNodeConfiguration config; |
|||
private TbNodeConfiguration nodeConfiguration; |
|||
|
|||
@Mock |
|||
private TbContext ctxMock; |
|||
@Mock |
|||
private RuleEngineDeviceProfileCache deviceProfileCacheMock; |
|||
@Mock |
|||
private RuleEngineAssetProfileCache assetProfileCacheMock; |
|||
|
|||
@BeforeEach |
|||
public void setUp() { |
|||
node = spy(new TbRuleChainInputNode()); |
|||
config = new TbRuleChainInputNodeConfiguration().defaultConfiguration(); |
|||
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); |
|||
} |
|||
|
|||
@ParameterizedTest |
|||
@MethodSource |
|||
public void givenValidConfig_whenInit_thenOk(String ruleChainIdStr, boolean forwardMsgToDefaultRuleChain) throws TbNodeException { |
|||
//GIVEN
|
|||
config.setRuleChainId(ruleChainIdStr); |
|||
config.setForwardMsgToDefaultRuleChain(forwardMsgToDefaultRuleChain); |
|||
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); |
|||
|
|||
//WHEN
|
|||
assertThatCode(() -> node.init(ctxMock, nodeConfiguration)) |
|||
.doesNotThrowAnyException(); |
|||
|
|||
//THEN
|
|||
verify(ctxMock).checkTenantEntity(new RuleChainId(UUID.fromString(ruleChainIdStr))); |
|||
} |
|||
|
|||
private static Stream<Arguments> givenValidConfig_whenInit_thenOk() { |
|||
return Stream.of( |
|||
Arguments.of("45bba7c4-04bf-419b-ae03-6ceb9724f10e", false), |
|||
Arguments.of("52d57e1b-70bb-480e-bcc4-6710e1dcc9d8", true) |
|||
); |
|||
} |
|||
|
|||
@ParameterizedTest |
|||
@ValueSource(strings = {"91acbce0-079fdb", "", " ", "my test string"}) |
|||
public void givenInvalidRuleChainId_whenInit_thenThrowsException(String ruleChainIdStr) { |
|||
//GIVEN
|
|||
config.setRuleChainId(ruleChainIdStr); |
|||
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); |
|||
|
|||
//WHEN-THEN
|
|||
Assertions.assertThatThrownBy(() -> node.init(ctxMock, nodeConfiguration)) |
|||
.isInstanceOf(TbNodeException.class) |
|||
.hasMessage("Failed to parse rule chain id: " + ruleChainIdStr); |
|||
} |
|||
|
|||
@Test |
|||
public void givenRuleChainIdIsNotSet_whenInit_thenThrowsException() { |
|||
assertThatThrownBy(() -> node.init(ctxMock, nodeConfiguration)) |
|||
.isInstanceOf(TbNodeException.class) |
|||
.hasMessage("Rule chain must be set!"); |
|||
} |
|||
|
|||
@Test |
|||
public void givenForwardMsgToDefaultIsTrue_whenOnMsg_thenShouldTransferToDeviceDefaultRuleChain() throws TbNodeException { |
|||
//GIVEN
|
|||
DeviceProfile deviceProfile = new DeviceProfile(); |
|||
RuleChainId defaultRuleChainId = new RuleChainId(UUID.fromString("196e3cd5-68b8-421e-a0cf-1d44fa377cdf")); |
|||
deviceProfile.setDefaultRuleChainId(defaultRuleChainId); |
|||
|
|||
TbMsg msg = getMsg(DEVICE_ID); |
|||
|
|||
String ruleChainIdFromConfigStr = "acbc924f-7f95-4a9b-a854-e4822deb74c7"; |
|||
config.setRuleChainId(ruleChainIdFromConfigStr); |
|||
config.setForwardMsgToDefaultRuleChain(true); |
|||
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); |
|||
|
|||
when(ctxMock.getTenantId()).thenReturn(TENANT_ID); |
|||
when(ctxMock.getDeviceProfileCache()).thenReturn(deviceProfileCacheMock); |
|||
when(deviceProfileCacheMock.get(any(TenantId.class), any(DeviceId.class))).thenReturn(deviceProfile); |
|||
|
|||
node.init(ctxMock, nodeConfiguration); |
|||
|
|||
//WHEN
|
|||
node.onMsg(ctxMock, msg); |
|||
|
|||
//THEN
|
|||
ArgumentCaptor<RuleChainId> ruleChainArgumentCaptor = ArgumentCaptor.forClass(RuleChainId.class); |
|||
verify(ctxMock).input(eq(msg), ruleChainArgumentCaptor.capture()); |
|||
RuleChainId expectedRuleChainId = ruleChainArgumentCaptor.getValue(); |
|||
assertThat(expectedRuleChainId).isEqualTo(defaultRuleChainId); |
|||
|
|||
RuleChainId ruleChainId = (RuleChainId) ReflectionTestUtils.getField(node, "ruleChainId"); |
|||
assertThat(ruleChainId).isEqualTo(new RuleChainId(UUID.fromString(ruleChainIdFromConfigStr))); |
|||
} |
|||
|
|||
@Test |
|||
public void givenForwardMsgToDefaultIsTrue_whenOnMsg_thenShouldTransferToAssetDefaultRuleChain() throws TbNodeException { |
|||
//GIVEN
|
|||
AssetProfile assetProfile = new AssetProfile(); |
|||
RuleChainId defaultRuleChainId = new RuleChainId(UUID.fromString("f0a3cd58-980c-4730-a40c-8f59064d2065")); |
|||
assetProfile.setDefaultRuleChainId(defaultRuleChainId); |
|||
|
|||
TbMsg msg = getMsg(ASSET_ID); |
|||
|
|||
String ruleChainIdFromConfigStr = "56f1c0b8-1a00-4ce0-b3ab-a1416d7cc429"; |
|||
config.setRuleChainId(ruleChainIdFromConfigStr); |
|||
config.setForwardMsgToDefaultRuleChain(true); |
|||
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); |
|||
|
|||
when(ctxMock.getTenantId()).thenReturn(TENANT_ID); |
|||
when(ctxMock.getAssetProfileCache()).thenReturn(assetProfileCacheMock); |
|||
when(assetProfileCacheMock.get(any(TenantId.class), any(AssetId.class))).thenReturn(assetProfile); |
|||
|
|||
node.init(ctxMock, nodeConfiguration); |
|||
|
|||
//WHEN
|
|||
node.onMsg(ctxMock, msg); |
|||
|
|||
//THEN
|
|||
ArgumentCaptor<RuleChainId> ruleChainArgumentCaptor = ArgumentCaptor.forClass(RuleChainId.class); |
|||
verify(ctxMock).input(eq(msg), ruleChainArgumentCaptor.capture()); |
|||
RuleChainId expectedRuleChainId = ruleChainArgumentCaptor.getValue(); |
|||
assertThat(expectedRuleChainId).isEqualTo(defaultRuleChainId); |
|||
|
|||
RuleChainId ruleChainId = (RuleChainId) ReflectionTestUtils.getField(node, "ruleChainId"); |
|||
assertThat(ruleChainId).isEqualTo(new RuleChainId(UUID.fromString(ruleChainIdFromConfigStr))); |
|||
} |
|||
|
|||
@Test |
|||
public void givenForwardMsgToDefaultIsTrueWithoutDeviceDefaultRuleChain_whenOnMsg_thenShouldTransferToRuleChainFromConfig() throws TbNodeException { |
|||
//GIVEN
|
|||
DeviceProfile deviceProfile = new DeviceProfile(); |
|||
|
|||
TbMsg msg = getMsg(DEVICE_ID); |
|||
|
|||
String ruleChainIdFromConfigStr = "357c2785-e7cc-46a8-9797-957180dabdeb"; |
|||
RuleChainId ruleChainIdFromConfig = new RuleChainId(UUID.fromString(ruleChainIdFromConfigStr)); |
|||
config.setRuleChainId(ruleChainIdFromConfigStr); |
|||
config.setForwardMsgToDefaultRuleChain(true); |
|||
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); |
|||
|
|||
when(ctxMock.getTenantId()).thenReturn(TENANT_ID); |
|||
when(ctxMock.getDeviceProfileCache()).thenReturn(deviceProfileCacheMock); |
|||
when(deviceProfileCacheMock.get(any(TenantId.class), any(DeviceId.class))).thenReturn(deviceProfile); |
|||
|
|||
node.init(ctxMock, nodeConfiguration); |
|||
|
|||
//WHEN
|
|||
node.onMsg(ctxMock, msg); |
|||
|
|||
//THEN
|
|||
ArgumentCaptor<RuleChainId> ruleChainArgumentCaptor = ArgumentCaptor.forClass(RuleChainId.class); |
|||
verify(ctxMock).input(eq(msg), ruleChainArgumentCaptor.capture()); |
|||
assertThat(ruleChainArgumentCaptor.getValue()).isEqualTo(ruleChainIdFromConfig); |
|||
} |
|||
|
|||
@Test |
|||
public void givenForwardMsgToDefaultIsTrueWithoutAssetDefaultRuleChain_whenOnMsg_thenShouldTransferToRuleChainFromConfig() throws TbNodeException { |
|||
//GIVEN
|
|||
AssetProfile assetProfile = new AssetProfile(); |
|||
|
|||
TbMsg msg = getMsg(ASSET_ID); |
|||
|
|||
String ruleChainIdFromConfigStr = "12883c3d-c10b-4d5b-b606-a59385a920bc"; |
|||
RuleChainId ruleChainIdFromConfig = new RuleChainId(UUID.fromString(ruleChainIdFromConfigStr)); |
|||
config.setRuleChainId(ruleChainIdFromConfigStr); |
|||
config.setForwardMsgToDefaultRuleChain(true); |
|||
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); |
|||
|
|||
when(ctxMock.getTenantId()).thenReturn(TENANT_ID); |
|||
when(ctxMock.getAssetProfileCache()).thenReturn(assetProfileCacheMock); |
|||
when(assetProfileCacheMock.get(any(TenantId.class), any(AssetId.class))).thenReturn(assetProfile); |
|||
|
|||
node.init(ctxMock, nodeConfiguration); |
|||
|
|||
//WHEN
|
|||
node.onMsg(ctxMock, msg); |
|||
|
|||
//THEN
|
|||
ArgumentCaptor<RuleChainId> ruleChainArgumentCaptor = ArgumentCaptor.forClass(RuleChainId.class); |
|||
verify(ctxMock).input(eq(msg), ruleChainArgumentCaptor.capture()); |
|||
assertThat(ruleChainArgumentCaptor.getValue()).isEqualTo(ruleChainIdFromConfig); |
|||
} |
|||
|
|||
@Test |
|||
public void givenRuleChainInConfig_whenOnMsg_thenShouldTransferToRuleChainFromConfig() throws TbNodeException { |
|||
//GIVEN
|
|||
String ruleChainIdFromConfigStr = "3c02c8b3-645c-4e67-aac5-f984f59471d1"; |
|||
RuleChainId ruleChainIdFromConfig = new RuleChainId(UUID.fromString(ruleChainIdFromConfigStr)); |
|||
|
|||
TbMsg msg = getMsg(DEVICE_ID); |
|||
|
|||
config.setRuleChainId(ruleChainIdFromConfigStr); |
|||
config.setForwardMsgToDefaultRuleChain(false); |
|||
nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); |
|||
|
|||
node.init(ctxMock, nodeConfiguration); |
|||
|
|||
//WHEN
|
|||
node.onMsg(ctxMock, msg); |
|||
|
|||
//THEN
|
|||
ArgumentCaptor<RuleChainId> ruleChainArgumentCaptor = ArgumentCaptor.forClass(RuleChainId.class); |
|||
verify(ctxMock).input(eq(msg), ruleChainArgumentCaptor.capture()); |
|||
assertThat(ruleChainArgumentCaptor.getValue()).isEqualTo(ruleChainIdFromConfig); |
|||
} |
|||
|
|||
private static Stream<Arguments> givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { |
|||
return Stream.of( |
|||
//config for version 0
|
|||
Arguments.of(0, |
|||
"{\"ruleChainId\": null}", |
|||
true, |
|||
"{\"ruleChainId\": null, \"forwardMsgToDefaultRuleChain\": false}" |
|||
), |
|||
//config for version 1 with upgrade from version 0
|
|||
Arguments.of(1, |
|||
"{\"ruleChainId\": null, \"forwardMsgToDefaultRuleChain\": false}", |
|||
false, |
|||
"{\"ruleChainId\": null, \"forwardMsgToDefaultRuleChain\": false}" |
|||
) |
|||
); |
|||
} |
|||
|
|||
@Override |
|||
protected TbNode getTestNode() { |
|||
return node; |
|||
} |
|||
|
|||
private TbMsg getMsg(EntityId entityId) { |
|||
return TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_STRING); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue