From 59d75bf2f131fe091d33f9e519c51a2fb8bfed64 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 11 Apr 2024 19:27:09 +0300 Subject: [PATCH 01/16] added tests for send rpc request and reply nodes --- .../engine/rpc/TbSendRPCReplyNodeTest.java | 91 +++++++-- .../engine/rpc/TbSendRPCRequestNodeTest.java | 184 ++++++++++++++++++ 2 files changed, 262 insertions(+), 13 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java index 454c1a9fe6..f628db07e6 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java @@ -16,12 +16,14 @@ package org.thingsboard.rule.engine.rpc; import com.google.common.util.concurrent.SettableFuture; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +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.EnumSource; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ListeningExecutor; import org.thingsboard.rule.engine.api.RuleEngineRpcService; @@ -29,7 +31,9 @@ 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.DataConstants; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; @@ -39,11 +43,14 @@ import org.thingsboard.server.dao.edge.EdgeEventService; import java.util.UUID; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class TbSendRPCReplyNodeTest { private static final String DUMMY_SERVICE_ID = "testServiceId"; @@ -68,7 +75,7 @@ public class TbSendRPCReplyNodeTest { @Mock private ListeningExecutor listeningExecutor; - @Before + @BeforeEach public void setUp() throws TbNodeException { node = new TbSendRPCReplyNode(); TbSendRpcReplyNodeConfiguration config = new TbSendRpcReplyNodeConfiguration().defaultConfiguration(); @@ -77,8 +84,7 @@ public class TbSendRPCReplyNodeTest { @Test public void sendReplyToTransport() { - Mockito.when(ctx.getRpcService()).thenReturn(rpcService); - + when(ctx.getRpcService()).thenReturn(rpcService); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, getDefaultMetadata(), TbMsgDataType.JSON, DUMMY_DATA, null, null); @@ -91,10 +97,10 @@ public class TbSendRPCReplyNodeTest { @Test public void sendReplyToEdgeQueue() { - Mockito.when(ctx.getTenantId()).thenReturn(tenantId); - Mockito.when(ctx.getEdgeEventService()).thenReturn(edgeEventService); - Mockito.when(edgeEventService.saveAsync(any())).thenReturn(SettableFuture.create()); - Mockito.when(ctx.getDbCallbackExecutor()).thenReturn(listeningExecutor); + when(ctx.getTenantId()).thenReturn(tenantId); + when(ctx.getEdgeEventService()).thenReturn(edgeEventService); + when(edgeEventService.saveAsync(any())).thenReturn(SettableFuture.create()); + when(ctx.getDbCallbackExecutor()).thenReturn(listeningExecutor); TbMsgMetaData defaultMetadata = getDefaultMetadata(); defaultMetadata.putValue(DataConstants.EDGE_ID, UUID.randomUUID().toString()); @@ -108,6 +114,65 @@ public class TbSendRPCReplyNodeTest { verify(rpcService, never()).sendRpcReplyToDevice(DUMMY_SERVICE_ID, DUMMY_SESSION_ID, DUMMY_REQUEST_ID, DUMMY_DATA); } + @ParameterizedTest + @EnumSource(EntityType.class) + public void testOriginatorEntityTypes(EntityType entityType) throws TbNodeException { + if (entityType == EntityType.DEVICE) return; + EntityId entityId = new EntityId() { + @Override + public UUID getId() { + return UUID.randomUUID(); + } + + @Override + public EntityType getEntityType() { + return entityType; + } + }; + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + node.onMsg(ctx, msg); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); + verify(ctx).tellFailure(eq(msg), captor.capture()); + Throwable value = captor.getValue(); + assertThat(value.getClass()).isEqualTo(RuntimeException.class); + assertThat(value.getMessage()).isEqualTo("Message originator is not a device entity!"); + } + + @Test + public void testForAvailabilityOfMetadataAndDataValues2() { + //without requestId + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + verifyFailure(msg, "Request id is not present in the metadata!"); + + //without serviceId + TbMsgMetaData metadata = new TbMsgMetaData(); + metadata.putValue("requestId", Integer.toString(DUMMY_REQUEST_ID)); + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, metadata, TbMsg.EMPTY_JSON_OBJECT); + verifyFailure(msg, "Service id is not present in the metadata!"); + + //without sessionId + metadata.putValue("serviceId", DUMMY_SERVICE_ID); + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, metadata, TbMsg.EMPTY_JSON_OBJECT); + verifyFailure(msg, "Session id is not present in the metadata!"); + + //with empty data + metadata.putValue("sessionId", DUMMY_SESSION_ID.toString()); + msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, metadata, TbMsg.EMPTY_STRING); + verifyFailure(msg, "Request body is empty!"); + } + + private void verifyFailure(TbMsg msg, String expectedErrorMessage) { + node.onMsg(ctx, msg); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); + verify(ctx).tellFailure(eq(msg), captor.capture()); + Throwable value = captor.getValue(); + assertThat(value.getClass()).isEqualTo(RuntimeException.class); + assertThat(value.getMessage()).isEqualTo(expectedErrorMessage); + } + private TbMsgMetaData getDefaultMetadata() { TbSendRpcReplyNodeConfiguration config = new TbSendRpcReplyNodeConfiguration().defaultConfiguration(); TbMsgMetaData metadata = new TbMsgMetaData(); diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java new file mode 100644 index 0000000000..0a2f0a0bf8 --- /dev/null +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java @@ -0,0 +1,184 @@ +/** + * 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.rpc; + +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.EnumSource; +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; +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcResponse; +import org.thingsboard.rule.engine.api.RuleEngineRpcService; +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.EntityType; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.msg.TbMsgType; +import org.thingsboard.server.common.data.msg.TbNodeConnectionType; +import org.thingsboard.server.common.data.rpc.RpcError; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgMetaData; + +import java.util.Optional; +import java.util.UUID; +import java.util.function.Consumer; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class TbSendRPCRequestNodeTest { + + private TbSendRPCRequestNode node; + private TbSendRpcRequestNodeConfiguration config; + + @Mock + private TbContext ctxMock; + @Mock + private RuleEngineRpcService rpcServiceMock; + + @BeforeEach + public void setUp() throws TbNodeException { + node = new TbSendRPCRequestNode(); + config = new TbSendRpcRequestNodeConfiguration().defaultConfiguration(); + var configuration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); + node.init(ctxMock, configuration); + } + + @Test + public void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { + DeviceId deviceId = new DeviceId(UUID.fromString("dda00a40-9d9c-4464-a759-488b9617319c")); + TenantId tenantId = new TenantId(UUID.fromString("81622599-afb3-4b52-9b47-f930f11ee963")); + String data = """ + { + "method": "setGpio", + "params": { + "pin": "23", + "value": 1 + } + } + """; + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, deviceId, TbMsgMetaData.EMPTY, data); + TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + when(ctxMock.getRpcService()).thenReturn(rpcServiceMock); + when(ctxMock.getTenantId()).thenReturn(tenantId); + when(ctxMock.newMsg(any(), any(String.class), any(), any(), any(), any())).thenReturn(outMsg); + + doAnswer(invocation -> { + Consumer callback = invocation.getArgument(1); + RuleEngineDeviceRpcResponse rpcResponseMock = mock(RuleEngineDeviceRpcResponse.class); + when(rpcResponseMock.getError()).thenReturn(Optional.empty()); + when(rpcResponseMock.getResponse()).thenReturn(Optional.of(TbMsg.EMPTY_JSON_OBJECT)); + callback.accept(rpcResponseMock); + return null; + }).when(rpcServiceMock).sendRpcRequestToDevice(any(), any()); + + node.onMsg(ctxMock, msg); + + verify(ctxMock).enqueueForTellNext(eq(outMsg), eq(TbNodeConnectionType.SUCCESS)); + verify(ctxMock).ack(eq(msg)); + } + + @Test + public void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { + DeviceId deviceId = new DeviceId(UUID.fromString("dda00a40-9d9c-4464-a759-488b9617319c")); + TenantId tenantId = new TenantId(UUID.fromString("81622599-afb3-4b52-9b47-f930f11ee963")); + String data = """ + { + "method": "setGpio", + "params": { + "pin": "23", + "value": 1 + } + } + """; + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, deviceId, TbMsgMetaData.EMPTY, data); + TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + when(ctxMock.getRpcService()).thenReturn(rpcServiceMock); + when(ctxMock.getTenantId()).thenReturn(tenantId); + when(ctxMock.newMsg(any(), any(String.class), any(), any(), any(), any())).thenReturn(outMsg); + + doAnswer(invocation -> { + Consumer callback = invocation.getArgument(1); + RuleEngineDeviceRpcResponse rpcResponseMock = mock(RuleEngineDeviceRpcResponse.class); + when(rpcResponseMock.getError()).thenReturn(Optional.of(RpcError.NO_ACTIVE_CONNECTION)); + callback.accept(rpcResponseMock); + return null; + }).when(rpcServiceMock).sendRpcRequestToDevice(any(), any()); + + node.onMsg(ctxMock, msg); + + verify(ctxMock).enqueueForTellFailure(eq(outMsg), eq("NO_ACTIVE_CONNECTION")); + verify(ctxMock).ack(eq(msg)); + } + + @ParameterizedTest + @EnumSource(EntityType.class) + public void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType entityType) { + if (entityType == EntityType.DEVICE) return; + EntityId entityId = new EntityId() { + @Override + public UUID getId() { + return UUID.randomUUID(); + } + + @Override + public EntityType getEntityType() { + return entityType; + } + }; + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + node.onMsg(ctxMock, msg); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); + verify(ctxMock).tellFailure(eq(msg), captor.capture()); + Throwable value = captor.getValue(); + assertThat(value.getClass()).isEqualTo(RuntimeException.class); + assertThat(value.getMessage()).isEqualTo("Message originator is not a device entity!"); + } + + @ParameterizedTest + @ValueSource(strings = {"method", "params"}) + public void givenMethodOrParamsAreNotPresent_whenOnMsg_thenThrowsException(String key) { + DeviceId deviceId = new DeviceId(UUID.randomUUID()); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, "{\"" + key + "\": \"value\"}"); + + node.onMsg(ctxMock, msg); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); + verify(ctxMock).tellFailure(eq(msg), captor.capture()); + Throwable value = captor.getValue(); + assertThat(value.getClass()).isEqualTo(RuntimeException.class); + assertThat(value.getMessage()).isEqualTo( + key.equals("method") ? "Params are not present in the message!" : "Method is not present in the message!"); + } +} From 15defc34c3e50fe4b8becf41f5e02299511ceb6b Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Tue, 28 May 2024 09:46:38 +0300 Subject: [PATCH 02/16] removed deprecated useRedisQueueForMessagePersistence parameter from config --- .../rule/engine/rest/TbRestApiCallNode.java | 10 +-- .../rest/TbRestApiCallNodeConfiguration.java | 2 - .../engine/rest/TbRestApiCallNodeTest.java | 64 +++++++++++++------ 3 files changed, 52 insertions(+), 24 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java index f0cb102778..4b34131975 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java @@ -33,7 +33,7 @@ import org.thingsboard.server.common.msg.TbMsg; type = ComponentType.EXTERNAL, name = "rest api call", configClazz = TbRestApiCallNodeConfiguration.class, - version = 1, + version = 2, nodeDescription = "Invoke REST API calls to external REST server", nodeDetails = "Will invoke REST API call GET | POST | PUT | DELETE to external REST server. " + "Message payload added into Request body. Configured attributes can be added into Headers from Message Metadata." + @@ -58,9 +58,6 @@ public class TbRestApiCallNode extends TbAbstractExternalNode { super.init(ctx); TbRestApiCallNodeConfiguration config = TbNodeUtils.convert(configuration, TbRestApiCallNodeConfiguration.class); httpClient = new TbHttpClient(config, ctx.getSharedEventLoop()); - if (config.isUseRedisQueueForMsgPersistence()) { - log.warn("[{}][{}] Usage of Redis Template is deprecated starting 2.5 and will have no affect", ctx.getTenantId(), ctx.getSelfId()); - } } @Override @@ -88,6 +85,11 @@ public class TbRestApiCallNode extends TbAbstractExternalNode { ((ObjectNode) oldConfiguration).put(PARSE_TO_PLAIN_TEXT, oldConfiguration.get(TRIM_DOUBLE_QUOTES).booleanValue()); ((ObjectNode) oldConfiguration).remove(TRIM_DOUBLE_QUOTES); } + case 1: + if (oldConfiguration.has("useRedisQueueForMsgPersistence")) { + hasChanges = true; + ((ObjectNode) oldConfiguration).remove("useRedisQueueForMsgPersistence"); + } break; default: break; diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java index 7f159bd6a9..7d2ff7167d 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeConfiguration.java @@ -36,7 +36,6 @@ public class TbRestApiCallNodeConfiguration implements NodeConfiguration upgrade = node.upgrade(0, configJson); - Assertions.assertTrue(upgrade.getFirst()); - Assertions.assertTrue(JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass()).isParseToPlainText()); + private static Stream givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() { + return Stream.of( + // config for version 2 with upgrade from version 0 + Arguments.of(0, + "{\"restEndpointUrlPattern\":\"http://localhost/api\",\"requestMethod\":\"POST\"," + + "\"useSimpleClientHttpFactory\":false,\"ignoreRequestBody\":false,\"enableProxy\":false," + + "\"useSystemProxyProperties\":false,\"proxyScheme\":null,\"proxyHost\":null,\"proxyPort\":0," + + "\"proxyUser\":null,\"proxyPassword\":null,\"readTimeoutMs\":0,\"maxParallelRequestsCount\":0," + + "\"headers\":{\"Content-Type\":\"application/json\"},\"useRedisQueueForMsgPersistence\":false," + + "\"trimQueue\":null,\"maxQueueSize\":null,\"credentials\":{\"type\":\"anonymous\"},\"trimDoubleQuotes\":false}", + true, + "{\"restEndpointUrlPattern\":\"http://localhost/api\",\"requestMethod\": \"POST\"," + + "\"useSimpleClientHttpFactory\": false,\"parseToPlainText\": false,\"ignoreRequestBody\": false," + + "\"enableProxy\": false,\"useSystemProxyProperties\": false,\"proxyScheme\": null,\"proxyHost\": null," + + "\"proxyPort\": 0,\"proxyUser\": null,\"proxyPassword\": null,\"readTimeoutMs\": 0," + + "\"maxParallelRequestsCount\": 0,\"headers\": {\"Content-Type\": \"application/json\"}," + + "\"trimQueue\": null,\"maxQueueSize\": null,\"credentials\": {\"type\": \"anonymous\"}}"), + // config for version 2 with upgrade from version 1 + Arguments.of(1, + "{\"restEndpointUrlPattern\":\"http://localhost/api\",\"requestMethod\": \"POST\"," + + "\"useSimpleClientHttpFactory\": false,\"parseToPlainText\": false,\"ignoreRequestBody\": false," + + "\"enableProxy\": false,\"useSystemProxyProperties\": false,\"proxyScheme\": null,\"proxyHost\": null," + + "\"proxyPort\": 0,\"proxyUser\": null,\"proxyPassword\": null,\"readTimeoutMs\": 0," + + "\"maxParallelRequestsCount\": 0,\"headers\": {\"Content-Type\": \"application/json\"}," + + "\"useRedisQueueForMsgPersistence\": false,\"trimQueue\": null,\"maxQueueSize\": null," + + "\"credentials\": {\"type\": \"anonymous\"}}", + true, + "{\"restEndpointUrlPattern\":\"http://localhost/api\",\"requestMethod\": \"POST\"," + + "\"useSimpleClientHttpFactory\": false,\"parseToPlainText\": false,\"ignoreRequestBody\": false," + + "\"enableProxy\": false,\"useSystemProxyProperties\": false,\"proxyScheme\": null,\"proxyHost\": null," + + "\"proxyPort\": 0,\"proxyUser\": null,\"proxyPassword\": null,\"readTimeoutMs\": 0," + + "\"maxParallelRequestsCount\": 0,\"headers\": {\"Content-Type\": \"application/json\"}," + + "\"trimQueue\": null,\"maxQueueSize\": null,\"credentials\": {\"type\": \"anonymous\"}}") + ); + } + + @Override + protected TbNode getTestNode() { + return restNode; } } From 7de6bb764783de2f86d52d4a65c2d5ba99a42f0d Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Tue, 28 May 2024 11:08:23 +0300 Subject: [PATCH 03/16] removed trimQueue and maxQueueSize parameters --- .../org/thingsboard/rule/engine/rest/TbRestApiCallNode.java | 4 +++- .../thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java index 4b34131975..81de1ded43 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java @@ -28,6 +28,8 @@ import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.util.TbPair; import org.thingsboard.server.common.msg.TbMsg; +import java.util.List; + @Slf4j @RuleNode( type = ComponentType.EXTERNAL, @@ -88,7 +90,7 @@ public class TbRestApiCallNode extends TbAbstractExternalNode { case 1: if (oldConfiguration.has("useRedisQueueForMsgPersistence")) { hasChanges = true; - ((ObjectNode) oldConfiguration).remove("useRedisQueueForMsgPersistence"); + ((ObjectNode) oldConfiguration).remove(List.of("useRedisQueueForMsgPersistence", "trimQueue", "maxQueueSize")); } break; default: diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java index 6feaf7d5d9..635bfac356 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rest/TbRestApiCallNodeTest.java @@ -233,7 +233,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { "\"enableProxy\": false,\"useSystemProxyProperties\": false,\"proxyScheme\": null,\"proxyHost\": null," + "\"proxyPort\": 0,\"proxyUser\": null,\"proxyPassword\": null,\"readTimeoutMs\": 0," + "\"maxParallelRequestsCount\": 0,\"headers\": {\"Content-Type\": \"application/json\"}," + - "\"trimQueue\": null,\"maxQueueSize\": null,\"credentials\": {\"type\": \"anonymous\"}}"), + "\"credentials\": {\"type\": \"anonymous\"}}"), // config for version 2 with upgrade from version 1 Arguments.of(1, "{\"restEndpointUrlPattern\":\"http://localhost/api\",\"requestMethod\": \"POST\"," + @@ -249,7 +249,7 @@ public class TbRestApiCallNodeTest extends AbstractRuleNodeUpgradeTest { "\"enableProxy\": false,\"useSystemProxyProperties\": false,\"proxyScheme\": null,\"proxyHost\": null," + "\"proxyPort\": 0,\"proxyUser\": null,\"proxyPassword\": null,\"readTimeoutMs\": 0," + "\"maxParallelRequestsCount\": 0,\"headers\": {\"Content-Type\": \"application/json\"}," + - "\"trimQueue\": null,\"maxQueueSize\": null,\"credentials\": {\"type\": \"anonymous\"}}") + "\"credentials\": {\"type\": \"anonymous\"}}") ); } From ba4fb71486d5082a37cf75c3a9d64d730a4128cf Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Tue, 28 May 2024 15:34:43 +0300 Subject: [PATCH 04/16] refactored tests --- .../engine/rpc/TbSendRPCReplyNodeTest.java | 65 ++++++----- .../engine/rpc/TbSendRPCRequestNodeTest.java | 101 +++++++++--------- 2 files changed, 79 insertions(+), 87 deletions(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java index f628db07e6..17d53802a1 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java @@ -20,7 +20,9 @@ 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.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -41,7 +43,9 @@ import org.thingsboard.server.common.msg.TbMsgDataType; import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.edge.EdgeEventService; +import java.util.Map; import java.util.UUID; +import java.util.stream.Stream; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -116,7 +120,7 @@ public class TbSendRPCReplyNodeTest { @ParameterizedTest @EnumSource(EntityType.class) - public void testOriginatorEntityTypes(EntityType entityType) throws TbNodeException { + public void testOriginatorEntityTypes(EntityType entityType) { if (entityType == EntityType.DEVICE) return; EntityId entityId = new EntityId() { @Override @@ -133,44 +137,37 @@ public class TbSendRPCReplyNodeTest { node.onMsg(ctx, msg); - ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); - verify(ctx).tellFailure(eq(msg), captor.capture()); - Throwable value = captor.getValue(); - assertThat(value.getClass()).isEqualTo(RuntimeException.class); - assertThat(value.getMessage()).isEqualTo("Message originator is not a device entity!"); + ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); + verify(ctx).tellFailure(eq(msg), throwableCaptor.capture()); + assertThat(throwableCaptor.getValue()).isInstanceOf(RuntimeException.class) + .hasMessage("Message originator is not a device entity!"); } - @Test - public void testForAvailabilityOfMetadataAndDataValues2() { - //without requestId - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); - verifyFailure(msg, "Request id is not present in the metadata!"); - - //without serviceId - TbMsgMetaData metadata = new TbMsgMetaData(); - metadata.putValue("requestId", Integer.toString(DUMMY_REQUEST_ID)); - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, metadata, TbMsg.EMPTY_JSON_OBJECT); - verifyFailure(msg, "Service id is not present in the metadata!"); - - //without sessionId - metadata.putValue("serviceId", DUMMY_SERVICE_ID); - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, metadata, TbMsg.EMPTY_JSON_OBJECT); - verifyFailure(msg, "Session id is not present in the metadata!"); - - //with empty data - metadata.putValue("sessionId", DUMMY_SESSION_ID.toString()); - msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, metadata, TbMsg.EMPTY_STRING); - verifyFailure(msg, "Request body is empty!"); - } + @ParameterizedTest + @MethodSource + public void testForAvailabilityOfMetadataAndDataValues(TbMsgMetaData metaData, String errorMsg) { + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, metaData, TbMsg.EMPTY_STRING); - private void verifyFailure(TbMsg msg, String expectedErrorMessage) { node.onMsg(ctx, msg); - ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); - verify(ctx).tellFailure(eq(msg), captor.capture()); - Throwable value = captor.getValue(); - assertThat(value.getClass()).isEqualTo(RuntimeException.class); - assertThat(value.getMessage()).isEqualTo(expectedErrorMessage); + ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); + verify(ctx).tellFailure(eq(msg), throwableCaptor.capture()); + assertThat(throwableCaptor.getValue()).isInstanceOf(RuntimeException.class).hasMessage(errorMsg); + } + + private static Stream testForAvailabilityOfMetadataAndDataValues() { + return Stream.of( + Arguments.of(TbMsgMetaData.EMPTY, "Request id is not present in the metadata!"), + Arguments.of(new TbMsgMetaData(Map.of( + "requestId", Integer.toString(DUMMY_REQUEST_ID))), "Service id is not present in the metadata!"), + Arguments.of(new TbMsgMetaData(Map.of( + "requestId", Integer.toString(DUMMY_REQUEST_ID), + "serviceId", DUMMY_SERVICE_ID)), "Session id is not present in the metadata!"), + Arguments.of(new TbMsgMetaData(Map.of( + "requestId", Integer.toString(DUMMY_REQUEST_ID), + "serviceId", DUMMY_SERVICE_ID, "sessionId", + DUMMY_SESSION_ID.toString())), "Request body is empty!") + ); } private TbMsgMetaData getDefaultMetadata() { diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java index 0a2f0a0bf8..969aecb4d5 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java @@ -25,6 +25,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest; import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcResponse; import org.thingsboard.rule.engine.api.RuleEngineRpcService; import org.thingsboard.rule.engine.api.TbContext; @@ -54,10 +55,12 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class TbSendRPCRequestNodeTest { + + private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("d3a47f8b-d863-4c1f-b6f0-2c946b43f21c")); + private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("b052ae59-b9b4-47e8-ac71-39e7124bbd66")); private TbSendRPCRequestNode node; - private TbSendRpcRequestNodeConfiguration config; - + @Mock private TbContext ctxMock; @Mock @@ -66,50 +69,28 @@ public class TbSendRPCRequestNodeTest { @BeforeEach public void setUp() throws TbNodeException { node = new TbSendRPCRequestNode(); - config = new TbSendRpcRequestNodeConfiguration().defaultConfiguration(); + var config = new TbSendRpcRequestNodeConfiguration().defaultConfiguration(); var configuration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node.init(ctxMock, configuration); } @Test public void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { - DeviceId deviceId = new DeviceId(UUID.fromString("dda00a40-9d9c-4464-a759-488b9617319c")); - TenantId tenantId = new TenantId(UUID.fromString("81622599-afb3-4b52-9b47-f930f11ee963")); - String data = """ - { - "method": "setGpio", - "params": { - "pin": "23", - "value": 1 - } - } - """; - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, deviceId, TbMsgMetaData.EMPTY, data); - TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); when(ctxMock.getRpcService()).thenReturn(rpcServiceMock); - when(ctxMock.getTenantId()).thenReturn(tenantId); + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + // TODO: replace deprecated method newMsg() when(ctxMock.newMsg(any(), any(String.class), any(), any(), any(), any())).thenReturn(outMsg); - doAnswer(invocation -> { - Consumer callback = invocation.getArgument(1); + Consumer consumer = invocation.getArgument(1); RuleEngineDeviceRpcResponse rpcResponseMock = mock(RuleEngineDeviceRpcResponse.class); when(rpcResponseMock.getError()).thenReturn(Optional.empty()); when(rpcResponseMock.getResponse()).thenReturn(Optional.of(TbMsg.EMPTY_JSON_OBJECT)); - callback.accept(rpcResponseMock); + consumer.accept(rpcResponseMock); return null; - }).when(rpcServiceMock).sendRpcRequestToDevice(any(), any()); - - node.onMsg(ctxMock, msg); + }).when(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); - verify(ctxMock).enqueueForTellNext(eq(outMsg), eq(TbNodeConnectionType.SUCCESS)); - verify(ctxMock).ack(eq(msg)); - } - - @Test - public void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { - DeviceId deviceId = new DeviceId(UUID.fromString("dda00a40-9d9c-4464-a759-488b9617319c")); - TenantId tenantId = new TenantId(UUID.fromString("81622599-afb3-4b52-9b47-f930f11ee963")); String data = """ { "method": "setGpio", @@ -119,24 +100,42 @@ public class TbSendRPCRequestNodeTest { } } """; - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, deviceId, TbMsgMetaData.EMPTY, data); - TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, deviceId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, data); + node.onMsg(ctxMock, msg); + + verify(ctxMock).enqueueForTellNext(eq(outMsg), eq(TbNodeConnectionType.SUCCESS)); + verify(ctxMock).ack(eq(msg)); + } + + @Test + public void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { + TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); when(ctxMock.getRpcService()).thenReturn(rpcServiceMock); - when(ctxMock.getTenantId()).thenReturn(tenantId); + when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + // TODO: replace deprecated method newMsg() when(ctxMock.newMsg(any(), any(String.class), any(), any(), any(), any())).thenReturn(outMsg); - doAnswer(invocation -> { - Consumer callback = invocation.getArgument(1); + Consumer consumer = invocation.getArgument(1); RuleEngineDeviceRpcResponse rpcResponseMock = mock(RuleEngineDeviceRpcResponse.class); when(rpcResponseMock.getError()).thenReturn(Optional.of(RpcError.NO_ACTIVE_CONNECTION)); - callback.accept(rpcResponseMock); + consumer.accept(rpcResponseMock); return null; - }).when(rpcServiceMock).sendRpcRequestToDevice(any(), any()); + }).when(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); + String data = """ + { + "method": "setGpio", + "params": { + "pin": "23", + "value": 1 + } + } + """; + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, data); node.onMsg(ctxMock, msg); - verify(ctxMock).enqueueForTellFailure(eq(outMsg), eq("NO_ACTIVE_CONNECTION")); + verify(ctxMock).enqueueForTellFailure(eq(outMsg), eq(RpcError.NO_ACTIVE_CONNECTION.name())); verify(ctxMock).ack(eq(msg)); } @@ -155,30 +154,26 @@ public class TbSendRPCRequestNodeTest { return entityType; } }; + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); - node.onMsg(ctxMock, msg); - ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); - verify(ctxMock).tellFailure(eq(msg), captor.capture()); - Throwable value = captor.getValue(); - assertThat(value.getClass()).isEqualTo(RuntimeException.class); - assertThat(value.getMessage()).isEqualTo("Message originator is not a device entity!"); + ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); + verify(ctxMock).tellFailure(eq(msg), throwableCaptor.capture()); + assertThat(throwableCaptor.getValue()).isInstanceOf(RuntimeException.class) + .hasMessage("Message originator is not a device entity!"); } @ParameterizedTest @ValueSource(strings = {"method", "params"}) public void givenMethodOrParamsAreNotPresent_whenOnMsg_thenThrowsException(String key) { - DeviceId deviceId = new DeviceId(UUID.randomUUID()); - TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, deviceId, TbMsgMetaData.EMPTY, "{\"" + key + "\": \"value\"}"); + TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, "{\"" + key + "\": \"value\"}"); node.onMsg(ctxMock, msg); - ArgumentCaptor captor = ArgumentCaptor.forClass(Throwable.class); - verify(ctxMock).tellFailure(eq(msg), captor.capture()); - Throwable value = captor.getValue(); - assertThat(value.getClass()).isEqualTo(RuntimeException.class); - assertThat(value.getMessage()).isEqualTo( - key.equals("method") ? "Params are not present in the message!" : "Method is not present in the message!"); + ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); + verify(ctxMock).tellFailure(eq(msg), throwableCaptor.capture()); + assertThat(throwableCaptor.getValue()).isInstanceOf(RuntimeException.class) + .hasMessage(key.equals("method") ? "Params are not present in the message!" : "Method is not present in the message!"); } } From 6be2c617177f9f5d989b7356d85661ff070f8820 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Wed, 5 Jun 2024 15:23:59 +0300 Subject: [PATCH 05/16] added tests to verify request that is passed to sendRpcRequestToDevice() method --- .../engine/rpc/TbSendRPCReplyNodeTest.java | 39 +- .../engine/rpc/TbSendRPCRequestNodeTest.java | 365 +++++++++++++++--- 2 files changed, 321 insertions(+), 83 deletions(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java index 17d53802a1..9cab33fcdd 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNodeTest.java @@ -36,6 +36,7 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.msg.TbMsg; @@ -47,7 +48,7 @@ import java.util.Map; import java.util.UUID; import java.util.stream.Stream; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; @@ -59,13 +60,14 @@ public class TbSendRPCReplyNodeTest { private static final String DUMMY_SERVICE_ID = "testServiceId"; private static final int DUMMY_REQUEST_ID = 0; - private static final UUID DUMMY_SESSION_ID = UUID.randomUUID(); - private static final String DUMMY_DATA = "{\"key\":\"value\"}"; + private static final UUID DUMMY_SESSION_ID = UUID.fromString("4f1d94aa-f6ee-4078-8499-b8e68443f8ad"); + private final String DUMMY_DATA = "{\"key\":\"value\"}"; - TbSendRPCReplyNode node; + private TbSendRPCReplyNode node; + private TbSendRpcReplyNodeConfiguration config; - private final TenantId tenantId = TenantId.fromUUID(UUID.randomUUID()); - private final DeviceId deviceId = new DeviceId(UUID.randomUUID()); + private final TenantId tenantId = TenantId.fromUUID(UUID.fromString("4e2e2336-3376-4238-ba0a-c669b412ca66")); + private final DeviceId deviceId = new DeviceId(UUID.fromString("af64d1b9-8635-47e1-8738-6389df7fe57e")); @Mock private TbContext ctx; @@ -82,7 +84,7 @@ public class TbSendRPCReplyNodeTest { @BeforeEach public void setUp() throws TbNodeException { node = new TbSendRPCReplyNode(); - TbSendRpcReplyNodeConfiguration config = new TbSendRpcReplyNodeConfiguration().defaultConfiguration(); + config = new TbSendRpcReplyNodeConfiguration().defaultConfiguration(); node.init(ctx, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); } @@ -121,18 +123,7 @@ public class TbSendRPCReplyNodeTest { @ParameterizedTest @EnumSource(EntityType.class) public void testOriginatorEntityTypes(EntityType entityType) { - if (entityType == EntityType.DEVICE) return; - EntityId entityId = new EntityId() { - @Override - public UUID getId() { - return UUID.randomUUID(); - } - - @Override - public EntityType getEntityType() { - return entityType; - } - }; + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, "0f386739-210f-4e23-8739-23f84a172adc"); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); node.onMsg(ctx, msg); @@ -140,7 +131,8 @@ public class TbSendRPCReplyNodeTest { ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); verify(ctx).tellFailure(eq(msg), throwableCaptor.capture()); assertThat(throwableCaptor.getValue()).isInstanceOf(RuntimeException.class) - .hasMessage("Message originator is not a device entity!"); + .hasMessage(EntityType.DEVICE != entityType ? "Message originator is not a device entity!" + : "Request id is not present in the metadata!"); } @ParameterizedTest @@ -155,6 +147,13 @@ public class TbSendRPCReplyNodeTest { assertThat(throwableCaptor.getValue()).isInstanceOf(RuntimeException.class).hasMessage(errorMsg); } + @Test + public void verifyDefaultConfig() { + assertThat(config.getServiceIdMetaDataAttribute()).isEqualTo("serviceId"); + assertThat(config.getSessionIdMetaDataAttribute()).isEqualTo("sessionId"); + assertThat(config.getRequestIdMetaDataAttribute()).isEqualTo("requestId"); + } + private static Stream testForAvailabilityOfMetadataAndDataValues() { return Stream.of( Arguments.of(TbMsgMetaData.EMPTY, "Request id is not present in the metadata!"), diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java index 969aecb4d5..585638a1d9 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java @@ -19,7 +19,9 @@ 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.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; @@ -31,9 +33,11 @@ import org.thingsboard.rule.engine.api.RuleEngineRpcService; 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.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.msg.TbMsgType; import org.thingsboard.server.common.data.msg.TbNodeConnectionType; @@ -41,25 +45,40 @@ import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import java.util.UUID; import java.util.function.Consumer; +import java.util.stream.Stream; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.BDDMockito.willAnswer; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class TbSendRPCRequestNodeTest { private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("d3a47f8b-d863-4c1f-b6f0-2c946b43f21c")); private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("b052ae59-b9b4-47e8-ac71-39e7124bbd66")); - + + private final String MSG_DATA = """ + { + "method": "setGpio", + "params": { + "pin": "23", + "value": 1 + }, + "additionalInfo": "information" + } + """; + private TbSendRPCRequestNode node; + private TbSendRpcRequestNodeConfiguration config; @Mock private TbContext ctxMock; @@ -67,112 +86,332 @@ public class TbSendRPCRequestNodeTest { private RuleEngineRpcService rpcServiceMock; @BeforeEach - public void setUp() throws TbNodeException { + void setUp() throws TbNodeException { node = new TbSendRPCRequestNode(); - var config = new TbSendRpcRequestNodeConfiguration().defaultConfiguration(); + config = new TbSendRpcRequestNodeConfiguration().defaultConfiguration(); var configuration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); node.init(ctxMock, configuration); } @Test - public void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { - TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + void verifyDefaultConfig() { + assertThat(config.getTimeoutInSeconds()).isEqualTo(60); + } - when(ctxMock.getRpcService()).thenReturn(rpcServiceMock); - when(ctxMock.getTenantId()).thenReturn(TENANT_ID); - // TODO: replace deprecated method newMsg() - when(ctxMock.newMsg(any(), any(String.class), any(), any(), any(), any())).thenReturn(outMsg); - doAnswer(invocation -> { - Consumer consumer = invocation.getArgument(1); - RuleEngineDeviceRpcResponse rpcResponseMock = mock(RuleEngineDeviceRpcResponse.class); - when(rpcResponseMock.getError()).thenReturn(Optional.empty()); - when(rpcResponseMock.getResponse()).thenReturn(Optional.of(TbMsg.EMPTY_JSON_OBJECT)); - consumer.accept(rpcResponseMock); - return null; - }).when(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); + @ParameterizedTest + @MethodSource + void givenOneway_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + TbMsgMetaData msgMetadata = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetadata, MSG_DATA); + node.onMsg(ctxMock, msg); + + verifyRequest(requestConsumer); + } + + private static Stream givenOneway_whenOnMsg_thenVerifyRequest() { + var metadata = new HashMap<>(); + metadata.put("oneway", null); + return Stream.of( + Arguments.of(Map.of("oneway", "true"), (Consumer) req -> + assertThat(req.isOneway()).isTrue()), + Arguments.of(null, (Consumer) req -> + assertThat(req.isOneway()).isFalse()), + Arguments.of(Map.of("oneway", ""), (Consumer) req -> + assertThat(req.isOneway()).isFalse()), + Arguments.of(metadata, (Consumer) req -> + assertThat(req.isOneway()).isFalse()) + ); + } + + @Test + void givenMsgBody_whenOnMsg_thenVerifyRequest() { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + node.onMsg(ctxMock, msg); + + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(RuleEngineDeviceRpcRequest.class); + then(rpcServiceMock).should().sendRpcRequestToDevice(requestCaptor.capture(), any(Consumer.class)); + assertThat(requestCaptor.getValue()) + .hasFieldOrPropertyWithValue("method", "setGpio") + .hasFieldOrPropertyWithValue("body", "{\"pin\":\"23\",\"value\":1}") + .hasFieldOrPropertyWithValue("deviceId", DEVICE_ID) + .hasFieldOrPropertyWithValue("tenantId", TENANT_ID) + .hasFieldOrPropertyWithValue("additionalInfo", "information"); + } + + @ParameterizedTest + @MethodSource + void givenRequestId_whenOnMsg_thenVerifyRequest(String requestId, Consumer requestConsumer) { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); - String data = """ + String data = String.format(""" { "method": "setGpio", "params": { "pin": "23", "value": 1 - } + }%s%s } - """; - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, data); + """, requestId != null ? ",\"requestId\":" : "", requestId != null ? requestId : ""); + TbMsg msg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); node.onMsg(ctxMock, msg); - verify(ctxMock).enqueueForTellNext(eq(outMsg), eq(TbNodeConnectionType.SUCCESS)); - verify(ctxMock).ack(eq(msg)); + verifyRequest(requestConsumer); + } + + private static Stream givenRequestId_whenOnMsg_thenVerifyRequest() { + return Stream.of( + Arguments.of("12345", (Consumer) req -> + assertThat(req.getRequestId()).isEqualTo(12345)), + Arguments.of(null, (Consumer) req -> + assertThat(req.getRequestId()).isNotNull()) + ); + } + + @ParameterizedTest + @MethodSource + void givenRequestUUID_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + TbMsgMetaData msgMetadata = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetadata, MSG_DATA); + node.onMsg(ctxMock, msg); + + verifyRequest(requestConsumer); + } + + private static Stream givenRequestUUID_whenOnMsg_thenVerifyRequest() { + var metadata= new HashMap<>(); + metadata.put("requestUUID", null); + return Stream.of( + Arguments.of(Map.of("requestUUID", "1c4ef338-ea1b-495f-8e2b-67981f27cf35"), (Consumer) req -> + assertThat(req.getRequestUUID()).isEqualTo(UUID.fromString("1c4ef338-ea1b-495f-8e2b-67981f27cf35"))), + Arguments.of(null, (Consumer) req -> + assertThat(req.getRequestUUID()).isNotNull()), + Arguments.of(Map.of("requestUUID", ""), (Consumer) req -> + assertThat(req.getRequestUUID()).isNotNull()), + Arguments.of(metadata, (Consumer) req -> + assertThat(req.getRequestUUID()).isNotNull()) + ); + } + + @ParameterizedTest + @MethodSource + void givenOriginServiceId_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + TbMsgMetaData msgMetaData = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetaData, MSG_DATA); + node.onMsg(ctxMock, msg); + + verifyRequest(requestConsumer); + } + + private static Stream givenOriginServiceId_whenOnMsg_thenVerifyRequest() { + var metadata= new HashMap<>(); + metadata.put("originServiceId", null); + return Stream.of( + Arguments.of(Map.of("originServiceId", "service-id-123"), (Consumer) req -> + assertThat(req.getOriginServiceId()).isEqualTo("service-id-123")), + Arguments.of(null, (Consumer) req -> + assertThat(req.getOriginServiceId()).isNull()), + Arguments.of(Map.of("originServiceId", ""), (Consumer) req -> + assertThat(req.getOriginServiceId()).isNull()), + Arguments.of(metadata, (Consumer) req -> + assertThat(req.getOriginServiceId()).isNull()) + ); + } + + @ParameterizedTest + @MethodSource + void givenExpirationTime_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + TbMsgMetaData msgMetaData = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetaData, MSG_DATA); + node.onMsg(ctxMock, msg); + + verifyRequest(requestConsumer); + } + + private static Stream givenExpirationTime_whenOnMsg_thenVerifyRequest() { + var metadata= new HashMap<>(); + metadata.put(DataConstants.EXPIRATION_TIME, null); + return Stream.of( + Arguments.of(Map.of(DataConstants.EXPIRATION_TIME, "2000000000000"), (Consumer) req -> + assertThat(req.getExpirationTime()).isEqualTo(2000000000000L)), + Arguments.of(null, (Consumer) req -> + assertThat(req.getExpirationTime()).isGreaterThan(System.currentTimeMillis())), + Arguments.of(Map.of(DataConstants.EXPIRATION_TIME, ""), (Consumer) req -> + assertThat(req.getExpirationTime()).isGreaterThan(System.currentTimeMillis())), + Arguments.of(metadata, (Consumer) req -> + assertThat(req.getExpirationTime()).isGreaterThan(System.currentTimeMillis())) + ); + } + + @ParameterizedTest + @MethodSource + void givenRetries_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + TbMsgMetaData msgMetaData = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetaData, MSG_DATA); + node.onMsg(ctxMock, msg); + + verifyRequest(requestConsumer); + } + + private static Stream givenRetries_whenOnMsg_thenVerifyRequest() { + var metadata= new HashMap<>(); + metadata.put(DataConstants.RETRIES, null); + return Stream.of( + Arguments.of(Map.of(DataConstants.RETRIES, "3"), (Consumer) req -> + assertThat(req.getRetries()).isEqualTo(3)), + Arguments.of(null, (Consumer) req -> + assertThat(req.getRetries()).isNull()), + Arguments.of(Map.of(DataConstants.RETRIES,""), (Consumer) req -> + assertThat(req.getRetries()).isNull()), + Arguments.of(metadata, (Consumer) req -> + assertThat(req.getRetries()).isNull()) + ); + } + + @ParameterizedTest + @MethodSource + void givenTbMsgType_whenOnMsg_thenVerifyRequest(TbMsgType msgType, Consumer requestConsumer) { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + TbMsg msg = TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + node.onMsg(ctxMock, msg); + + verifyRequest(requestConsumer); + } + + private static Stream givenTbMsgType_whenOnMsg_thenVerifyRequest() { + return Stream.of( + Arguments.of(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, (Consumer) req -> + assertThat(req.isRestApiCall()).isTrue()), + Arguments.of(TbMsgType.TO_SERVER_RPC_REQUEST, (Consumer) req -> + assertThat(req.isRestApiCall()).isFalse()) + ); + } + + @ParameterizedTest + @MethodSource + void givenPersistent_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + TbMsgMetaData msgMetaData = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetaData, MSG_DATA); + node.onMsg(ctxMock, msg); + + verifyRequest(requestConsumer); + } + + private static Stream givenPersistent_whenOnMsg_thenVerifyRequest() { + var metadata= new HashMap<>(); + metadata.put(DataConstants.PERSISTENT, null); + return Stream.of( + Arguments.of(Map.of(DataConstants.PERSISTENT, "true"), (Consumer) req -> + assertThat(req.isPersisted()).isTrue()), + Arguments.of(null, (Consumer) req -> + assertThat(req.isPersisted()).isFalse()), + Arguments.of(Map.of(DataConstants.PERSISTENT, ""), (Consumer) req -> + assertThat(req.isPersisted()).isFalse()), + Arguments.of(metadata, (Consumer) req -> + assertThat(req.isPersisted()).isFalse()) + ); + } + + private void verifyRequest(Consumer requestConsumer) { + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(RuleEngineDeviceRpcRequest.class); + then(rpcServiceMock).should().sendRpcRequestToDevice(requestCaptor.capture(), any(Consumer.class)); + requestConsumer.accept(requestCaptor.getValue()); } @Test - public void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { + void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); - when(ctxMock.getRpcService()).thenReturn(rpcServiceMock); - when(ctxMock.getTenantId()).thenReturn(TENANT_ID); + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); // TODO: replace deprecated method newMsg() - when(ctxMock.newMsg(any(), any(String.class), any(), any(), any(), any())).thenReturn(outMsg); - doAnswer(invocation -> { + given(ctxMock.newMsg(any(), any(String.class), any(), any(), any(), any())).willReturn(outMsg); + willAnswer(invocation -> { Consumer consumer = invocation.getArgument(1); RuleEngineDeviceRpcResponse rpcResponseMock = mock(RuleEngineDeviceRpcResponse.class); - when(rpcResponseMock.getError()).thenReturn(Optional.of(RpcError.NO_ACTIVE_CONNECTION)); + given(rpcResponseMock.getError()).willReturn(Optional.empty()); + given(rpcResponseMock.getResponse()).willReturn(Optional.of(TbMsg.EMPTY_JSON_OBJECT)); consumer.accept(rpcResponseMock); return null; - }).when(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); + }).given(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); - String data = """ - { - "method": "setGpio", - "params": { - "pin": "23", - "value": 1 - } - } - """; - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, data); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + node.onMsg(ctxMock, msg); + + then(ctxMock).should().enqueueForTellNext(outMsg, TbNodeConnectionType.SUCCESS); + then(ctxMock).should().ack(msg); + } + + @Test + void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { + TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); + + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + // TODO: replace deprecated method newMsg() + given(ctxMock.newMsg(any(), any(String.class), any(), any(), any(), any())).willReturn(outMsg); + willAnswer(invocation -> { + Consumer consumer = invocation.getArgument(1); + RuleEngineDeviceRpcResponse rpcResponseMock = mock(RuleEngineDeviceRpcResponse.class); + given(rpcResponseMock.getError()).willReturn(Optional.of(RpcError.NO_ACTIVE_CONNECTION)); + consumer.accept(rpcResponseMock); + return null; + }).given(rpcServiceMock).sendRpcRequestToDevice(any(RuleEngineDeviceRpcRequest.class), any(Consumer.class)); + + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); node.onMsg(ctxMock, msg); - verify(ctxMock).enqueueForTellFailure(eq(outMsg), eq(RpcError.NO_ACTIVE_CONNECTION.name())); - verify(ctxMock).ack(eq(msg)); + then(ctxMock).should().enqueueForTellFailure(outMsg, RpcError.NO_ACTIVE_CONNECTION.name()); + then(ctxMock).should().ack(msg); } @ParameterizedTest @EnumSource(EntityType.class) - public void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType entityType) { - if (entityType == EntityType.DEVICE) return; - EntityId entityId = new EntityId() { - @Override - public UUID getId() { - return UUID.randomUUID(); - } - - @Override - public EntityType getEntityType() { - return entityType; - } - }; + void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType entityType) { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, "ac21a1bb-eabf-4463-8313-24bea1f498d9"); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); node.onMsg(ctxMock, msg); ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); - verify(ctxMock).tellFailure(eq(msg), throwableCaptor.capture()); + then(ctxMock).should().tellFailure(eq(msg), throwableCaptor.capture()); assertThat(throwableCaptor.getValue()).isInstanceOf(RuntimeException.class) - .hasMessage("Message originator is not a device entity!"); + .hasMessage(EntityType.DEVICE != entityType ? "Message originator is not a device entity!" + : "Method is not present in the message!"); } @ParameterizedTest @ValueSource(strings = {"method", "params"}) - public void givenMethodOrParamsAreNotPresent_whenOnMsg_thenThrowsException(String key) { + void givenMethodOrParamsAreNotPresent_whenOnMsg_thenThrowsException(String key) { TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, "{\"" + key + "\": \"value\"}"); node.onMsg(ctxMock, msg); ArgumentCaptor throwableCaptor = ArgumentCaptor.forClass(Throwable.class); - verify(ctxMock).tellFailure(eq(msg), throwableCaptor.capture()); + then(ctxMock).should().tellFailure(eq(msg), throwableCaptor.capture()); assertThat(throwableCaptor.getValue()).isInstanceOf(RuntimeException.class) .hasMessage(key.equals("method") ? "Params are not present in the message!" : "Method is not present in the message!"); } From f9a126df31cfa70a9f17092be93a7d3c4e4fd89f Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 6 Jun 2024 19:10:09 +0300 Subject: [PATCH 06/16] refactored parametrized tests --- .../engine/rpc/TbSendRPCRequestNodeTest.java | 275 +++++++++--------- 1 file changed, 139 insertions(+), 136 deletions(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java index 585638a1d9..859157454f 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java @@ -22,10 +22,12 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcRequest; import org.thingsboard.rule.engine.api.RuleEngineDeviceRpcResponse; @@ -45,9 +47,8 @@ import org.thingsboard.server.common.data.rpc.RpcError; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; +import java.util.Random; import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Stream; @@ -100,34 +101,30 @@ public class TbSendRPCRequestNodeTest { @ParameterizedTest @MethodSource - void givenOneway_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + public void givenOneway_whenOnMsg_thenVerifyRequest(String mdKeyValue, boolean expectedResult) { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsgMetaData msgMetadata = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); + TbMsgMetaData msgMetadata = new TbMsgMetaData(); + msgMetadata.putValue("oneway", mdKeyValue); TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetadata, MSG_DATA); node.onMsg(ctxMock, msg); - verifyRequest(requestConsumer); + var ruleEngineDeviceRpcRequestCaptor = captureRequest(); + assertThat(ruleEngineDeviceRpcRequestCaptor.getValue().isOneway()).isEqualTo(expectedResult); } private static Stream givenOneway_whenOnMsg_thenVerifyRequest() { - var metadata = new HashMap<>(); - metadata.put("oneway", null); return Stream.of( - Arguments.of(Map.of("oneway", "true"), (Consumer) req -> - assertThat(req.isOneway()).isTrue()), - Arguments.of(null, (Consumer) req -> - assertThat(req.isOneway()).isFalse()), - Arguments.of(Map.of("oneway", ""), (Consumer) req -> - assertThat(req.isOneway()).isFalse()), - Arguments.of(metadata, (Consumer) req -> - assertThat(req.isOneway()).isFalse()) + Arguments.of("true", true), + Arguments.of("false", false), + Arguments.of(null, false), + Arguments.of("", false) ); } @Test - void givenMsgBody_whenOnMsg_thenVerifyRequest() { + public void givenMsgBody_whenOnMsg_thenVerifyRequest() { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); @@ -144,205 +141,211 @@ public class TbSendRPCRequestNodeTest { .hasFieldOrPropertyWithValue("additionalInfo", "information"); } - @ParameterizedTest - @MethodSource - void givenRequestId_whenOnMsg_thenVerifyRequest(String requestId, Consumer requestConsumer) { + @Test + public void givenRequestIdIsNotSet_whenOnMsg_thenVerifyRequest() { + Random randomMock = mock(Random.class); + given(randomMock.nextInt()).willReturn(123); + ReflectionTestUtils.setField(node, "random", randomMock); given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - String data = String.format(""" + TbMsg msg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); + node.onMsg(ctxMock, msg); + + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().getRequestId()).isEqualTo(123); + } + + @Test + public void givenRequestId_whenOnMsg_thenVerifyRequest() { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + String data = """ { "method": "setGpio", "params": { "pin": "23", "value": 1 - }%s%s + }, + "requestId": 12345 } - """, requestId != null ? ",\"requestId\":" : "", requestId != null ? requestId : ""); + """; TbMsg msg = TbMsg.newMsg(TbMsgType.TO_SERVER_RPC_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, data); node.onMsg(ctxMock, msg); - verifyRequest(requestConsumer); + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().getRequestId()).isEqualTo(12345); } - private static Stream givenRequestId_whenOnMsg_thenVerifyRequest() { - return Stream.of( - Arguments.of("12345", (Consumer) req -> - assertThat(req.getRequestId()).isEqualTo(12345)), - Arguments.of(null, (Consumer) req -> - assertThat(req.getRequestId()).isNotNull()) - ); + @Test + public void givenRequestUUID_whenOnMsg_thenVerifyRequest() { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + String requestUUID = "b795a241-5a30-48fb-92d5-46b864d47130"; + TbMsgMetaData metadata = new TbMsgMetaData(); + metadata.putValue("requestUUID", requestUUID); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + node.onMsg(ctxMock, msg); + + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().getRequestUUID()).isEqualTo(UUID.fromString(requestUUID)); } @ParameterizedTest - @MethodSource - void givenRequestUUID_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + @NullAndEmptySource + public void givenInvalidRequestUUID_whenOnMsg_thenVerifyRequest(String requestUUID) { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsgMetaData msgMetadata = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetadata, MSG_DATA); + TbMsgMetaData metadata = new TbMsgMetaData(); + metadata.putValue("requestUUID", requestUUID); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); node.onMsg(ctxMock, msg); - verifyRequest(requestConsumer); + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().getRequestUUID()).isNotNull(); } - private static Stream givenRequestUUID_whenOnMsg_thenVerifyRequest() { - var metadata= new HashMap<>(); - metadata.put("requestUUID", null); - return Stream.of( - Arguments.of(Map.of("requestUUID", "1c4ef338-ea1b-495f-8e2b-67981f27cf35"), (Consumer) req -> - assertThat(req.getRequestUUID()).isEqualTo(UUID.fromString("1c4ef338-ea1b-495f-8e2b-67981f27cf35"))), - Arguments.of(null, (Consumer) req -> - assertThat(req.getRequestUUID()).isNotNull()), - Arguments.of(Map.of("requestUUID", ""), (Consumer) req -> - assertThat(req.getRequestUUID()).isNotNull()), - Arguments.of(metadata, (Consumer) req -> - assertThat(req.getRequestUUID()).isNotNull()) - ); + @Test + public void givenOriginServiceId_whenOnMsg_thenVerifyRequest() { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + String originServiceId = "service-id-123"; + TbMsgMetaData metadata = new TbMsgMetaData(); + metadata.putValue("originServiceId", originServiceId); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + node.onMsg(ctxMock, msg); + + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().getOriginServiceId()).isEqualTo(originServiceId); } @ParameterizedTest - @MethodSource - void givenOriginServiceId_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + @NullAndEmptySource + public void givenInvalidOriginServiceId_whenOnMsg_thenVerifyRequest(String originServiceId) { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsgMetaData msgMetaData = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetaData, MSG_DATA); + TbMsgMetaData metadata = new TbMsgMetaData(); + metadata.putValue("originServiceId", originServiceId); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); node.onMsg(ctxMock, msg); - verifyRequest(requestConsumer); + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().getOriginServiceId()).isNull(); } - private static Stream givenOriginServiceId_whenOnMsg_thenVerifyRequest() { - var metadata= new HashMap<>(); - metadata.put("originServiceId", null); - return Stream.of( - Arguments.of(Map.of("originServiceId", "service-id-123"), (Consumer) req -> - assertThat(req.getOriginServiceId()).isEqualTo("service-id-123")), - Arguments.of(null, (Consumer) req -> - assertThat(req.getOriginServiceId()).isNull()), - Arguments.of(Map.of("originServiceId", ""), (Consumer) req -> - assertThat(req.getOriginServiceId()).isNull()), - Arguments.of(metadata, (Consumer) req -> - assertThat(req.getOriginServiceId()).isNull()) - ); + @Test + public void givenExpirationTime_whenOnMsg_thenVerifyRequest() { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + String expirationTime = "2000000000000"; + TbMsgMetaData metadata = new TbMsgMetaData(); + metadata.putValue(DataConstants.EXPIRATION_TIME, expirationTime); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + node.onMsg(ctxMock, msg); + + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().getExpirationTime()).isEqualTo(Long.parseLong(expirationTime)); } @ParameterizedTest - @MethodSource - void givenExpirationTime_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + @NullAndEmptySource + public void givenInvalidExpirationTime_whenOnMsg_thenVerifyRequest(String expirationTime) { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsgMetaData msgMetaData = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetaData, MSG_DATA); + TbMsgMetaData metadata = new TbMsgMetaData(); + metadata.putValue(DataConstants.EXPIRATION_TIME, expirationTime); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); node.onMsg(ctxMock, msg); - verifyRequest(requestConsumer); + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().getExpirationTime()).isGreaterThan(System.currentTimeMillis()); } - private static Stream givenExpirationTime_whenOnMsg_thenVerifyRequest() { - var metadata= new HashMap<>(); - metadata.put(DataConstants.EXPIRATION_TIME, null); - return Stream.of( - Arguments.of(Map.of(DataConstants.EXPIRATION_TIME, "2000000000000"), (Consumer) req -> - assertThat(req.getExpirationTime()).isEqualTo(2000000000000L)), - Arguments.of(null, (Consumer) req -> - assertThat(req.getExpirationTime()).isGreaterThan(System.currentTimeMillis())), - Arguments.of(Map.of(DataConstants.EXPIRATION_TIME, ""), (Consumer) req -> - assertThat(req.getExpirationTime()).isGreaterThan(System.currentTimeMillis())), - Arguments.of(metadata, (Consumer) req -> - assertThat(req.getExpirationTime()).isGreaterThan(System.currentTimeMillis())) - ); + @Test + public void givenRetries_whenOnMsg_thenVerifyRequest() { + given(ctxMock.getRpcService()).willReturn(rpcServiceMock); + given(ctxMock.getTenantId()).willReturn(TENANT_ID); + + Integer retries = 3; + TbMsgMetaData metadata = new TbMsgMetaData(); + metadata.putValue(DataConstants.RETRIES, String.valueOf(retries)); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); + node.onMsg(ctxMock, msg); + + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().getRetries()).isEqualTo(retries); } @ParameterizedTest - @MethodSource - void givenRetries_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + @NullAndEmptySource + public void givenInvalidRetriesValue_whenOnMsg_thenVerifyRequest(String retries) { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsgMetaData msgMetaData = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetaData, MSG_DATA); + TbMsgMetaData metadata = new TbMsgMetaData(); + metadata.putValue(DataConstants.RETRIES, retries); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); node.onMsg(ctxMock, msg); - verifyRequest(requestConsumer); - } - - private static Stream givenRetries_whenOnMsg_thenVerifyRequest() { - var metadata= new HashMap<>(); - metadata.put(DataConstants.RETRIES, null); - return Stream.of( - Arguments.of(Map.of(DataConstants.RETRIES, "3"), (Consumer) req -> - assertThat(req.getRetries()).isEqualTo(3)), - Arguments.of(null, (Consumer) req -> - assertThat(req.getRetries()).isNull()), - Arguments.of(Map.of(DataConstants.RETRIES,""), (Consumer) req -> - assertThat(req.getRetries()).isNull()), - Arguments.of(metadata, (Consumer) req -> - assertThat(req.getRetries()).isNull()) - ); + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().getRetries()).isNull(); } @ParameterizedTest - @MethodSource - void givenTbMsgType_whenOnMsg_thenVerifyRequest(TbMsgType msgType, Consumer requestConsumer) { + @EnumSource(TbMsgType.class) + public void givenTbMsgType_whenOnMsg_thenVerifyRequest(TbMsgType msgType) { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); TbMsg msg = TbMsg.newMsg(msgType, DEVICE_ID, TbMsgMetaData.EMPTY, MSG_DATA); node.onMsg(ctxMock, msg); - verifyRequest(requestConsumer); - } - - private static Stream givenTbMsgType_whenOnMsg_thenVerifyRequest() { - return Stream.of( - Arguments.of(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, (Consumer) req -> - assertThat(req.isRestApiCall()).isTrue()), - Arguments.of(TbMsgType.TO_SERVER_RPC_REQUEST, (Consumer) req -> - assertThat(req.isRestApiCall()).isFalse()) - ); + ArgumentCaptor requestCaptor = captureRequest(); + if (msgType == TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE) { + assertThat(requestCaptor.getValue().isRestApiCall()).isTrue(); + return; + } + assertThat(requestCaptor.getValue().isRestApiCall()).isFalse(); } @ParameterizedTest @MethodSource - void givenPersistent_whenOnMsg_thenVerifyRequest(Map metadata, Consumer requestConsumer) { + public void givenPersistent_whenOnMsg_thenVerifyRequest(String isPersisted, boolean expectedPersistence) { given(ctxMock.getRpcService()).willReturn(rpcServiceMock); given(ctxMock.getTenantId()).willReturn(TENANT_ID); - TbMsgMetaData msgMetaData = metadata == null ? TbMsgMetaData.EMPTY : new TbMsgMetaData(metadata); - TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, msgMetaData, MSG_DATA); + TbMsgMetaData metadata = new TbMsgMetaData(); + metadata.putValue(DataConstants.PERSISTENT, isPersisted); + TbMsg msg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, metadata, MSG_DATA); node.onMsg(ctxMock, msg); - verifyRequest(requestConsumer); + ArgumentCaptor requestCaptor = captureRequest(); + assertThat(requestCaptor.getValue().isPersisted()).isEqualTo(expectedPersistence); } private static Stream givenPersistent_whenOnMsg_thenVerifyRequest() { - var metadata= new HashMap<>(); - metadata.put(DataConstants.PERSISTENT, null); return Stream.of( - Arguments.of(Map.of(DataConstants.PERSISTENT, "true"), (Consumer) req -> - assertThat(req.isPersisted()).isTrue()), - Arguments.of(null, (Consumer) req -> - assertThat(req.isPersisted()).isFalse()), - Arguments.of(Map.of(DataConstants.PERSISTENT, ""), (Consumer) req -> - assertThat(req.isPersisted()).isFalse()), - Arguments.of(metadata, (Consumer) req -> - assertThat(req.isPersisted()).isFalse()) + Arguments.of("true", true), + Arguments.of("false", false), + Arguments.of(null, false), + Arguments.of("", false) ); } - private void verifyRequest(Consumer requestConsumer) { + private ArgumentCaptor captureRequest() { ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(RuleEngineDeviceRpcRequest.class); then(rpcServiceMock).should().sendRpcRequestToDevice(requestCaptor.capture(), any(Consumer.class)); - requestConsumer.accept(requestCaptor.getValue()); + return requestCaptor; } @Test - void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { + public void givenRpcResponseWithoutError_whenOnMsg_thenSendsRpcRequest() { TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); given(ctxMock.getRpcService()).willReturn(rpcServiceMock); @@ -366,7 +369,7 @@ public class TbSendRPCRequestNodeTest { } @Test - void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { + public void givenRpcResponseWithError_whenOnMsg_thenTellFailure() { TbMsg outMsg = TbMsg.newMsg(TbMsgType.RPC_CALL_FROM_SERVER_TO_DEVICE, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); given(ctxMock.getRpcService()).willReturn(rpcServiceMock); @@ -390,7 +393,7 @@ public class TbSendRPCRequestNodeTest { @ParameterizedTest @EnumSource(EntityType.class) - void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType entityType) { + public void givenOriginatorIsNotDevice_whenOnMsg_thenThrowsException(EntityType entityType) { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, "ac21a1bb-eabf-4463-8313-24bea1f498d9"); TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, entityId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); @@ -405,7 +408,7 @@ public class TbSendRPCRequestNodeTest { @ParameterizedTest @ValueSource(strings = {"method", "params"}) - void givenMethodOrParamsAreNotPresent_whenOnMsg_thenThrowsException(String key) { + public void givenMethodOrParamsAreNotPresent_whenOnMsg_thenThrowsException(String key) { TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, "{\"" + key + "\": \"value\"}"); node.onMsg(ctxMock, msg); From dce51e9e2d5836e766bacd5c5cb4c1a64b4a50da Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 6 Jun 2024 19:12:21 +0300 Subject: [PATCH 07/16] made methods public --- .../thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java index 859157454f..e9d25e0d5e 100644 --- a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java +++ b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNodeTest.java @@ -87,7 +87,7 @@ public class TbSendRPCRequestNodeTest { private RuleEngineRpcService rpcServiceMock; @BeforeEach - void setUp() throws TbNodeException { + public void setUp() throws TbNodeException { node = new TbSendRPCRequestNode(); config = new TbSendRpcRequestNodeConfiguration().defaultConfiguration(); var configuration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); @@ -95,7 +95,7 @@ public class TbSendRPCRequestNodeTest { } @Test - void verifyDefaultConfig() { + public void verifyDefaultConfig() { assertThat(config.getTimeoutInSeconds()).isEqualTo(60); } From 159c97ac67248216bbbd5afef39271ac2bfb9b63 Mon Sep 17 00:00:00 2001 From: Shvaika Dmytro Date: Wed, 12 Jun 2024 18:00:31 +0300 Subject: [PATCH 08/16] Rule node ids seach query: replaced Page with Slice to exclude count query execution (#10989) * replaced Page with Slice to exclude count query execution * fixed test since Slice totalPages and totalElements is always 0 --- .../server/dao/sql/rule/RuleNodeRepository.java | 7 ++++--- .../server/dao/sql/rule/JpaRuleNodeDaoTest.java | 16 +++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java index fc4fff49d0..264ea88326 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.sql.rule; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -51,9 +52,9 @@ public interface RuleNodeRepository extends JpaRepository Pageable pageable); @Query("SELECT r.id FROM RuleNodeEntity r WHERE r.type = :ruleType AND r.configurationVersion < :version") - Page findAllRuleNodeIdsByTypeAndVersionLessThan(@Param("ruleType") String ruleType, - @Param("version") int version, - Pageable pageable); + Slice findAllRuleNodeIdsByTypeAndVersionLessThan(@Param("ruleType") String ruleType, + @Param("version") int version, + Pageable pageable); List findRuleNodesByRuleChainIdAndExternalIdIn(UUID ruleChainId, List externalIds); diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDaoTest.java index 0fa9383ab1..dcc95b2aff 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDaoTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDaoTest.java @@ -22,7 +22,6 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; @@ -127,20 +126,15 @@ public class JpaRuleNodeDaoTest extends AbstractJpaDaoTest { @Test public void testFindRuleNodeIdsByTypeAndVersionLessThan() { + // test - search text ignored PageData ruleNodeIds = ruleNodeDao.findAllRuleNodeIdsByTypeAndVersionLessThan( "A", 1, new PageLink(10, 0, PREFIX_FOR_RULE_NODE_NAME)); - assertEquals(20, ruleNodeIds.getTotalElements()); - assertEquals(2, ruleNodeIds.getTotalPages()); + assertEquals(0, ruleNodeIds.getTotalElements()); // due to DaoUtil.pageToPageData impl for Slice + assertEquals(0, ruleNodeIds.getTotalPages()); // due to DaoUtil.pageToPageData impl for Slice assertEquals(10, ruleNodeIds.getData().size()); ruleNodeIds = ruleNodeDao.findAllRuleNodeIdsByTypeAndVersionLessThan( "A", 1, new PageLink(10, 0)); - assertEquals(20, ruleNodeIds.getTotalElements()); - assertEquals(2, ruleNodeIds.getTotalPages()); - assertEquals(10, ruleNodeIds.getData().size()); - - // test - search text ignored - ruleNodeIds = ruleNodeDao.findAllRuleNodeIdsByTypeAndVersionLessThan( "A", 1, new PageLink(10, 0, StringUtils.randomAlphabetic(5))); - assertEquals(20, ruleNodeIds.getTotalElements()); - assertEquals(2, ruleNodeIds.getTotalPages()); + assertEquals(0, ruleNodeIds.getTotalElements()); // due to DaoUtil.pageToPageData impl for Slice + assertEquals(0, ruleNodeIds.getTotalPages()); // due to DaoUtil.pageToPageData impl for Slice assertEquals(10, ruleNodeIds.getData().size()); } From 17be2154ce193a65f6923f317223f89ef41c8b68 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 13 Jun 2024 16:04:55 +0300 Subject: [PATCH 09/16] fixed cassandra timeseries deletion if partition is INDEFINITE --- .../server/dao/timeseries/CassandraBaseTimeseriesDao.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java index 383c592a31..6700e3614a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQueryResult; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntryAggWrapper; +import org.thingsboard.server.common.data.kv.TsKvQuery; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.nosql.TbResultSet; import org.thingsboard.server.dao.nosql.TbResultSetFuture; @@ -239,10 +240,8 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD long minPartition = toPartitionTs(query.getStartTs()); long maxPartition = toPartitionTs(query.getEndTs()); - TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition); - final SimpleListenableFuture resultFuture = new SimpleListenableFuture<>(); - final ListenableFuture> partitionsListFuture = Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor); + final ListenableFuture> partitionsListFuture = getPartitionsFuture(tenantId, query, entityId, minPartition, maxPartition); Futures.addCallback(partitionsListFuture, new FutureCallback>() { @Override @@ -416,7 +415,7 @@ public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesD readResultsProcessingExecutor); } - private ListenableFuture> getPartitionsFuture(TenantId tenantId, ReadTsKvQuery query, EntityId entityId, long minPartition, long maxPartition) { + private ListenableFuture> getPartitionsFuture(TenantId tenantId, TsKvQuery query, EntityId entityId, long minPartition, long maxPartition) { if (isFixedPartitioning()) { //no need to fetch partitions from DB return Futures.immediateFuture(FIXED_PARTITION); } From 3ea1e5706ecdbd1ab7e5ab4bd907eaba1b999a5a Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 14 Jun 2024 17:42:54 +0300 Subject: [PATCH 10/16] updated TenantProfile swagger example --- .../controller/TenantProfileController.java | 6 ++-- .../server/common/data/TenantProfile.java | 6 ++-- .../DefaultTenantProfileConfiguration.java | 29 +++++++++++++++++++ .../profile/TenantProfileConfiguration.java | 3 ++ 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java index ad1ed7824a..007f77aa20 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TenantProfileController.java @@ -118,8 +118,8 @@ public class TenantProfileController extends BaseController { "Let's review the example of tenant profile data below: " + "\n\n" + MARKDOWN_CODE_BLOCK_START + "{\n" + - " \"name\": \"Default\",\n" + - " \"description\": \"Default tenant profile\",\n" + + " \"name\": \"Your name\",\n" + + " \"description\": \"Your description\",\n" + " \"isolatedTbRuleEngine\": false,\n" + " \"profileData\": {\n" + " \"configuration\": {\n" + @@ -162,7 +162,7 @@ public class TenantProfileController extends BaseController { " \"warnThreshold\": 0\n" + " }\n" + " },\n" + - " \"default\": true\n" + + " \"default\": false\n" + "}" + MARKDOWN_CODE_BLOCK_END + "Remove 'id', from the request body example (below) to create new Tenant Profile entity." + diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java index e60091a16d..9fe969f58d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.annotations.media.Schema; @@ -51,10 +52,11 @@ public class TenantProfile extends BaseData implements HasName @NoXss @Schema(description = "Description of the tenant profile", example = "Any text") private String description; - @Schema(description = "Default Tenant profile to be used.", example = "true") + @Schema(description = "Default Tenant profile to be used.", example = "false") + @JsonProperty("default") private boolean isDefault; @Schema(description = "If enabled, will push all messages related to this tenant and processed by the rule engine into separate queue. " + - "Useful for complex microservices deployments, to isolate processing of the data for specific tenants", example = "true") + "Useful for complex microservices deployments, to isolate processing of the data for specific tenants", example = "false") private boolean isolatedTbRuleEngine; @Schema(description = "Complex JSON object that contains profile settings: queue configs, max devices, max assets, rate limits, etc.") private transient TenantProfileData profileData; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 40f4b7f42b..51738c75be 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.tenant.profile; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -23,6 +24,7 @@ import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.TenantProfileType; +@Schema @AllArgsConstructor @NoArgsConstructor @Builder @@ -41,34 +43,61 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxOtaPackagesInBytes; private long maxResourceSize; + @Schema(example = "1000:1,20000:60") private String transportTenantMsgRateLimit; + @Schema(example = "1000:1,20000:60") private String transportTenantTelemetryMsgRateLimit; + @Schema(example = "1000:1,20000:60") private String transportTenantTelemetryDataPointsRateLimit; + @Schema(example = "20:1,600:60") private String transportDeviceMsgRateLimit; + @Schema(example = "20:1,600:60") private String transportDeviceTelemetryMsgRateLimit; + @Schema(example = "20:1,600:60") private String transportDeviceTelemetryDataPointsRateLimit; + @Schema(example = "20:1,600:60") private String transportGatewayMsgRateLimit; + @Schema(example = "20:1,600:60") private String transportGatewayTelemetryMsgRateLimit; + @Schema(example = "20:1,600:60") private String transportGatewayTelemetryDataPointsRateLimit; + @Schema(example = "20:1,600:60") private String transportGatewayDeviceMsgRateLimit; + @Schema(example = "20:1,600:60") private String transportGatewayDeviceTelemetryMsgRateLimit; + @Schema(example = "20:1,600:60") private String transportGatewayDeviceTelemetryDataPointsRateLimit; + @Schema(example = "20:1,600:60") private String tenantEntityExportRateLimit; + @Schema(example = "20:1,600:60") private String tenantEntityImportRateLimit; + @Schema(example = "20:1,600:60") private String tenantNotificationRequestsRateLimit; + @Schema(example = "20:1,600:60") private String tenantNotificationRequestsPerRuleRateLimit; + @Schema(example = "10000000") private long maxTransportMessages; + @Schema(example = "10000000") private long maxTransportDataPoints; + @Schema(example = "4000000") private long maxREExecutions; + @Schema(example = "5000000") private long maxJSExecutions; + @Schema(example = "5000000") private long maxTbelExecutions; + @Schema(example = "0") private long maxDPStorageDays; + @Schema(example = "50") private int maxRuleNodeExecutionsPerMessage; + @Schema(example = "0") private long maxEmails; + @Schema(example = "true") private Boolean smsEnabled; + @Schema(example = "0") private long maxSms; + @Schema(example = "1000") private long maxCreatedAlarms; private String tenantServerRestLimitsConfiguration; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileConfiguration.java index d3238b20f8..0662765455 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/TenantProfileConfiguration.java @@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; +import io.swagger.v3.oas.annotations.media.Schema; import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.TenantProfileType; @@ -31,6 +33,7 @@ import java.io.Serializable; property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = DefaultTenantProfileConfiguration.class, name = "DEFAULT")}) +@Schema(discriminatorProperty = "type", discriminatorMapping = {@DiscriminatorMapping(value = "DEFAULT", schema = DefaultTenantProfileConfiguration.class)}) public interface TenantProfileConfiguration extends Serializable { @JsonIgnore From 93e62dd1e84c9c63fc496ea2f8b137695a5ff1d2 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 14 Jun 2024 18:13:05 +0300 Subject: [PATCH 11/16] Handle TenantNotFoundException for stale subscriptions (#10973) * Handle TenantNotFoundException for stale subscriptions * Minor refactoring for stale subs cleanup --- .../DefaultTbLocalSubscriptionService.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java index 8abb78d9c1..100636ad5d 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.service.subscription; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.context.event.EventListener; @@ -24,9 +26,9 @@ import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.AttributeScope; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.AlarmInfo; +import org.thingsboard.server.common.data.exception.TenantNotFoundException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.Aggregation; @@ -51,8 +53,6 @@ import org.thingsboard.server.service.ws.notification.sub.NotificationsSubscript import org.thingsboard.server.service.ws.telemetry.sub.AlarmSubscriptionUpdate; import org.thingsboard.server.service.ws.telemetry.sub.TelemetrySubscriptionUpdate; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.PreDestroy; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -143,7 +143,20 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer * Even if we cache locally the list of active subscriptions by entity id, it is still time-consuming operation to get them from cache * Since number of subscriptions is usually much less than number of devices that are pushing data. */ - subscriptionsByEntityId.values().forEach(sub -> pushSubEventToManagerService(sub.getTenantId(), sub.getEntityId(), sub.toEvent(ComponentLifecycleEvent.UPDATED))); + Set staleSubs = new HashSet<>(); + subscriptionsByEntityId.forEach((id, sub) -> { + try { + pushSubEventToManagerService(sub.getTenantId(), sub.getEntityId(), sub.toEvent(ComponentLifecycleEvent.UPDATED)); + } catch (TenantNotFoundException e) { + staleSubs.add(id); + log.warn("Cleaning up stale subscription {} for tenant {} due to TenantNotFoundException", id, sub.getTenantId()); + } catch (Exception e) { + log.error("Failed to push subscription {} to manager service", sub, e); + } + }); + if (!staleSubs.isEmpty()) { + staleSubs.forEach(subscriptionsByEntityId::remove); + } } } From 1df93833cb94fc4b3c781f598e2bb2c790c739bf Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 17 Jun 2024 12:49:28 +0300 Subject: [PATCH 12/16] Refactor stale subs cleanup --- .../DefaultTbLocalSubscriptionService.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java index 100636ad5d..9367439d4f 100644 --- a/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java +++ b/application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java @@ -155,7 +155,15 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer } }); if (!staleSubs.isEmpty()) { - staleSubs.forEach(subscriptionsByEntityId::remove); + subsLock.lock(); + try { + staleSubs.forEach(entityId -> { + subscriptionsByEntityId.remove(entityId); + entityUpdates.remove(entityId); + }); + } finally { + subsLock.unlock(); + } } } } From b7891dfdd9940bea7026eaed296d029072b5d4a7 Mon Sep 17 00:00:00 2001 From: Kulikov <44275303+nickAS21@users.noreply.github.com> Date: Tue, 18 Jun 2024 11:45:04 +0300 Subject: [PATCH 13/16] fix_bug: lwm2m monitoring (#11025) --- .../monitoring/client/Lwm2mClient.java | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/monitoring/src/main/java/org/thingsboard/monitoring/client/Lwm2mClient.java b/monitoring/src/main/java/org/thingsboard/monitoring/client/Lwm2mClient.java index 4b95ae1aac..381aac30a0 100644 --- a/monitoring/src/main/java/org/thingsboard/monitoring/client/Lwm2mClient.java +++ b/monitoring/src/main/java/org/thingsboard/monitoring/client/Lwm2mClient.java @@ -30,10 +30,12 @@ import org.eclipse.leshan.client.californium.LeshanClientBuilder; import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory; import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Server; +import org.eclipse.leshan.client.observer.LwM2mClientObserver; import org.eclipse.leshan.client.resource.BaseInstanceEnabler; import org.eclipse.leshan.client.resource.DummyInstanceEnabler; import org.eclipse.leshan.client.resource.ObjectsInitializer; import org.eclipse.leshan.client.servers.ServerIdentity; +import org.eclipse.leshan.core.ResponseCode; import org.eclipse.leshan.core.californium.EndpointFactory; import org.eclipse.leshan.core.model.InvalidDDFFileException; import org.eclipse.leshan.core.model.LwM2mModel; @@ -42,6 +44,10 @@ import org.eclipse.leshan.core.model.ObjectModel; import org.eclipse.leshan.core.model.StaticModel; import org.eclipse.leshan.core.node.codec.DefaultLwM2mDecoder; import org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder; +import org.eclipse.leshan.core.request.BootstrapRequest; +import org.eclipse.leshan.core.request.DeregisterRequest; +import org.eclipse.leshan.core.request.RegisterRequest; +import org.eclipse.leshan.core.request.UpdateRequest; import org.eclipse.leshan.core.response.ReadResponse; import org.thingsboard.monitoring.util.ResourceUtils; @@ -96,7 +102,7 @@ public class Lwm2mClient extends BaseInstanceEnabler implements Destroyable { LwM2mModel model = new StaticModel(models); ObjectsInitializer initializer = new ObjectsInitializer(model); initializer.setInstancesForObject(SECURITY, security); - initializer.setInstancesForObject(SERVER, new Server(123, TimeUnit.MINUTES.toSeconds(60))); + initializer.setInstancesForObject(SERVER, new Server(123, TimeUnit.MINUTES.toSeconds(5))); initializer.setInstancesForObject(DEVICE, this); initializer.setClassForObject(ACCESS_CONTROL, DummyInstanceEnabler.class); DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); @@ -139,6 +145,89 @@ public class Lwm2mClient extends BaseInstanceEnabler implements Destroyable { builder.setEncoder(new DefaultLwM2mEncoder(false)); leshanClient = builder.build(); + LwM2mClientObserver observer = new LwM2mClientObserver() { + + @Override + public void onBootstrapStarted(ServerIdentity bsserver, BootstrapRequest request) {} + + @Override + public void onBootstrapSuccess(ServerIdentity bsserver, BootstrapRequest request) {} + + @Override + public void onBootstrapFailure(ServerIdentity bsserver, BootstrapRequest request, + ResponseCode responseCode, String errorMessage, Exception cause) {} + + @Override + public void onBootstrapTimeout(ServerIdentity bsserver, BootstrapRequest request) {} + + @Override + public void onRegistrationStarted(ServerIdentity server, RegisterRequest request) { + log.debug("onRegistrationStarted [{}]", request.getEndpointName()); + } + + @Override + public void onRegistrationSuccess(ServerIdentity server, RegisterRequest request, String registrationID) { + log.debug("onRegistrationSuccess [{}] [{}]", request.getEndpointName(), registrationID); + } + + @Override + public void onRegistrationFailure(ServerIdentity server, RegisterRequest request, ResponseCode responseCode, String errorMessage, Exception cause) { + log.debug("onRegistrationFailure [{}] [{}] [{}]", request.getEndpointName(), responseCode, errorMessage); + } + + @Override + public void onRegistrationTimeout(ServerIdentity server, RegisterRequest request) { + log.debug("onRegistrationTimeout [{}]", request.getEndpointName()); + } + + @Override + public void onUpdateStarted(ServerIdentity server, UpdateRequest request) { + log.debug("onUpdateStarted [{}]", request.getRegistrationId()); + } + + @Override + public void onUpdateSuccess(ServerIdentity server, UpdateRequest request) { + log.debug("onUpdateSuccess [{}]", request.getRegistrationId()); + } + + @Override + public void onUpdateFailure(ServerIdentity server, UpdateRequest request, ResponseCode responseCode, String errorMessage, Exception cause) { + log.debug("onUpdateFailure [{}]", request.getRegistrationId()); + } + + @Override + public void onUpdateTimeout(ServerIdentity server, UpdateRequest request) { + log.debug("onUpdateTimeout [{}]", request.getRegistrationId()); + } + + @Override + public void onDeregistrationStarted(ServerIdentity server, DeregisterRequest request) { + log.debug("onDeregistrationStarted [{}]", request.getRegistrationId()); + } + + @Override + public void onDeregistrationSuccess(ServerIdentity server, DeregisterRequest request) { + log.debug("onDeregistrationStarted [{}]", request.getRegistrationId()); + } + + @Override + public void onDeregistrationFailure(ServerIdentity server, DeregisterRequest request, ResponseCode responseCode, String errorMessage, Exception cause) { + log.debug("onDeregistrationFailure [{}] [{}] [{}]", request.getRegistrationId(), responseCode, errorMessage); + } + + @Override + public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) { + log.debug("onDeregistrationTimeout [{}]", request.getRegistrationId()); + } + + @Override + public void onUnexpectedError(Throwable unexpectedError) { + log.debug("onUnexpectedError [{}]", unexpectedError.toString()); + } + + }; + leshanClient.addObserver(observer); + setLeshanClient(leshanClient); leshanClient.start(); From e0e5fa12c9f9a10d2b08dd6af40ab0311add8613 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 19 Jun 2024 12:28:20 +0300 Subject: [PATCH 14/16] "Request Timeout." -> "Request timeout" --- .../components/widget/lib/rpc/persistent-table.component.ts | 2 +- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts index 0ae1bdc4f5..37879144cd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts @@ -517,7 +517,7 @@ class PersistentDatasource implements DataSource { if (!this.executingRpcRequest || rejection.status === 504) { this.subscription.rpcRejection = rejection; if (rejection.status === 504) { - this.subscription.rpcErrorText = 'Request Timeout.'; + this.subscription.rpcErrorText = 'Request timeout'; } else { this.subscription.rpcErrorText = 'Error : ' + rejection.status + ' - ' + rejection.statusText; const error = parseHttpErrorMessage(rejection, this.translate); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 5106b622dd..695c2454ae 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -4088,7 +4088,7 @@ "target-device-is-not-set": "Target device is not set!", "invalid-target-entity": "RPC commands are not supported by {{entityType}} entity.", "failed-to-resolve-target-device": "Failed to resolve target device!", - "request-timeout": "Request Timeout.", + "request-timeout": "Request timeout", "rpc-http-error": "Error: {{status}} - {{statusText}}" } }, From fb47a95bf6d44b788a939deb3576e2c666038876 Mon Sep 17 00:00:00 2001 From: Kulikov <44275303+nickAS21@users.noreply.github.com> Date: Wed, 19 Jun 2024 14:03:59 +0300 Subject: [PATCH 15/16] Add tests for LwM2M device profile validation (#10987) * fix_bug: lwm2m Bootstrap ShortServerId add tests * fix_bug: lwm2m Bootstrap ShortServerId add tests comments1 * fix_bug: lwm2m monitoring * fix_bug: lwm2m Bootstrap tests n- formatting --- .../DeviceProfileDataValidatorTest.java | 52 +++++++++++++++++-- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidatorTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidatorTest.java index 2da8a7ff45..5a4b20d7a8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidatorTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidatorTest.java @@ -45,6 +45,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.BDDMockito.willReturn; import static org.mockito.Mockito.verify; @@ -73,6 +74,9 @@ class DeviceProfileDataValidatorTest { " \"clientOnlyObserveAfterConnect\": 1\n" + " }"; + private static final String msgErrorLwm2mRange = "LwM2M Server ShortServerId must be in range [1 - 65534]!"; + private static final String msgErrorBsRange = "Bootstrap Server ShortServerId must be in range [0 - 65535]!"; + private static final String msgErrorNotNull = " Server ShortServerId must not be null!"; private static final String host = "localhost"; private static final String hostBs = "localhost"; @@ -116,19 +120,50 @@ class DeviceProfileDataValidatorTest { validator.validateDataImpl(tenantId, deviceProfile); verify(validator).validateString("Device profile name", deviceProfile.getName()); } + @Test void testValidateDeviceProfile_Lwm2mBootstrap_ShortServerId_Ok() { Integer shortServerId = 123; Integer shortServerIdBs = 0; - Lwm2mDeviceProfileTransportConfiguration transportConfiguration = - getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsNoSec(shortServerId, shortServerIdBs)); - DeviceProfile deviceProfile = getDeviceProfile(transportConfiguration); + DeviceProfile deviceProfile = getDeviceProfile(shortServerId, shortServerIdBs); validator.validateDataImpl(tenantId, deviceProfile); verify(validator).validateString("Device profile name", deviceProfile.getName()); } - private DeviceProfile getDeviceProfile(Lwm2mDeviceProfileTransportConfiguration transportConfiguration) { + @Test + void testValidateDeviceProfile_Lwm2mShortServerId_Ok_BootstrapShortServerId_null_Error() { + verifyValidationError(123, null, "Bootstrap" + msgErrorNotNull); + } + + @Test + void testValidateDeviceProfile_Lwm2mShortServerId_Ok_BootstrapShortServerId_More_65535_Error() { + verifyValidationError(123, 65536, msgErrorBsRange); + } + + @Test + void testValidateDeviceProfile_Lwm2mShortServerId_Ok_BootstrapShortServerId_Less_0_Error() { + verifyValidationError(123, -1, msgErrorBsRange); + } + + @Test + void testValidateDeviceProfile_Lwm2mShortServerId_null_Error_BootstrapShortServerId_Ok() { + verifyValidationError(null, 1, "LwM2M" + msgErrorNotNull); + } + + @Test + void testValidateDeviceProfile_Lwm2mShortServerId_More_65534_Error_BootstrapShortServerId_Ok() { + verifyValidationError(65535, 111, msgErrorLwm2mRange); + } + + @Test + void testValidateDeviceProfile_Lwm2mShortServerId_Less_1_Error_BootstrapShortServerId_Ok() { + verifyValidationError(0, 111, msgErrorLwm2mRange); + } + + private DeviceProfile getDeviceProfile(Integer shortServerId, Integer shortServerIdBs) { + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = + getTransportConfiguration(OBSERVE_ATTRIBUTES_WITHOUT_PARAMS, getBootstrapServerCredentialsNoSec(shortServerId, shortServerIdBs)); DeviceProfile deviceProfile = new DeviceProfile(); deviceProfile.setName("default"); deviceProfile.setType(DeviceProfileType.DEFAULT); @@ -151,7 +186,7 @@ class DeviceProfileDataValidatorTest { return transportConfiguration; } - private List getBootstrapServerCredentialsNoSec(Integer shortServerId, Integer shortServerIdBs){ + private List getBootstrapServerCredentialsNoSec(Integer shortServerId, Integer shortServerIdBs) { List bootstrap = new ArrayList<>(); bootstrap.add(getBootstrapServerCredentialNoSec(false, shortServerId, shortServerIdBs)); bootstrap.add(getBootstrapServerCredentialNoSec(true, shortServerId, shortServerIdBs)); @@ -168,4 +203,11 @@ class DeviceProfileDataValidatorTest { return bootstrapServerCredential; } + private void verifyValidationError(Integer shortServerId, Integer shortServerIdBs, String msgError) { + DeviceProfile deviceProfile = getDeviceProfile(shortServerId, shortServerIdBs); + assertThatThrownBy(() -> validator.validateDataImpl(tenantId, deviceProfile)) + .hasMessageContaining(msgError); + + } + } From 468c00788268b6680b42485b9e78ec028e83277a Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 20 Jun 2024 15:53:41 +0300 Subject: [PATCH 16/16] Version set to 3.7.1-SNAPSHOT --- application/pom.xml | 2 +- common/actor/pom.xml | 2 +- common/cache/pom.xml | 2 +- common/cluster-api/pom.xml | 2 +- common/coap-server/pom.xml | 2 +- common/dao-api/pom.xml | 2 +- common/data/pom.xml | 2 +- common/edge-api/pom.xml | 2 +- common/message/pom.xml | 2 +- common/pom.xml | 2 +- common/proto/pom.xml | 2 +- common/queue/pom.xml | 2 +- common/script/pom.xml | 2 +- common/script/remote-js-client/pom.xml | 2 +- common/script/script-api/pom.xml | 2 +- common/stats/pom.xml | 2 +- common/transport/coap/pom.xml | 2 +- common/transport/http/pom.xml | 2 +- common/transport/lwm2m/pom.xml | 2 +- common/transport/mqtt/pom.xml | 2 +- common/transport/pom.xml | 2 +- common/transport/snmp/pom.xml | 2 +- common/transport/transport-api/pom.xml | 2 +- common/util/pom.xml | 2 +- common/version-control/pom.xml | 2 +- dao/pom.xml | 2 +- monitoring/pom.xml | 2 +- msa/black-box-tests/pom.xml | 2 +- msa/js-executor/package.json | 2 +- msa/js-executor/pom.xml | 2 +- msa/monitoring/pom.xml | 2 +- msa/pom.xml | 2 +- msa/tb-node/pom.xml | 2 +- msa/tb/pom.xml | 2 +- msa/transport/coap/pom.xml | 2 +- msa/transport/http/pom.xml | 2 +- msa/transport/lwm2m/pom.xml | 2 +- msa/transport/mqtt/pom.xml | 2 +- msa/transport/pom.xml | 2 +- msa/transport/snmp/pom.xml | 2 +- msa/vc-executor-docker/pom.xml | 2 +- msa/vc-executor/pom.xml | 2 +- msa/web-ui/package.json | 2 +- msa/web-ui/pom.xml | 2 +- netty-mqtt/pom.xml | 4 ++-- pom.xml | 2 +- rest-client/pom.xml | 2 +- rule-engine/pom.xml | 2 +- rule-engine/rule-engine-api/pom.xml | 2 +- rule-engine/rule-engine-components/pom.xml | 2 +- tools/pom.xml | 2 +- transport/coap/pom.xml | 2 +- transport/http/pom.xml | 2 +- transport/lwm2m/pom.xml | 2 +- transport/mqtt/pom.xml | 2 +- transport/pom.xml | 2 +- transport/snmp/pom.xml | 2 +- ui-ngx/package.json | 2 +- ui-ngx/pom.xml | 2 +- 59 files changed, 60 insertions(+), 60 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index 902b541566..da2c0bc249 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard application diff --git a/common/actor/pom.xml b/common/actor/pom.xml index 77ea283827..e8e9f9b9c5 100644 --- a/common/actor/pom.xml +++ b/common/actor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/cache/pom.xml b/common/cache/pom.xml index 8a95b2a16d..cfff3d8dba 100644 --- a/common/cache/pom.xml +++ b/common/cache/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml index eda43510b5..8851b99583 100644 --- a/common/cluster-api/pom.xml +++ b/common/cluster-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml index 59640b586d..ddfcaf38e3 100644 --- a/common/coap-server/pom.xml +++ b/common/coap-server/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml index 8b3d7f7f5f..3d3eae4679 100644 --- a/common/dao-api/pom.xml +++ b/common/dao-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/data/pom.xml b/common/data/pom.xml index 4cd63dbc1f..91176d088f 100644 --- a/common/data/pom.xml +++ b/common/data/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml index cf3134f09b..19ed1e0b40 100644 --- a/common/edge-api/pom.xml +++ b/common/edge-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/message/pom.xml b/common/message/pom.xml index 6f650c127b..64b46a9dd2 100644 --- a/common/message/pom.xml +++ b/common/message/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/pom.xml b/common/pom.xml index b12140012d..d3cd3d6928 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard common diff --git a/common/proto/pom.xml b/common/proto/pom.xml index 06ae7293ba..57652f2645 100644 --- a/common/proto/pom.xml +++ b/common/proto/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/queue/pom.xml b/common/queue/pom.xml index cd77d9da07..54d68afe62 100644 --- a/common/queue/pom.xml +++ b/common/queue/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/script/pom.xml b/common/script/pom.xml index b307d328d2..bef66f2c96 100644 --- a/common/script/pom.xml +++ b/common/script/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml index d196dd13ea..07dec58d66 100644 --- a/common/script/remote-js-client/pom.xml +++ b/common/script/remote-js-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT script org.thingsboard.common.script diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml index b760129479..b6d6731067 100644 --- a/common/script/script-api/pom.xml +++ b/common/script/script-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT script org.thingsboard.common.script diff --git a/common/stats/pom.xml b/common/stats/pom.xml index 23c507cc43..5eb96a1e1a 100644 --- a/common/stats/pom.xml +++ b/common/stats/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml index 48bd1cfe9c..5dff87a05b 100644 --- a/common/transport/coap/pom.xml +++ b/common/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml index e27f7cbc1b..d5946c8545 100644 --- a/common/transport/http/pom.xml +++ b/common/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml index 0ec3492c99..d3cd755545 100644 --- a/common/transport/lwm2m/pom.xml +++ b/common/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml index 952b94ea2f..023b9473dc 100644 --- a/common/transport/mqtt/pom.xml +++ b/common/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/transport/pom.xml b/common/transport/pom.xml index 0c5c7a02c9..368dcec7d8 100644 --- a/common/transport/pom.xml +++ b/common/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml index 8ad570fceb..96d714f032 100644 --- a/common/transport/snmp/pom.xml +++ b/common/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.common - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml index 20d0f6e5d2..d1fead6a4a 100644 --- a/common/transport/transport-api/pom.xml +++ b/common/transport/transport-api/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.common - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.common.transport diff --git a/common/util/pom.xml b/common/util/pom.xml index c4cee40e0d..971357ea40 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml index cce6e00501..ec913318a7 100644 --- a/common/version-control/pom.xml +++ b/common/version-control/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT common org.thingsboard.common diff --git a/dao/pom.xml b/dao/pom.xml index 061c130a69..6aa47ec4bd 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard dao diff --git a/monitoring/pom.xml b/monitoring/pom.xml index 2dd8e54f83..2ed55e141f 100644 --- a/monitoring/pom.xml +++ b/monitoring/pom.xml @@ -21,7 +21,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard diff --git a/msa/black-box-tests/pom.xml b/msa/black-box-tests/pom.xml index 4c81e0f31f..ce4de7af17 100644 --- a/msa/black-box-tests/pom.xml +++ b/msa/black-box-tests/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json index d0864c013a..b7871628f8 100644 --- a/msa/js-executor/package.json +++ b/msa/js-executor/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-js-executor", "private": true, - "version": "3.7.0", + "version": "3.7.1", "description": "ThingsBoard JavaScript Executor Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml index 8d3592cbe2..8f67cff304 100644 --- a/msa/js-executor/pom.xml +++ b/msa/js-executor/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/monitoring/pom.xml b/msa/monitoring/pom.xml index 60ff2e3f4a..9c6ff99615 100644 --- a/msa/monitoring/pom.xml +++ b/msa/monitoring/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT msa diff --git a/msa/pom.xml b/msa/pom.xml index 71ddc91412..9faf269a20 100644 --- a/msa/pom.xml +++ b/msa/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard msa diff --git a/msa/tb-node/pom.xml b/msa/tb-node/pom.xml index 708af73ae0..3dab1c01da 100644 --- a/msa/tb-node/pom.xml +++ b/msa/tb-node/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/tb/pom.xml b/msa/tb/pom.xml index 8b4a6d0747..b799572468 100644 --- a/msa/tb/pom.xml +++ b/msa/tb/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/coap/pom.xml b/msa/transport/coap/pom.xml index 5c11fd7adf..fbe4677b8f 100644 --- a/msa/transport/coap/pom.xml +++ b/msa/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/http/pom.xml b/msa/transport/http/pom.xml index b7cdae9c95..efedec74ab 100644 --- a/msa/transport/http/pom.xml +++ b/msa/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/lwm2m/pom.xml b/msa/transport/lwm2m/pom.xml index 19ea2a022c..f3c05af81a 100644 --- a/msa/transport/lwm2m/pom.xml +++ b/msa/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/mqtt/pom.xml b/msa/transport/mqtt/pom.xml index f06777265e..a6d4e265a4 100644 --- a/msa/transport/mqtt/pom.xml +++ b/msa/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard.msa - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.msa.transport diff --git a/msa/transport/pom.xml b/msa/transport/pom.xml index 13ee09c3a7..cd6685b7a5 100644 --- a/msa/transport/pom.xml +++ b/msa/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/transport/snmp/pom.xml b/msa/transport/snmp/pom.xml index 35b3809aae..350f49e7fb 100644 --- a/msa/transport/snmp/pom.xml +++ b/msa/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard.msa transport - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT org.thingsboard.msa.transport diff --git a/msa/vc-executor-docker/pom.xml b/msa/vc-executor-docker/pom.xml index e70656c49b..8111c1370d 100644 --- a/msa/vc-executor-docker/pom.xml +++ b/msa/vc-executor-docker/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/vc-executor/pom.xml b/msa/vc-executor/pom.xml index 081ebd1d3c..e4f4e0afe9 100644 --- a/msa/vc-executor/pom.xml +++ b/msa/vc-executor/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT msa org.thingsboard.msa diff --git a/msa/web-ui/package.json b/msa/web-ui/package.json index 6e7cdd4bc1..6bd7266dd8 100644 --- a/msa/web-ui/package.json +++ b/msa/web-ui/package.json @@ -1,7 +1,7 @@ { "name": "thingsboard-web-ui", "private": true, - "version": "3.7.0", + "version": "3.7.1", "description": "ThingsBoard Web UI Microservice", "main": "server.ts", "bin": "server.js", diff --git a/msa/web-ui/pom.xml b/msa/web-ui/pom.xml index 103a498a61..e592b1a8e9 100644 --- a/msa/web-ui/pom.xml +++ b/msa/web-ui/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT msa org.thingsboard.msa diff --git a/netty-mqtt/pom.xml b/netty-mqtt/pom.xml index b0111b9bcd..7f06a09be2 100644 --- a/netty-mqtt/pom.xml +++ b/netty-mqtt/pom.xml @@ -19,11 +19,11 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard netty-mqtt - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT jar Netty MQTT Client diff --git a/pom.xml b/pom.xml index f75e782bef..13125c67f9 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT pom Thingsboard diff --git a/rest-client/pom.xml b/rest-client/pom.xml index e4a959a7ec..7e38a4119d 100644 --- a/rest-client/pom.xml +++ b/rest-client/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard rest-client diff --git a/rule-engine/pom.xml b/rule-engine/pom.xml index 8902ef0cee..1448fa650e 100644 --- a/rule-engine/pom.xml +++ b/rule-engine/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard rule-engine diff --git a/rule-engine/rule-engine-api/pom.xml b/rule-engine/rule-engine-api/pom.xml index 743baf1ab4..8fe01acdff 100644 --- a/rule-engine/rule-engine-api/pom.xml +++ b/rule-engine/rule-engine-api/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index de48596d70..5ac1421cdb 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -22,7 +22,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT rule-engine org.thingsboard.rule-engine diff --git a/tools/pom.xml b/tools/pom.xml index 38309f35b0..dfd750aa4f 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard tools diff --git a/transport/coap/pom.xml b/transport/coap/pom.xml index c2c0252d9c..bdd27ae21c 100644 --- a/transport/coap/pom.xml +++ b/transport/coap/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/http/pom.xml b/transport/http/pom.xml index 7aaf88b59e..b48c3d801d 100644 --- a/transport/http/pom.xml +++ b/transport/http/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/lwm2m/pom.xml b/transport/lwm2m/pom.xml index d0b961939a..be11fbdf2f 100644 --- a/transport/lwm2m/pom.xml +++ b/transport/lwm2m/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/mqtt/pom.xml b/transport/mqtt/pom.xml index fe2c2be544..a10d3ff9ff 100644 --- a/transport/mqtt/pom.xml +++ b/transport/mqtt/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport org.thingsboard.transport diff --git a/transport/pom.xml b/transport/pom.xml index 49009b83b0..6b1311b949 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard transport diff --git a/transport/snmp/pom.xml b/transport/snmp/pom.xml index 5f236adb9a..a02cd85c58 100644 --- a/transport/snmp/pom.xml +++ b/transport/snmp/pom.xml @@ -21,7 +21,7 @@ org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT transport diff --git a/ui-ngx/package.json b/ui-ngx/package.json index b8a8e4c9a9..f383054ece 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -1,6 +1,6 @@ { "name": "thingsboard", - "version": "3.7.0", + "version": "3.7.1", "scripts": { "ng": "ng", "start": "node --max_old_space_size=8048 ./node_modules/@angular/cli/bin/ng serve --configuration development --host 0.0.0.0 --open", diff --git a/ui-ngx/pom.xml b/ui-ngx/pom.xml index c8b50c4149..931df3a6bc 100644 --- a/ui-ngx/pom.xml +++ b/ui-ngx/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.thingsboard - 3.7.0-SNAPSHOT + 3.7.1-SNAPSHOT thingsboard org.thingsboard