From 358af2b9323b2643196b42329da4d50e2045c887 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Tue, 2 Jul 2024 12:37:09 +0300 Subject: [PATCH 01/14] added tests for the change originator node --- .../transform/TbChangeOriginatorNode.java | 8 +- .../transform/TbChangeOriginatorNodeTest.java | 311 ++++++++++++++---- 2 files changed, 260 insertions(+), 59 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java index 7bf6338e81..177814df4a 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java @@ -111,25 +111,25 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode knownSources = Sets.newHashSet(CUSTOMER_SOURCE, TENANT_SOURCE, RELATED_SOURCE, ALARM_ORIGINATOR_SOURCE, ENTITY_SOURCE); if (!knownSources.contains(conf.getOriginatorSource())) { log.error("Unsupported source [{}] for TbChangeOriginatorNode", conf.getOriginatorSource()); - throw new IllegalArgumentException("Unsupported source TbChangeOriginatorNode" + conf.getOriginatorSource()); + throw new IllegalArgumentException("Source '" + conf.getOriginatorSource() + "' is unsupported."); } if (conf.getOriginatorSource().equals(RELATED_SOURCE)) { if (conf.getRelationsQuery() == null) { log.error("Related source for TbChangeOriginatorNode should have relations query. Actual [{}]", conf.getRelationsQuery()); - throw new IllegalArgumentException("Wrong config for RElated Source in TbChangeOriginatorNode" + conf.getOriginatorSource()); + throw new IllegalArgumentException("Relations query should be specified for the related entity."); } } if (conf.getOriginatorSource().equals(ENTITY_SOURCE)) { if (conf.getEntityType() == null) { log.error("Entity type not specified for [{}]", ENTITY_SOURCE); - throw new IllegalArgumentException("Wrong config for [{}] in TbChangeOriginatorNode!" + ENTITY_SOURCE); + throw new IllegalArgumentException("Entity type should be specified for the entity."); } if (StringUtils.isEmpty(conf.getEntityNamePattern())) { log.error("EntityNamePattern not specified for type [{}]", conf.getEntityType()); - throw new IllegalArgumentException("Wrong config for [{}] in TbChangeOriginatorNode!" + ENTITY_SOURCE); + throw new IllegalArgumentException("Entity name pattern should be specified for the entity."); } EntitiesByNameAndTypeLoader.checkEntityType(EntityType.valueOf(conf.getEntityType())); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java index 952a566695..be328a0aec 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java @@ -20,58 +20,86 @@ import com.google.common.util.concurrent.Futures; 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.NullAndEmptySource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; import org.thingsboard.rule.engine.TestDbCallbackExecutor; +import org.thingsboard.rule.engine.api.RuleEngineAlarmService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.data.RelationsQuery; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; +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.RuleNodeId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntityRelationsQuery; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; +import org.thingsboard.server.common.data.relation.RelationsSearchParameters; 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.asset.AssetService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.relation.RelationService; +import java.util.Collections; import java.util.NoSuchElementException; +import java.util.UUID; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; @ExtendWith(MockitoExtension.class) public class TbChangeOriginatorNodeTest { - private static final String CUSTOMER_SOURCE = "CUSTOMER"; + private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("79830b6d-4f93-49bd-9b5b-d31ce51da77b")); + private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("990605a4-db46-4ed4-942f-e18200453571")); + private final AssetId ASSET_ID = new AssetId(UUID.fromString("55de3f10-1b55-4950-b711-ed132896b260")); + + private final ListeningExecutor dbExecutor = new TestDbCallbackExecutor(); private TbChangeOriginatorNode node; + private TbChangeOriginatorNodeConfiguration config; @Mock - private TbContext ctx; + private TbContext ctxMock; @Mock - private AssetService assetService; - - private ListeningExecutor dbExecutor; + private AssetService assetServiceMock; + @Mock + private DeviceService deviceServiceMock; + @Mock + private RelationService relationServiceMock; + @Mock + private RuleEngineAlarmService alarmServiceMock; @BeforeEach public void before() throws TbNodeException { - dbExecutor = new TestDbCallbackExecutor(); - init(); + node = new TbChangeOriginatorNode(); + config = new TbChangeOriginatorNodeConfiguration().defaultConfiguration(); } @Test - public void originatorCanBeChangedToCustomerId() { + public void newChainCanBeStarted() throws TbNodeException { + init(); AssetId assetId = new AssetId(Uuids.timeBased()); CustomerId customerId = new CustomerId(Uuids.timeBased()); Asset asset = new Asset(); @@ -80,74 +108,247 @@ public class TbChangeOriginatorNodeTest { RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); - - when(ctx.getAssetService()).thenReturn(assetService); - when(assetService.findAssetByIdAsync(any(),eq( assetId))).thenReturn(Futures.immediateFuture(asset)); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON,TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); - node.onMsg(ctx, msg); + given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); + given(ctxMock.getAssetService()).willReturn(assetServiceMock); + given(assetServiceMock.findAssetByIdAsync(any(), eq(assetId))).willReturn(Futures.immediateFuture(asset)); + node.onMsg(ctxMock, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); - verify(ctx).transformMsgOriginator(msgCaptor.capture(), originatorCaptor.capture()); + then(ctxMock).should().transformMsgOriginator(msgCaptor.capture(), originatorCaptor.capture()); - assertEquals(customerId, originatorCaptor.getValue()); + assertThat(originatorCaptor.getValue()).isEqualTo(customerId); + } + + public void init() throws TbNodeException { + TbChangeOriginatorNodeConfiguration config = new TbChangeOriginatorNodeConfiguration(); + config.setOriginatorSource("CUSTOMER"); + TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + + given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); + + node = new TbChangeOriginatorNode(); + node.init(null, nodeConfiguration); } @Test - public void newChainCanBeStarted() { - AssetId assetId = new AssetId(Uuids.timeBased()); - CustomerId customerId = new CustomerId(Uuids.timeBased()); - Asset asset = new Asset(); - asset.setCustomerId(customerId); + public void verifyDefaultConfig() { + var config = new TbChangeOriginatorNodeConfiguration().defaultConfiguration(); + assertThat(config.getOriginatorSource()).isEqualTo("CUSTOMER"); + + RelationsQuery relationsQuery = new RelationsQuery(); + relationsQuery.setDirection(EntitySearchDirection.FROM); + relationsQuery.setMaxLevel(1); + RelationEntityTypeFilter relationEntityTypeFilter = new RelationEntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.emptyList()); + relationsQuery.setFilters(Collections.singletonList(relationEntityTypeFilter)); + assertThat(config.getRelationsQuery()).isEqualTo(relationsQuery); + + assertThat(config.getEntityType()).isNull(); + assertThat(config.getEntityNamePattern()).isNull(); + } - RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); - RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); + @Test + public void givenUnsupportedSource_whenInit_thenThrowsException() { + config.setOriginatorSource("UNSUPPORTED_SOURCE"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON,TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Source 'UNSUPPORTED_SOURCE' is unsupported."); + } - when(ctx.getAssetService()).thenReturn(assetService); - when(assetService.findAssetByIdAsync(any(), eq(assetId))).thenReturn(Futures.immediateFuture(asset)); + @Test + public void givenRelatedSourceAndRelatedQueryIsNull_whenInit_thenThrowsException() { + config.setOriginatorSource("RELATED"); + config.setRelationsQuery(null); - node.onMsg(ctx, msg); - ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); - verify(ctx).transformMsgOriginator(msgCaptor.capture(), originatorCaptor.capture()); + assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Relations query should be specified for the related entity."); + } - assertEquals(customerId, originatorCaptor.getValue()); + @Test + public void givenEntitySourceAndEntityTypeIsNull_whenInit_thenThrowsException() { + config.setOriginatorSource("ENTITY"); + config.setEntityType(null); + + assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Entity type should be specified for the entity."); + } + + @ParameterizedTest + @NullAndEmptySource + public void givenEntitySourceAndEntityNamePatternIsEmpty_whenInit_thenThrowsException(String entityName) { + config.setOriginatorSource("ENTITY"); + config.setEntityType("DEVICE"); + config.setEntityNamePattern(entityName); + + assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Entity name pattern should be specified for the entity."); } @Test - public void exceptionThrownIfCannotFindNewOriginator() { - AssetId assetId = new AssetId(Uuids.timeBased()); - CustomerId customerId = new CustomerId(Uuids.timeBased()); - Asset asset = new Asset(); - asset.setCustomerId(customerId); + public void givenEntitySourceAndUnexpectedEntityType_whenInit_thenThrowsException() { + config.setOriginatorSource("ENTITY"); + config.setEntityType("TENANT"); + config.setEntityNamePattern("tenant-A"); + + assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Unexpected entity type TENANT"); + } - RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); - RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); + @Test + public void givenOriginatorSourceIsCustomer_whenOnMsg_thenTellSuccess() throws TbNodeException { + config.setOriginatorSource("CUSTOMER"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON,TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + CustomerId customerId = new CustomerId(UUID.fromString("d2746ed1-2c45-41d7-b34c-94f947f2aa03")); + Device device = new Device(DEVICE_ID); + device.setCustomerId(customerId); - when(ctx.getAssetService()).thenReturn(assetService); - when(assetService.findAssetByIdAsync(any(), eq(assetId))).thenReturn(Futures.immediateFuture(null)); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, customerId); - ArgumentCaptor exceptionCaptor = ArgumentCaptor.forClass(NoSuchElementException.class); + given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); + given(ctxMock.getDeviceService()).willReturn(deviceServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + given(deviceServiceMock.findDeviceById(any(TenantId.class), any(DeviceId.class))).willReturn(device); + given(ctxMock.transformMsgOriginator(any(TbMsg.class), any(EntityId.class))).willReturn(expectedMsg); - node.onMsg(ctx, msg); - verify(ctx).tellFailure(same(msg), exceptionCaptor.capture()); + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + node.onMsg(ctxMock, msg); - assertEquals("Failed to find new originator!", exceptionCaptor.getValue().getMessage()); + then(deviceServiceMock).should().findDeviceById(TENANT_ID, DEVICE_ID); + ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); + then(ctxMock).should().tellSuccess(actualMsg.capture()); + assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } - public void init() throws TbNodeException { - TbChangeOriginatorNodeConfiguration config = new TbChangeOriginatorNodeConfiguration(); - config.setOriginatorSource(CUSTOMER_SOURCE); - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + @Test + public void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbNodeException { + config.setOriginatorSource("TENANT"); - when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, TENANT_ID); - node = new TbChangeOriginatorNode(); - node.init(null, nodeConfiguration); + given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + given(ctxMock.transformMsgOriginator(any(TbMsg.class), any(EntityId.class))).willReturn(expectedMsg); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + node.onMsg(ctxMock, msg); + + ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); + then(ctxMock).should().tellSuccess(actualMsg.capture()); + assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } + + @Test + public void givenOriginatorSourceIsRelatedAndNewOriginatorIsNull_whenOnMsg_thenTellFailure() throws TbNodeException { + config.setOriginatorSource("RELATED"); + + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); + given(ctxMock.getRelationService()).willReturn(relationServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + given(relationServiceMock.findByQuery(any(TenantId.class), any(EntityRelationsQuery.class))).willReturn(Futures.immediateFuture(Collections.emptyList())); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + node.onMsg(ctxMock, msg); + + var query = new EntityRelationsQuery(); + var relationsQuery = config.getRelationsQuery(); + var parameters = new RelationsSearchParameters( + ASSET_ID, + relationsQuery.getDirection(), + relationsQuery.getMaxLevel(), + relationsQuery.isFetchLastLevelOnly() + ); + query.setParameters(parameters); + query.setFilters(relationsQuery.getFilters()); + then(relationServiceMock).should().findByQuery(TENANT_ID, query); + ArgumentCaptor throwable = ArgumentCaptor.forClass(Throwable.class); + then(ctxMock).should().tellFailure(eq(msg), throwable.capture()); + assertThat(throwable.getValue()).isInstanceOf(NoSuchElementException.class).hasMessage("Failed to find new originator!"); + } + + @Test + public void givenOriginatorSourceIsAlarmOriginator_whenOnMsg_thenTellSuccess() throws TbNodeException { + config.setOriginatorSource("ALARM_ORIGINATOR"); + + AlarmId alarmId = new AlarmId(UUID.fromString("6b43f694-cb5f-4199-9023-e9e40eeb82dd")); + Alarm alarm = new Alarm(alarmId); + alarm.setOriginator(DEVICE_ID); + + TbMsg msg = TbMsg.newMsg(TbMsgType.ALARM, alarmId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, DEVICE_ID); + + given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); + given(ctxMock.getAlarmService()).willReturn(alarmServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + given(alarmServiceMock.findAlarmByIdAsync(any(TenantId.class), any(AlarmId.class))).willReturn(Futures.immediateFuture(alarm)); + given(ctxMock.transformMsgOriginator(any(TbMsg.class), any(EntityId.class))).willReturn(expectedMsg); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + node.onMsg(ctxMock, msg); + + then(alarmServiceMock).should().findAlarmByIdAsync(TENANT_ID, alarmId); + ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); + then(ctxMock).should().tellSuccess(actualMsg.capture()); + assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); + } + + @Test + public void givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess() throws TbNodeException { + config.setOriginatorSource("ENTITY"); + config.setEntityType("ASSET"); + config.setEntityNamePattern("${md-name-pattern}"); + + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("md-name-pattern", "test-asset"); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, ASSET_ID); + + given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); + given(ctxMock.getAssetService()).willReturn(assetServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + given(assetServiceMock.findAssetByTenantIdAndName(any(TenantId.class), any(String.class))).willReturn(new Asset(ASSET_ID)); + given(ctxMock.transformMsgOriginator(any(TbMsg.class), any(EntityId.class))).willReturn(expectedMsg); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + node.onMsg(ctxMock, msg); + + then(assetServiceMock).should().findAssetByTenantIdAndName(TENANT_ID, "test-asset"); + ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); + then(ctxMock).should().tellSuccess(actualMsg.capture()); + assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); + } + + @Test + public void givenOriginatorSourceIsEntityAndEntityCouldNotFound_whenOnMsg_thenTellFailure() throws TbNodeException { + config.setOriginatorSource("ENTITY"); + config.setEntityType("ASSET"); + config.setEntityNamePattern("${md-name-pattern}"); + + TbMsgMetaData metaData = new TbMsgMetaData(); + metaData.putValue("md-name-pattern", "test-asset"); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + + given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); + given(ctxMock.getAssetService()).willReturn(assetServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + given(assetServiceMock.findAssetByTenantIdAndName(any(TenantId.class), any(String.class))).willReturn(null); + + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); + node.onMsg(ctxMock, msg); + + ArgumentCaptor throwable = ArgumentCaptor.forClass(Throwable.class); + then(ctxMock).should().tellFailure(eq(msg), throwable.capture()); + assertThat(throwable.getValue()).isInstanceOf(IllegalStateException.class).hasMessage("Failed to found ASSET entity by name: 'test-asset'!"); + } + } From 8e81bd80e6c130fc0317c8eaf26ef08545ecaff8 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Fri, 2 Aug 2024 17:41:51 +0300 Subject: [PATCH 02/14] added tests for flow type rule nodes --- .../rule/engine/flow/TbAckNodeTest.java | 78 +++++++++++++++++ .../engine/flow/TbCheckpointNodeTest.java | 75 ++++++++++++++++- .../engine/flow/TbRuleChainInputNodeTest.java | 6 ++ .../flow/TbRuleChainOutputNodeTest.java | 83 +++++++++++++++++++ 4 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java create mode 100644 rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java new file mode 100644 index 0000000000..909f866493 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java @@ -0,0 +1,78 @@ +/** + * 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.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.id.DeviceId; +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 static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.mockito.BDDMockito.then; + +@ExtendWith(MockitoExtension.class) +public class TbAckNodeTest { + + private TbAckNode node; + private EmptyNodeConfiguration config; + private TbNodeConfiguration nodeConfiguration; + + + @Mock + private TbContext ctxMock; + + @BeforeEach + public void setUp() { + node = new TbAckNode(); + config = new EmptyNodeConfiguration().defaultConfiguration(); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + } + + @Test + public void verifyDefaultConfig() { + assertThat(config.getVersion()).isEqualTo(0); + } + + @Test + public void givenDefaultConfig_whenInit_thenOk() { + assertThatNoException().isThrownBy(() -> node.init(ctxMock, nodeConfiguration)); + } + + @Test + public void givenMsg_whenOnMsg_thenAckAndTellSuccess() throws TbNodeException { + node.init(ctxMock, nodeConfiguration); + DeviceId deviceId = new DeviceId(UUID.fromString("5770153d-6ca2-4447-8a54-5d8a4538e052")); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + node.onMsg(ctxMock, msg); + + then(ctxMock).should().ack(msg); + then(ctxMock).should().tellSuccess(msg); + } + +} diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java index 545f6112d8..9b6e86b9a6 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java @@ -16,17 +16,88 @@ package org.thingsboard.rule.engine.flow; import lombok.extern.slf4j.Slf4j; +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.ValueSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest; +import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; +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.DataConstants; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import java.util.UUID; +import java.util.function.Consumer; import java.util.stream.Stream; -import static org.mockito.Mockito.spy; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.spy; +import static org.mockito.BDDMockito.then; +import static org.mockito.BDDMockito.willAnswer; @Slf4j +@ExtendWith(MockitoExtension.class) public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { + private TbCheckpointNode node; + private EmptyNodeConfiguration config; + private TbNodeConfiguration nodeConfiguration; + + @Mock + private TbContext ctxMock; + + @BeforeEach + public void setUp() { + node = spy(new TbCheckpointNode()); + config = new EmptyNodeConfiguration().defaultConfiguration(); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + } + + @Test + public void verifyDefaultConfig() { + assertThat(config.getVersion()).isEqualTo(0); + } + + @Test + public void givenDefaultConfig_whenInit_thenOk() { + assertThatNoException().isThrownBy(() -> node.init(ctxMock, nodeConfiguration)); + } + + @ParameterizedTest + @ValueSource(strings = {DataConstants.MAIN_QUEUE_NAME, DataConstants.HP_QUEUE_NAME, DataConstants.HP_QUEUE_NAME, "Custom queue"}) + public void givenQueueName_whenOnMsg_thenTransfersMsgToDefinedQueue(String queueName) throws TbNodeException { + given(ctxMock.getQueueName()).willReturn(queueName); + willAnswer(invocationOnMock -> { + Runnable onSuccess = invocationOnMock.getArgument(3); + onSuccess.run(); + return null; + }).given(ctxMock).enqueueForTellNext(any(TbMsg.class), any(String.class), any(String.class), any(Runnable.class), any(Consumer.class)); + + node.init(ctxMock, nodeConfiguration); + DeviceId deviceId = new DeviceId(UUID.fromString("2cd04871-7f07-41d1-b850-95dd444a6506")); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + node.onMsg(ctxMock, msg); + + then(ctxMock).should().enqueueForTellNext(eq(msg), eq(queueName), eq(TbNodeConnectionType.SUCCESS), any(), any()); + then(ctxMock).should().ack(msg); + } + // Rule nodes upgrade private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { return Stream.of( @@ -50,6 +121,6 @@ public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { @Override protected TbNode getTestNode() { - return spy(TbCheckpointNode.class); + return node; } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java index 579ed7f035..680d106d1e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNodeTest.java @@ -83,6 +83,12 @@ public class TbRuleChainInputNodeTest extends AbstractRuleNodeUpgradeTest { nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); } + @Test + public void verifyDefaultConfig() { + assertThat(config.getRuleChainId()).isNull(); + assertThat(config.isForwardMsgToDefaultRuleChain()).isFalse(); + } + @ParameterizedTest @MethodSource public void givenValidConfig_whenInit_thenOk(String ruleChainIdStr, boolean forwardMsgToDefaultRuleChain) throws TbNodeException { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java new file mode 100644 index 0000000000..5c678e5c50 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNodeTest.java @@ -0,0 +1,83 @@ +/** + * 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.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.EmptyNodeConfiguration; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.spy; +import static org.mockito.BDDMockito.then; + +@ExtendWith(MockitoExtension.class) +public class TbRuleChainOutputNodeTest { + + private TbRuleChainOutputNode node; + private EmptyNodeConfiguration config; + private TbNodeConfiguration nodeConfiguration; + + @Mock + private TbContext ctxMock; + + @BeforeEach + public void setUp() { + node = spy(new TbRuleChainOutputNode()); + config = new EmptyNodeConfiguration().defaultConfiguration(); + nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + } + + @Test + public void verifyDefaultConfig() { + assertThat(config.getVersion()).isEqualTo(0); + } + + @Test + public void givenDefaultConfig_whenInit_thenOk() { + assertThatNoException().isThrownBy(() -> node.init(ctxMock, nodeConfiguration)); + } + + @Test + public void givenRuleNodeName_whenOnMsg_thenForwardMsgToTheCallerRuleChainWithRelationTypeMatchesWithRuleNodeName() throws TbNodeException { + RuleNode ruleNode = new RuleNode(); + ruleNode.setName("test"); + given(ctxMock.getSelf()).willReturn(ruleNode); + + node.init(ctxMock, nodeConfiguration); + DeviceId deviceId = new DeviceId(UUID.fromString("f514da88-79b3-46da-9f02-1747c5e84f44")); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + node.onMsg(ctxMock, msg); + + then(ctxMock).should().output(msg, "test"); + } + +} From 2e5708b6c224785f2170d63df176c006b4f0e67f Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 8 Aug 2024 10:26:16 +0300 Subject: [PATCH 03/14] removed unnecessary init() method --- .../transform/TbChangeOriginatorNode.java | 22 ++-- .../transform/TbChangeOriginatorNodeTest.java | 108 ++++++++++-------- 2 files changed, 71 insertions(+), 59 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java index 177814df4a..747ec345d9 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java @@ -65,6 +65,10 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode knownSources = Sets.newHashSet(CUSTOMER_SOURCE, TENANT_SOURCE, RELATED_SOURCE, ALARM_ORIGINATOR_SOURCE, ENTITY_SOURCE); if (!knownSources.contains(conf.getOriginatorSource())) { - log.error("Unsupported source [{}] for TbChangeOriginatorNode", conf.getOriginatorSource()); - throw new IllegalArgumentException("Source '" + conf.getOriginatorSource() + "' is unsupported."); + log.error("Unsupported source type '[{}]'! Only {} types are allowed.", conf.getOriginatorSource(), supportedOriginatorSourcesStr); + throw new IllegalArgumentException("Unsupported source type '" + conf.getOriginatorSource() + + "'! Only " + supportedOriginatorSourcesStr + " types are allowed."); } if (conf.getOriginatorSource().equals(RELATED_SOURCE)) { if (conf.getRelationsQuery() == null) { - log.error("Related source for TbChangeOriginatorNode should have relations query. Actual [{}]", - conf.getRelationsQuery()); - throw new IllegalArgumentException("Relations query should be specified for the related entity."); + log.error("Relations query should be specified if 'Related entity' source is selected."); + throw new IllegalArgumentException("Relations query should be specified if 'Related entity' source is selected."); } } if (conf.getOriginatorSource().equals(ENTITY_SOURCE)) { if (conf.getEntityType() == null) { - log.error("Entity type not specified for [{}]", ENTITY_SOURCE); - throw new IllegalArgumentException("Entity type should be specified for the entity."); + log.error("Entity type should be specified if '[{}]' source is selected.", ENTITY_SOURCE); + throw new IllegalArgumentException("Entity type should be specified if 'Entity by name pattern' source is selected."); } if (StringUtils.isEmpty(conf.getEntityNamePattern())) { - log.error("EntityNamePattern not specified for type [{}]", conf.getEntityType()); - throw new IllegalArgumentException("Entity name pattern should be specified for the entity."); + log.error("Name pattern should be specified if '[{}]' source is selected.", ENTITY_SOURCE); + throw new IllegalArgumentException("Name pattern should be specified if 'Entity by name pattern' source is selected."); } EntitiesByNameAndTypeLoader.checkEntityType(EntityType.valueOf(conf.getEntityType())); } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java index be328a0aec..8e72f92fc5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java @@ -21,6 +21,8 @@ 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.NullAndEmptySource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; @@ -32,8 +34,10 @@ import org.thingsboard.rule.engine.api.RuleEngineAlarmService; import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeConfiguration; import org.thingsboard.rule.engine.api.TbNodeException; +import org.thingsboard.rule.engine.api.util.TbNodeUtils; import org.thingsboard.rule.engine.data.RelationsQuery; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AlarmId; @@ -58,8 +62,11 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.relation.RelationService; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.UUID; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -72,11 +79,22 @@ import static org.mockito.BDDMockito.then; public class TbChangeOriginatorNodeTest { private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("79830b6d-4f93-49bd-9b5b-d31ce51da77b")); + private final CustomerId CUSTOMER_ID = new CustomerId(UUID.fromString("c6b2c94b-5517-4f20-bf8e-ae9407eb8a7a")); private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("990605a4-db46-4ed4-942f-e18200453571")); private final AssetId ASSET_ID = new AssetId(UUID.fromString("55de3f10-1b55-4950-b711-ed132896b260")); private final ListeningExecutor dbExecutor = new TestDbCallbackExecutor(); + private final String CUSTOMER_SOURCE = "CUSTOMER"; + private final String TENANT_SOURCE = "TENANT"; + private final String RELATED_SOURCE = "RELATED"; + private final String ALARM_ORIGINATOR_SOURCE = "ALARM_ORIGINATOR"; + private final String ENTITY_SOURCE = "ENTITY"; + + private final String supportedOriginatorSourcesStr = String.join(", ", List.of( + CUSTOMER_SOURCE, TENANT_SOURCE, RELATED_SOURCE, ALARM_ORIGINATOR_SOURCE, ENTITY_SOURCE) + ); + private TbChangeOriginatorNode node; private TbChangeOriginatorNodeConfiguration config; @@ -99,52 +117,37 @@ public class TbChangeOriginatorNodeTest { @Test public void newChainCanBeStarted() throws TbNodeException { - init(); - AssetId assetId = new AssetId(Uuids.timeBased()); - CustomerId customerId = new CustomerId(Uuids.timeBased()); Asset asset = new Asset(); - asset.setCustomerId(customerId); + asset.setCustomerId(CUSTOMER_ID); RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON,TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getAssetService()).willReturn(assetServiceMock); - given(assetServiceMock.findAssetByIdAsync(any(), eq(assetId))).willReturn(Futures.immediateFuture(asset)); + given(assetServiceMock.findAssetByIdAsync(any(), eq(ASSET_ID))).willReturn(Futures.immediateFuture(asset)); + node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); node.onMsg(ctxMock, msg); ArgumentCaptor msgCaptor = ArgumentCaptor.forClass(TbMsg.class); ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); then(ctxMock).should().transformMsgOriginator(msgCaptor.capture(), originatorCaptor.capture()); - assertThat(originatorCaptor.getValue()).isEqualTo(customerId); - } - - public void init() throws TbNodeException { - TbChangeOriginatorNodeConfiguration config = new TbChangeOriginatorNodeConfiguration(); - config.setOriginatorSource("CUSTOMER"); - TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); - - given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); - - node = new TbChangeOriginatorNode(); - node.init(null, nodeConfiguration); + assertThat(originatorCaptor.getValue()).isEqualTo(CUSTOMER_ID); } @Test public void verifyDefaultConfig() { var config = new TbChangeOriginatorNodeConfiguration().defaultConfiguration(); - assertThat(config.getOriginatorSource()).isEqualTo("CUSTOMER"); - + assertThat(config.getOriginatorSource()).isEqualTo(CUSTOMER_SOURCE); RelationsQuery relationsQuery = new RelationsQuery(); relationsQuery.setDirection(EntitySearchDirection.FROM); relationsQuery.setMaxLevel(1); RelationEntityTypeFilter relationEntityTypeFilter = new RelationEntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.emptyList()); relationsQuery.setFilters(Collections.singletonList(relationEntityTypeFilter)); assertThat(config.getRelationsQuery()).isEqualTo(relationsQuery); - assertThat(config.getEntityType()).isNull(); assertThat(config.getEntityNamePattern()).isNull(); } @@ -155,45 +158,45 @@ public class TbChangeOriginatorNodeTest { assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Source 'UNSUPPORTED_SOURCE' is unsupported."); + .hasMessage("Unsupported source type 'UNSUPPORTED_SOURCE'! Only " + supportedOriginatorSourcesStr + " types are allowed."); } @Test public void givenRelatedSourceAndRelatedQueryIsNull_whenInit_thenThrowsException() { - config.setOriginatorSource("RELATED"); + config.setOriginatorSource(RELATED_SOURCE); config.setRelationsQuery(null); assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Relations query should be specified for the related entity."); + .hasMessage("Relations query should be specified if 'Related entity' source is selected."); } @Test public void givenEntitySourceAndEntityTypeIsNull_whenInit_thenThrowsException() { - config.setOriginatorSource("ENTITY"); + config.setOriginatorSource(ENTITY_SOURCE); config.setEntityType(null); assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Entity type should be specified for the entity."); + .hasMessage("Entity type should be specified if 'Entity by name pattern' source is selected."); } @ParameterizedTest @NullAndEmptySource public void givenEntitySourceAndEntityNamePatternIsEmpty_whenInit_thenThrowsException(String entityName) { - config.setOriginatorSource("ENTITY"); - config.setEntityType("DEVICE"); + config.setOriginatorSource(ENTITY_SOURCE); + config.setEntityType(EntityType.DEVICE.name()); config.setEntityNamePattern(entityName); assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Entity name pattern should be specified for the entity."); + .hasMessage("Name pattern should be specified if 'Entity by name pattern' source is selected."); } @Test public void givenEntitySourceAndUnexpectedEntityType_whenInit_thenThrowsException() { - config.setOriginatorSource("ENTITY"); - config.setEntityType("TENANT"); + config.setOriginatorSource(ENTITY_SOURCE); + config.setEntityType(EntityType.TENANT.name()); config.setEntityNamePattern("tenant-A"); assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) @@ -203,14 +206,11 @@ public class TbChangeOriginatorNodeTest { @Test public void givenOriginatorSourceIsCustomer_whenOnMsg_thenTellSuccess() throws TbNodeException { - config.setOriginatorSource("CUSTOMER"); - - CustomerId customerId = new CustomerId(UUID.fromString("d2746ed1-2c45-41d7-b34c-94f947f2aa03")); Device device = new Device(DEVICE_ID); - device.setCustomerId(customerId); + device.setCustomerId(CUSTOMER_ID); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); - TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, customerId); + TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, CUSTOMER_ID); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); given(ctxMock.getDeviceService()).willReturn(deviceServiceMock); @@ -229,7 +229,7 @@ public class TbChangeOriginatorNodeTest { @Test public void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbNodeException { - config.setOriginatorSource("TENANT"); + config.setOriginatorSource(TENANT_SOURCE); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, TENANT_ID); @@ -248,7 +248,7 @@ public class TbChangeOriginatorNodeTest { @Test public void givenOriginatorSourceIsRelatedAndNewOriginatorIsNull_whenOnMsg_thenTellFailure() throws TbNodeException { - config.setOriginatorSource("RELATED"); + config.setOriginatorSource(RELATED_SOURCE); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); @@ -278,7 +278,7 @@ public class TbChangeOriginatorNodeTest { @Test public void givenOriginatorSourceIsAlarmOriginator_whenOnMsg_thenTellSuccess() throws TbNodeException { - config.setOriginatorSource("ALARM_ORIGINATOR"); + config.setOriginatorSource(ALARM_ORIGINATOR_SOURCE); AlarmId alarmId = new AlarmId(UUID.fromString("6b43f694-cb5f-4199-9023-e9e40eeb82dd")); Alarm alarm = new Alarm(alarmId); @@ -302,15 +302,14 @@ public class TbChangeOriginatorNodeTest { assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } - @Test - public void givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess() throws TbNodeException { - config.setOriginatorSource("ENTITY"); - config.setEntityType("ASSET"); - config.setEntityNamePattern("${md-name-pattern}"); + @ParameterizedTest + @MethodSource + public void givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess(String entityNamePattern, TbMsgMetaData metaData, String data) throws TbNodeException { + config.setOriginatorSource(ENTITY_SOURCE); + config.setEntityType(EntityType.ASSET.name()); + config.setEntityNamePattern(entityNamePattern); - TbMsgMetaData metaData = new TbMsgMetaData(); - metaData.putValue("md-name-pattern", "test-asset"); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, ASSET_ID); given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); @@ -322,16 +321,25 @@ public class TbChangeOriginatorNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); node.onMsg(ctxMock, msg); - then(assetServiceMock).should().findAssetByTenantIdAndName(TENANT_ID, "test-asset"); + String expectedEntityName = TbNodeUtils.processPattern(entityNamePattern, msg); + then(assetServiceMock).should().findAssetByTenantIdAndName(TENANT_ID, expectedEntityName); ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().tellSuccess(actualMsg.capture()); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); } + private static Stream givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess() { + return Stream.of( + Arguments.of("test-asset", TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT), + Arguments.of("${md-name-pattern}", new TbMsgMetaData(Map.of("md-name-pattern", "md-test-asset")), TbMsg.EMPTY_JSON_OBJECT), + Arguments.of("${msg-name-pattern}", TbMsgMetaData.EMPTY, "{\"msg-name-pattern\":\"msg-test-asset\"}") + ); + } + @Test public void givenOriginatorSourceIsEntityAndEntityCouldNotFound_whenOnMsg_thenTellFailure() throws TbNodeException { - config.setOriginatorSource("ENTITY"); - config.setEntityType("ASSET"); + config.setOriginatorSource(ENTITY_SOURCE); + config.setEntityType(EntityType.ASSET.name()); config.setEntityNamePattern("${md-name-pattern}"); TbMsgMetaData metaData = new TbMsgMetaData(); From c8066857343a082d6b0b6071ee6105eaf877b96e Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 8 Aug 2024 11:51:19 +0300 Subject: [PATCH 04/14] fixed exceptions --- .../transform/TbChangeOriginatorNode.java | 6 ++-- .../util/EntitiesByNameAndTypeLoader.java | 2 +- .../transform/TbChangeOriginatorNodeTest.java | 33 +++---------------- 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java index 747ec345d9..f6f33d30c0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java @@ -114,7 +114,7 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode knownSources = Sets.newHashSet(CUSTOMER_SOURCE, TENANT_SOURCE, RELATED_SOURCE, ALARM_ORIGINATOR_SOURCE, ENTITY_SOURCE); if (!knownSources.contains(conf.getOriginatorSource())) { - log.error("Unsupported source type '[{}]'! Only {} types are allowed.", conf.getOriginatorSource(), supportedOriginatorSourcesStr); + log.error("Unsupported source type '{}'! Only {} types are allowed.", conf.getOriginatorSource(), supportedOriginatorSourcesStr); throw new IllegalArgumentException("Unsupported source type '" + conf.getOriginatorSource() + "'! Only " + supportedOriginatorSourcesStr + " types are allowed."); } @@ -128,11 +128,11 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode msgCaptor = ArgumentCaptor.forClass(TbMsg.class); - ArgumentCaptor originatorCaptor = ArgumentCaptor.forClass(EntityId.class); - then(ctxMock).should().transformMsgOriginator(msgCaptor.capture(), originatorCaptor.capture()); - - assertThat(originatorCaptor.getValue()).isEqualTo(CUSTOMER_ID); - } - @Test public void verifyDefaultConfig() { var config = new TbChangeOriginatorNodeConfiguration().defaultConfiguration(); @@ -222,6 +195,7 @@ public class TbChangeOriginatorNodeTest { node.onMsg(ctxMock, msg); then(deviceServiceMock).should().findDeviceById(TENANT_ID, DEVICE_ID); + then(ctxMock).should().transformMsgOriginator(msg, CUSTOMER_ID); ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().tellSuccess(actualMsg.capture()); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); @@ -241,6 +215,7 @@ public class TbChangeOriginatorNodeTest { node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); node.onMsg(ctxMock, msg); + then(ctxMock).should().transformMsgOriginator(msg, TENANT_ID); ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().tellSuccess(actualMsg.capture()); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); @@ -297,6 +272,7 @@ public class TbChangeOriginatorNodeTest { node.onMsg(ctxMock, msg); then(alarmServiceMock).should().findAlarmByIdAsync(TENANT_ID, alarmId); + then(ctxMock).should().transformMsgOriginator(msg, DEVICE_ID); ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().tellSuccess(actualMsg.capture()); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); @@ -323,6 +299,7 @@ public class TbChangeOriginatorNodeTest { String expectedEntityName = TbNodeUtils.processPattern(entityNamePattern, msg); then(assetServiceMock).should().findAssetByTenantIdAndName(TENANT_ID, expectedEntityName); + then(ctxMock).should().transformMsgOriginator(msg, ASSET_ID); ArgumentCaptor actualMsg = ArgumentCaptor.forClass(TbMsg.class); then(ctxMock).should().tellSuccess(actualMsg.capture()); assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); @@ -356,7 +333,7 @@ public class TbChangeOriginatorNodeTest { ArgumentCaptor throwable = ArgumentCaptor.forClass(Throwable.class); then(ctxMock).should().tellFailure(eq(msg), throwable.capture()); - assertThat(throwable.getValue()).isInstanceOf(IllegalStateException.class).hasMessage("Failed to found ASSET entity by name: 'test-asset'!"); + assertThat(throwable.getValue()).isInstanceOf(IllegalStateException.class).hasMessage("Failed to found asset with name 'test-asset'!"); } } From ec1c2e307c74b1fbb7233375eb9cdd1db3ffb27d Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 8 Aug 2024 12:13:02 +0300 Subject: [PATCH 05/14] used getNormalName() method instead of name() --- .../rule/engine/util/EntitiesByNameAndTypeLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesByNameAndTypeLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesByNameAndTypeLoader.java index 888faaaf71..542341c5b4 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesByNameAndTypeLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesByNameAndTypeLoader.java @@ -53,7 +53,7 @@ public class EntitiesByNameAndTypeLoader { throw new IllegalStateException("Unexpected entity type " + entityType.name()); } if (targetEntity == null) { - throw new IllegalStateException("Failed to found " + entityType.name().toLowerCase() + " with name '" + entityName + "'!"); + throw new IllegalStateException("Failed to found " + entityType.getNormalName().toLowerCase() + " with name '" + entityName + "'!"); } return targetEntity.getId(); } From 7977d504101341ad720352285edc937b604d5614 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 8 Aug 2024 17:33:53 +0300 Subject: [PATCH 06/14] added test for negative case to the checkpoint node --- .../rule/engine/flow/TbAckNodeTest.java | 1 - .../engine/flow/TbCheckpointNodeTest.java | 35 +++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java index 909f866493..49e92f2d74 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbAckNodeTest.java @@ -43,7 +43,6 @@ public class TbAckNodeTest { private EmptyNodeConfiguration config; private TbNodeConfiguration nodeConfiguration; - @Mock private TbContext ctxMock; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java index 9b6e86b9a6..e596c71d1e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/flow/TbCheckpointNodeTest.java @@ -22,6 +22,7 @@ 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.ValueSource; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; @@ -49,12 +50,13 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.spy; import static org.mockito.BDDMockito.then; -import static org.mockito.BDDMockito.willAnswer; @Slf4j @ExtendWith(MockitoExtension.class) public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { + private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("37840655-b7dc-4f49-8da3-9429159e0970")); + private TbCheckpointNode node; private EmptyNodeConfiguration config; private TbNodeConfiguration nodeConfiguration; @@ -80,24 +82,37 @@ public class TbCheckpointNodeTest extends AbstractRuleNodeUpgradeTest { } @ParameterizedTest - @ValueSource(strings = {DataConstants.MAIN_QUEUE_NAME, DataConstants.HP_QUEUE_NAME, DataConstants.HP_QUEUE_NAME, "Custom queue"}) + @ValueSource(strings = {DataConstants.MAIN_QUEUE_NAME, DataConstants.HP_QUEUE_NAME, DataConstants.SQ_QUEUE_NAME, "Custom queue"}) public void givenQueueName_whenOnMsg_thenTransfersMsgToDefinedQueue(String queueName) throws TbNodeException { given(ctxMock.getQueueName()).willReturn(queueName); - willAnswer(invocationOnMock -> { - Runnable onSuccess = invocationOnMock.getArgument(3); - onSuccess.run(); - return null; - }).given(ctxMock).enqueueForTellNext(any(TbMsg.class), any(String.class), any(String.class), any(Runnable.class), any(Consumer.class)); node.init(ctxMock, nodeConfiguration); - DeviceId deviceId = new DeviceId(UUID.fromString("2cd04871-7f07-41d1-b850-95dd444a6506")); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); node.onMsg(ctxMock, msg); - then(ctxMock).should().enqueueForTellNext(eq(msg), eq(queueName), eq(TbNodeConnectionType.SUCCESS), any(), any()); + ArgumentCaptor onSuccess = ArgumentCaptor.forClass(Runnable.class); + then(ctxMock).should().enqueueForTellNext(eq(msg), eq(queueName), eq(TbNodeConnectionType.SUCCESS), onSuccess.capture(), any()); + onSuccess.getValue().run(); then(ctxMock).should().ack(msg); } + @Test + public void givenErrorDuringTransfer_whenOnMsg_thenTellFailure() throws TbNodeException { + given(ctxMock.getQueueName()).willReturn(DataConstants.HP_QUEUE_NAME); + + node.init(ctxMock, nodeConfiguration); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + node.onMsg(ctxMock, msg); + + ArgumentCaptor> onFailure = ArgumentCaptor.forClass(Consumer.class); + then(ctxMock).should().enqueueForTellNext(eq(msg), eq(DataConstants.HP_QUEUE_NAME), eq(TbNodeConnectionType.SUCCESS), any(), onFailure.capture()); + String errorMsg = "Something went wrong."; + onFailure.getValue().accept(new RuntimeException(errorMsg)); + ArgumentCaptor throwable = ArgumentCaptor.forClass(Throwable.class); + then(ctxMock).should().tellFailure(eq(msg), throwable.capture()); + assertThat(throwable.getValue()).isInstanceOf(RuntimeException.class).hasMessage(errorMsg); + } + // Rule nodes upgrade private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { return Stream.of( From 46a147792e6e16c3ced729cd9bd7ab161db03cf8 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Fri, 9 Aug 2024 16:06:00 +0300 Subject: [PATCH 07/14] created enum for originator source values --- .../transform/ChangeOriginatorSource.java | 24 ++++++++++ .../transform/TbChangeOriginatorNode.java | 43 +++++------------ .../TbChangeOriginatorNodeConfiguration.java | 9 ++-- .../util/EntitiesByNameAndTypeLoader.java | 2 +- .../transform/TbChangeOriginatorNodeTest.java | 47 +++++++------------ 5 files changed, 58 insertions(+), 67 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/ChangeOriginatorSource.java diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/ChangeOriginatorSource.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/ChangeOriginatorSource.java new file mode 100644 index 0000000000..57b3ded5fa --- /dev/null +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/ChangeOriginatorSource.java @@ -0,0 +1,24 @@ +/** + * 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.transform; + +public enum ChangeOriginatorSource { + CUSTOMER, + TENANT, + RELATED, + ALARM_ORIGINATOR, + ENTITY +} diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java index f6f33d30c0..d04736461d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java @@ -15,7 +15,6 @@ */ package org.thingsboard.rule.engine.transform; -import com.google.common.collect.Sets; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; @@ -34,10 +33,12 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; -import java.util.HashSet; import java.util.List; import java.util.NoSuchElementException; +import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.ENTITY; +import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.RELATED; + @Slf4j @RuleNode( type = ComponentType.TRANSFORMATION, @@ -59,16 +60,6 @@ import java.util.NoSuchElementException; ) public class TbChangeOriginatorNode extends TbAbstractTransformNode { - private static final String CUSTOMER_SOURCE = "CUSTOMER"; - private static final String TENANT_SOURCE = "TENANT"; - private static final String RELATED_SOURCE = "RELATED"; - private static final String ALARM_ORIGINATOR_SOURCE = "ALARM_ORIGINATOR"; - private static final String ENTITY_SOURCE = "ENTITY"; - - private final String supportedOriginatorSourcesStr = String.join(", ", List.of( - CUSTOMER_SOURCE, TENANT_SOURCE, RELATED_SOURCE, ALARM_ORIGINATOR_SOURCE, ENTITY_SOURCE) - ); - @Override protected TbChangeOriginatorNodeConfiguration loadNodeConfiguration(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { var config = TbNodeUtils.convert(configuration, TbChangeOriginatorNodeConfiguration.class); @@ -89,15 +80,15 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode getNewOriginator(TbContext ctx, TbMsg msg) { switch (config.getOriginatorSource()) { - case CUSTOMER_SOURCE: + case CUSTOMER: return EntitiesCustomerIdAsyncLoader.findEntityIdAsync(ctx, msg.getOriginator()); - case TENANT_SOURCE: + case TENANT: return Futures.immediateFuture(ctx.getTenantId()); - case RELATED_SOURCE: + case RELATED: return EntitiesRelatedEntityIdAsyncLoader.findEntityAsync(ctx, msg.getOriginator(), config.getRelationsQuery()); - case ALARM_ORIGINATOR_SOURCE: + case ALARM_ORIGINATOR: return EntitiesAlarmOriginatorIdAsyncLoader.findEntityIdAsync(ctx, msg.getOriginator()); - case ENTITY_SOURCE: + case ENTITY: EntityType entityType = EntityType.valueOf(config.getEntityType()); String entityName = TbNodeUtils.processPattern(config.getEntityNamePattern(), msg); try { @@ -112,27 +103,19 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode knownSources = Sets.newHashSet(CUSTOMER_SOURCE, TENANT_SOURCE, RELATED_SOURCE, ALARM_ORIGINATOR_SOURCE, ENTITY_SOURCE); - if (!knownSources.contains(conf.getOriginatorSource())) { - log.error("Unsupported source type '{}'! Only {} types are allowed.", conf.getOriginatorSource(), supportedOriginatorSourcesStr); - throw new IllegalArgumentException("Unsupported source type '" + conf.getOriginatorSource() + - "'! Only " + supportedOriginatorSourcesStr + " types are allowed."); - } - - if (conf.getOriginatorSource().equals(RELATED_SOURCE)) { + if (conf.getOriginatorSource().equals(RELATED)) { if (conf.getRelationsQuery() == null) { - log.error("Relations query should be specified if 'Related entity' source is selected."); + log.debug("Relations query should be specified if 'Related entity' source is selected."); throw new IllegalArgumentException("Relations query should be specified if 'Related entity' source is selected."); } } - - if (conf.getOriginatorSource().equals(ENTITY_SOURCE)) { + if (conf.getOriginatorSource().equals(ENTITY)) { if (conf.getEntityType() == null) { - log.error("Entity type should be specified if '{}' source is selected.", ENTITY_SOURCE); + log.debug("Entity type should be specified if '{}' source is selected.", ENTITY); throw new IllegalArgumentException("Entity type should be specified if 'Entity by name pattern' source is selected."); } if (StringUtils.isEmpty(conf.getEntityNamePattern())) { - log.error("Name pattern should be specified if '{}' source is selected.", ENTITY_SOURCE); + log.debug("Name pattern should be specified if '{}' source is selected.", ENTITY); throw new IllegalArgumentException("Name pattern should be specified if 'Entity by name pattern' source is selected."); } EntitiesByNameAndTypeLoader.checkEntityType(EntityType.valueOf(conf.getEntityType())); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java index 6449f832cd..8fcf2e8ce1 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java @@ -24,13 +24,12 @@ import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; import java.util.Collections; +import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.CUSTOMER; + @Data public class TbChangeOriginatorNodeConfiguration implements NodeConfiguration { - private static final String CUSTOMER_SOURCE = "CUSTOMER"; - - private String originatorSource; - + private ChangeOriginatorSource originatorSource; private RelationsQuery relationsQuery; private String entityType; private String entityNamePattern; @@ -38,7 +37,7 @@ public class TbChangeOriginatorNodeConfiguration implements NodeConfiguration node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Unsupported source type 'UNSUPPORTED_SOURCE'! Only " + supportedOriginatorSourcesStr + " types are allowed."); - } - @Test public void givenRelatedSourceAndRelatedQueryIsNull_whenInit_thenThrowsException() { - config.setOriginatorSource(RELATED_SOURCE); + config.setOriginatorSource(RELATED); config.setRelationsQuery(null); assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) @@ -146,7 +131,7 @@ public class TbChangeOriginatorNodeTest { @Test public void givenEntitySourceAndEntityTypeIsNull_whenInit_thenThrowsException() { - config.setOriginatorSource(ENTITY_SOURCE); + config.setOriginatorSource(ENTITY); config.setEntityType(null); assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) @@ -157,7 +142,7 @@ public class TbChangeOriginatorNodeTest { @ParameterizedTest @NullAndEmptySource public void givenEntitySourceAndEntityNamePatternIsEmpty_whenInit_thenThrowsException(String entityName) { - config.setOriginatorSource(ENTITY_SOURCE); + config.setOriginatorSource(ENTITY); config.setEntityType(EntityType.DEVICE.name()); config.setEntityNamePattern(entityName); @@ -168,7 +153,7 @@ public class TbChangeOriginatorNodeTest { @Test public void givenEntitySourceAndUnexpectedEntityType_whenInit_thenThrowsException() { - config.setOriginatorSource(ENTITY_SOURCE); + config.setOriginatorSource(ENTITY); config.setEntityType(EntityType.TENANT.name()); config.setEntityNamePattern("tenant-A"); @@ -203,7 +188,7 @@ public class TbChangeOriginatorNodeTest { @Test public void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbNodeException { - config.setOriginatorSource(TENANT_SOURCE); + config.setOriginatorSource(TENANT); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, TENANT_ID); @@ -223,7 +208,7 @@ public class TbChangeOriginatorNodeTest { @Test public void givenOriginatorSourceIsRelatedAndNewOriginatorIsNull_whenOnMsg_thenTellFailure() throws TbNodeException { - config.setOriginatorSource(RELATED_SOURCE); + config.setOriginatorSource(RELATED); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); @@ -253,7 +238,7 @@ public class TbChangeOriginatorNodeTest { @Test public void givenOriginatorSourceIsAlarmOriginator_whenOnMsg_thenTellSuccess() throws TbNodeException { - config.setOriginatorSource(ALARM_ORIGINATOR_SOURCE); + config.setOriginatorSource(ALARM_ORIGINATOR); AlarmId alarmId = new AlarmId(UUID.fromString("6b43f694-cb5f-4199-9023-e9e40eeb82dd")); Alarm alarm = new Alarm(alarmId); @@ -281,7 +266,7 @@ public class TbChangeOriginatorNodeTest { @ParameterizedTest @MethodSource public void givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess(String entityNamePattern, TbMsgMetaData metaData, String data) throws TbNodeException { - config.setOriginatorSource(ENTITY_SOURCE); + config.setOriginatorSource(ENTITY); config.setEntityType(EntityType.ASSET.name()); config.setEntityNamePattern(entityNamePattern); @@ -315,7 +300,7 @@ public class TbChangeOriginatorNodeTest { @Test public void givenOriginatorSourceIsEntityAndEntityCouldNotFound_whenOnMsg_thenTellFailure() throws TbNodeException { - config.setOriginatorSource(ENTITY_SOURCE); + config.setOriginatorSource(ENTITY); config.setEntityType(EntityType.ASSET.name()); config.setEntityNamePattern("${md-name-pattern}"); @@ -333,7 +318,7 @@ public class TbChangeOriginatorNodeTest { ArgumentCaptor throwable = ArgumentCaptor.forClass(Throwable.class); then(ctxMock).should().tellFailure(eq(msg), throwable.capture()); - assertThat(throwable.getValue()).isInstanceOf(IllegalStateException.class).hasMessage("Failed to found asset with name 'test-asset'!"); + assertThat(throwable.getValue()).isInstanceOf(IllegalStateException.class).hasMessage("Failed to find asset with name 'test-asset'!"); } } From feb51793b18e247d654460e5e3c68f740206e733 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Tue, 13 Aug 2024 10:47:22 +0300 Subject: [PATCH 08/14] added check for originator source for null --- .../engine/transform/TbChangeOriginatorNode.java | 12 +++++++----- .../engine/transform/TbChangeOriginatorNodeTest.java | 9 +++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java index d04736461d..522756c880 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java @@ -103,11 +103,13 @@ public class TbChangeOriginatorNode extends TbAbstractTransformNode node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Originator source should be specified."); + } + @Test public void givenRelatedSourceAndRelatedQueryIsNull_whenInit_thenThrowsException() { config.setOriginatorSource(RELATED); From 92cabebb04328c5e51f9cc9611dbb141dcb2dfe6 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 14 Aug 2024 11:52:13 +0300 Subject: [PATCH 09/14] Fix unrecognized Kafka config 'session.timeout.ms' --- .../org/thingsboard/server/queue/kafka/TbKafkaSettings.java | 3 +-- .../thingsboard/server/queue/kafka/TbKafkaSettingsTest.java | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java index 760487c61e..e03266b3e6 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaSettings.java @@ -151,6 +151,7 @@ public class TbKafkaSettings { Properties props = toProps(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, servers); props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords); + props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, sessionTimeoutMs); props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, maxPartitionFetchBytes); props.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, fetchMaxBytes); props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, maxPollIntervalMs); @@ -193,8 +194,6 @@ public class TbKafkaSettings { } props.put(CommonClientConfigs.REQUEST_TIMEOUT_MS_CONFIG, requestTimeoutMs); - props.put(CommonClientConfigs.SESSION_TIMEOUT_MS_CONFIG, sessionTimeoutMs); - props.putAll(PropertyUtils.getProps(otherInline)); if (other != null) { diff --git a/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaSettingsTest.java b/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaSettingsTest.java index 23ab877379..3abeadbe60 100644 --- a/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaSettingsTest.java +++ b/common/queue/src/test/java/org/thingsboard/server/queue/kafka/TbKafkaSettingsTest.java @@ -49,7 +49,6 @@ class TbKafkaSettingsTest { Properties props = settings.toProps(); assertThat(props).as("TB_QUEUE_KAFKA_REQUEST_TIMEOUT_MS").containsEntry("request.timeout.ms", 30000); - assertThat(props).as("TB_QUEUE_KAFKA_SESSION_TIMEOUT_MS").containsEntry("session.timeout.ms", 10000); //other-inline assertThat(props).as("metrics.recording.level").containsEntry("metrics.recording.level", "INFO"); From 0f4037155d12edb837bbafc6ed06e55c68fc59ef Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Wed, 14 Aug 2024 15:50:21 +0300 Subject: [PATCH 10/14] fixed swagger description --- .../org/thingsboard/server/controller/AdminController.java | 6 +++--- .../server/common/data/objects/AttributesEntityView.java | 6 +++--- .../server/common/data/objects/TelemetryEntityView.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index 65903a4dec..e4e6418168 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -137,7 +137,7 @@ public class AdminController extends BaseController { return adminSettings; } - @ApiOperation(value = "Get the Administration Settings object using key (getAdminSettings)", + @ApiOperation(value = "Creates or Updates the Administration Settings (saveAdminSettings)", notes = "Creates or Updates the Administration Settings. Platform generates random Administration Settings Id during settings creation. " + "The Administration Settings Id will be present in the response. Specify the Administration Settings Id when you would like to update the Administration Settings. " + "Referencing non-existing Administration Settings Id will cause an error." + SYSTEM_AUTHORITY_PARAGRAPH) @@ -160,7 +160,7 @@ public class AdminController extends BaseController { return adminSettings; } - @ApiOperation(value = "Get the Security Settings object", + @ApiOperation(value = "Get the Security Settings object (getSecuritySettings)", notes = "Get the Security Settings object that contains password policy, etc." + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") @RequestMapping(value = "/securitySettings", method = RequestMethod.GET) @@ -237,7 +237,7 @@ public class AdminController extends BaseController { } } - @ApiOperation(value = "Send test sms (sendTestMail)", + @ApiOperation(value = "Send test sms (sendTestSms)", notes = "Attempts to send test sms to the System Administrator User using SMS Settings and phone number provided as a parameters of the request. " + SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAuthority('SYS_ADMIN')") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java index 309385d5e4..c0caa68e3a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/objects/AttributesEntityView.java @@ -31,11 +31,11 @@ import java.util.List; @NoArgsConstructor public class AttributesEntityView implements Serializable { - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "List of client-side attribute keys to expose", example = "currentConfiguration") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "List of client-side attribute keys to expose", example = "[\"currentConfiguration\"]") private List cs = new ArrayList<>(); - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "List of server-side attribute keys to expose", example = "model") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "List of server-side attribute keys to expose", example = "[\"model\"]") private List ss = new ArrayList<>(); - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "List of shared attribute keys to expose", example = "targetConfiguration") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "List of shared attribute keys to expose", example = "[\"targetConfiguration\"]") private List sh = new ArrayList<>(); public AttributesEntityView(List cs, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java b/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java index a2484b0707..999db8178a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/objects/TelemetryEntityView.java @@ -31,7 +31,7 @@ import java.util.List; @NoArgsConstructor public class TelemetryEntityView implements Serializable { - @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "List of time-series data keys to expose", example = "temperature, humidity") + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "List of time-series data keys to expose", example = "[\"temperature\", \"humidity\"]") private List timeseries; @Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "JSON object with attributes to expose") private AttributesEntityView attributes; From 79e8b2e0bbb21f96c006df2ee070e2da40d4d46f Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 15 Aug 2024 11:20:07 +0200 Subject: [PATCH 11/14] used forward headers strategy 'framework' by default --- application/src/main/resources/thingsboard.yml | 4 ++-- msa/tb-node/docker/Dockerfile | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 19af8a8537..1f0a5d2bdd 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -20,8 +20,8 @@ server: address: "${HTTP_BIND_ADDRESS:0.0.0.0}" # Server bind port port: "${HTTP_BIND_PORT:8080}" - # Server forward headers strategy - forward_headers_strategy: "${HTTP_FORWARD_HEADERS_STRATEGY:NONE}" + # Server forward headers strategy. Required for SWAGGER UI when reverse proxy is used + forward_headers_strategy: "${HTTP_FORWARD_HEADERS_STRATEGY:framework}" # Server SSL configuration ssl: # Enable/disable SSL support diff --git a/msa/tb-node/docker/Dockerfile b/msa/tb-node/docker/Dockerfile index 247edd8d9d..92e4893946 100644 --- a/msa/tb-node/docker/Dockerfile +++ b/msa/tb-node/docker/Dockerfile @@ -18,9 +18,6 @@ FROM thingsboard/openjdk17:bookworm-slim COPY start-tb-node.sh ${pkg.name}.deb /tmp/ -# Required for SWAGGER UI when reverse proxy is used -ENV HTTP_FORWARD_HEADERS_STRATEGY=framework - RUN chmod a+x /tmp/*.sh \ && mv /tmp/start-tb-node.sh /usr/bin && \ (yes | dpkg -i /tmp/${pkg.name}.deb) && \ From a9d211ffeedb219a0a9041c408c712b1300e9335 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Tue, 20 Aug 2024 09:53:37 +0300 Subject: [PATCH 12/14] UI: Update widget bundle for tank scada symbol --- .../scada_water_system_symbols.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json b/application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json index 224938c019..1ec69037ae 100644 --- a/application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json +++ b/application/src/main/data/json/system/widget_bundles/scada_water_system_symbols.json @@ -47,6 +47,21 @@ "vertical_wheel_valve", "horizontal_ball_valve", "vertical_ball_valve", - "vertical_tank" + "vertical_tank", + "stand_vertical_tank", + "cylindrical_tank", + "stand_cylindrical_tank", + "vertical_short_tank", + "stand_vertical_short_tank", + "large_cylindrical_tank", + "large_stand_cylindrical_tank", + "large_vertical_tank", + "large_stand_vertical_tank", + "horizontal_tank", + "stand_horizontal_tank", + "spherical_tank", + "small_spherical_tank", + "elevated_tank", + "pool" ] } \ No newline at end of file From 7da08551806b3c9ec15ac58afb8f738eb2af2a38 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Wed, 21 Aug 2024 12:10:07 +0300 Subject: [PATCH 13/14] renamed enum --- ...angeOriginatorSource.java => OriginatorSource.java} | 2 +- .../rule/engine/transform/TbChangeOriginatorNode.java | 4 ++-- .../transform/TbChangeOriginatorNodeConfiguration.java | 4 ++-- .../engine/transform/TbChangeOriginatorNodeTest.java | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) rename rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/{ChangeOriginatorSource.java => OriginatorSource.java} (95%) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/ChangeOriginatorSource.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/OriginatorSource.java similarity index 95% rename from rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/ChangeOriginatorSource.java rename to rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/OriginatorSource.java index 57b3ded5fa..2ae5402404 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/ChangeOriginatorSource.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/OriginatorSource.java @@ -15,7 +15,7 @@ */ package org.thingsboard.rule.engine.transform; -public enum ChangeOriginatorSource { +public enum OriginatorSource { CUSTOMER, TENANT, RELATED, diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java index 522756c880..d590bbbb75 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java @@ -36,8 +36,8 @@ import org.thingsboard.server.common.msg.TbMsg; import java.util.List; import java.util.NoSuchElementException; -import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.ENTITY; -import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.RELATED; +import static org.thingsboard.rule.engine.transform.OriginatorSource.ENTITY; +import static org.thingsboard.rule.engine.transform.OriginatorSource.RELATED; @Slf4j @RuleNode( diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java index 8fcf2e8ce1..76d42c2ef0 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeConfiguration.java @@ -24,12 +24,12 @@ import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; import java.util.Collections; -import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.CUSTOMER; +import static org.thingsboard.rule.engine.transform.OriginatorSource.CUSTOMER; @Data public class TbChangeOriginatorNodeConfiguration implements NodeConfiguration { - private ChangeOriginatorSource originatorSource; + private OriginatorSource originatorSource; private RelationsQuery relationsQuery; private String entityType; private String entityNamePattern; diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java index 9334f9ccb1..098fe3efda 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNodeTest.java @@ -69,11 +69,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; -import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.ALARM_ORIGINATOR; -import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.CUSTOMER; -import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.ENTITY; -import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.RELATED; -import static org.thingsboard.rule.engine.transform.ChangeOriginatorSource.TENANT; +import static org.thingsboard.rule.engine.transform.OriginatorSource.ALARM_ORIGINATOR; +import static org.thingsboard.rule.engine.transform.OriginatorSource.CUSTOMER; +import static org.thingsboard.rule.engine.transform.OriginatorSource.ENTITY; +import static org.thingsboard.rule.engine.transform.OriginatorSource.RELATED; +import static org.thingsboard.rule.engine.transform.OriginatorSource.TENANT; @ExtendWith(MockitoExtension.class) public class TbChangeOriginatorNodeTest { From 92c09243ef049328065fdb6eaa3d78d3f4aaa345 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 21 Aug 2024 12:42:20 +0300 Subject: [PATCH 14/14] Fix transactionalCache.getOrFetchFromDB --- .../org/thingsboard/server/cache/TbTransactionalCache.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java index 89e6754d19..765c82a8db 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java @@ -53,7 +53,7 @@ public interface TbTransactionalCache cacheValueWrapper = get(key); + TbCacheValueWrapper cacheValueWrapper = get(key, true); if (cacheValueWrapper != null) { return cacheValueWrapper.get(); } @@ -92,7 +92,7 @@ public interface TbTransactionalCache cacheValueWrapper = get(key); + TbCacheValueWrapper cacheValueWrapper = get(key, true); if (cacheValueWrapper != null) { var cacheValue = cacheValueWrapper.get(); return cacheValue == null ? null : cacheValueToResult.apply(cacheValue);