From 59d75bf2f131fe091d33f9e519c51a2fb8bfed64 Mon Sep 17 00:00:00 2001 From: IrynaMatveieva Date: Thu, 11 Apr 2024 19:27:09 +0300 Subject: [PATCH 01/58] 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/58] 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/58] 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/58] 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/58] 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/58] 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/58] 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 c9565719b719e6c5a12abf436173915997c86fd1 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 11 Jun 2024 15:52:05 +0300 Subject: [PATCH 08/58] [PROD-3481] [FIX] changed docker commands button and pop-up window title --- .../src/main/data/json/tenant/dashboards/gateways.json | 8 ++++---- ui-ngx/src/assets/locale/locale.constant-en_US.json | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json index d98daa3632..42c4f199b5 100644 --- a/application/src/main/data/json/tenant/dashboards/gateways.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -181,12 +181,12 @@ "actions": { "actionCellButton": [ { - "name": "Docker commands", + "name": "Launch command", "icon": "terminal", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "customPretty", - "customHtml": "
\n \n

Docker commands

\n \n
\n \n
\n \n
\n \n
\n
\n", + "customHtml": "
\n \n

{{ 'gateway.launch-command' | translate }}

\n \n
\n \n
\n \n
\n \n
\n
\n", "customCss": ".container {\n display: grid;\n grid-template-rows: min-content minmax(auto, 1fr) min-content;\n height: 100%;\n max-height: 100vh;\n width: 600px;\n max-width: 100%;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenCommands();\n\nfunction openCommands() {\n customDialog.customDialog(htmlTemplate, CommandsDialogController, {panelClass: \"test\"}).subscribe();\n}\n\nfunction CommandsDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n", "customResources": [], @@ -1861,7 +1861,7 @@ "padding": "8px", "settings": { "useMarkdownTextFunction": false, - "markdownTextPattern": "
\r\n \r\n
", + "markdownTextPattern": "
\r\n \r\n
", "applyDefaultMarkdownStyle": false, "markdownCss": ".action-buttons-container {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex-direction: row;\r\n height: 100%;\r\n width: 100%;\r\n align-content: center;\r\n}\r\n\r\nbutton {\r\n flex-grow: 1;\r\n margin: 10px;\r\n min-width: 150px;\r\n height: auto;\r\n}" }, @@ -1887,7 +1887,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "customPretty", - "customHtml": "
\n \n

Launch command

\n \n
\n \n
\n \n
\n \n
\n
\n", + "customHtml": "
\n \n

{{ 'gateway.launch-command' | translate }}

\n \n
\n \n
\n \n
\n \n
\n
\n", "customCss": ".container {\n display: grid;\n grid-template-rows: min-content minmax(auto, 1fr) min-content;\n height: 100%;\n max-height: 100vh;\n width: 600px;\n max-width: 100%;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenCommands();\n\nfunction openCommands() {\n customDialog.customDialog(htmlTemplate, CommandsDialogController, {panelClass: \"test\"}).subscribe();\n}\n\nfunction CommandsDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n", "customResources": [], 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 0fa09da5e4..d0270a5c87 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2835,6 +2835,7 @@ "enable-remote-logging": "Enable remote logging", "ellipsis-chips-text": "+ {{count}} more", "launch-gateway": "Launch gateway", + "launch-command": "Launch command", "launch-docker-compose": "Start the gateway using the following command in the terminal from folder with docker-compose.yml file", "logs-configuration": "Logs configuration", "create-new-gateway": "Create a new gateway", From 98f873f0b606f48acb0b2ba5ba61d6dbfa9e52e8 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 10:52:59 +0300 Subject: [PATCH 09/58] [PROD-3727] [FIX] made response topic expression required --- ...teway-service-rpc-connector.component.html | 2 +- ...gateway-service-rpc-connector.component.ts | 34 +++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html index 2d992187f6..6f495984f5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html @@ -35,7 +35,7 @@ {{ 'gateway.rpc.withResponse' | translate }} - {{ 'gateway.rpc.responseTopicExpression' | translate }} + {{ 'gateway.rpc.responseTopicExpression' | translate }}* diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts index 2ea5964b84..cbb11fc059 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, forwardRef, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { ControlValueAccessor, FormArray, @@ -22,6 +22,8 @@ import { FormControl, FormGroup, NG_VALUE_ACCESSOR, + UntypedFormControl, + ValidatorFn, Validators } from '@angular/forms'; import { @@ -53,6 +55,8 @@ import { } from '@shared/components/dialog/json-object-edit-dialog.component'; import { jsonRequired } from '@shared/components/json-object-edit.component'; import { deepClone } from '@core/utils'; +import { takeUntil, tap } from "rxjs/operators"; +import { Subject } from "rxjs"; @Component({ selector: 'tb-gateway-service-rpc-connector', @@ -66,7 +70,7 @@ import { deepClone } from '@core/utils'; } ] }) -export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValueAccessor { +export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, ControlValueAccessor { @Input() connectorType: ConnectorType; @@ -105,6 +109,7 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue private propagateChange = (v: any) => { } + private destroy$ = new Subject(); constructor(private fb: FormBuilder, private dialog: MatDialog,) { @@ -138,6 +143,17 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue } }); this.isMQTTWithResponse = this.fb.control(false); + this.isMQTTWithResponse.valueChanges.pipe( + tap(() => { + this.commandForm.get('responseTopicExpression').updateValueAndValidity(); + }), + takeUntil(this.destroy$), + ).subscribe(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); } connectorParamsFormGroupByType(type: ConnectorType): FormGroup { @@ -148,7 +164,7 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue formGroup = this.fb.group({ methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], requestTopicExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - responseTopicExpression: [null, [Validators.pattern(noLeadTrailSpacesRegex)]], + responseTopicExpression: [null, [this.requiredOnWithResponse(), Validators.pattern(noLeadTrailSpacesRegex)]], responseTimeout: [null, [Validators.min(10), Validators.pattern(this.numbersOnlyPattern)]], valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], }) @@ -424,4 +440,16 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue this.commandForm.patchValue(value, {onlySelf: false}); } } + + private requiredOnWithResponse(): ValidatorFn { + return (control: UntypedFormControl) => { + const withResponse: boolean = this.isMQTTWithResponse?.value; + + if (withResponse && !control.value) { + return { 'required': true }; + } + + return null; + }; + } } From 45472e64fde17fdb5eefeb595555f739996620e3 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 10:55:33 +0300 Subject: [PATCH 10/58] Revert "[PROD-3481] [FIX] changed docker commands button and pop-up window title" This reverts commit c9565719b719e6c5a12abf436173915997c86fd1. --- .../src/main/data/json/tenant/dashboards/gateways.json | 8 ++++---- ui-ngx/src/assets/locale/locale.constant-en_US.json | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json index 42c4f199b5..d98daa3632 100644 --- a/application/src/main/data/json/tenant/dashboards/gateways.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -181,12 +181,12 @@ "actions": { "actionCellButton": [ { - "name": "Launch command", + "name": "Docker commands", "icon": "terminal", "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "customPretty", - "customHtml": "
\n \n

{{ 'gateway.launch-command' | translate }}

\n \n
\n \n
\n \n
\n \n
\n
\n", + "customHtml": "
\n \n

Docker commands

\n \n
\n \n
\n \n
\n \n
\n
\n", "customCss": ".container {\n display: grid;\n grid-template-rows: min-content minmax(auto, 1fr) min-content;\n height: 100%;\n max-height: 100vh;\n width: 600px;\n max-width: 100%;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenCommands();\n\nfunction openCommands() {\n customDialog.customDialog(htmlTemplate, CommandsDialogController, {panelClass: \"test\"}).subscribe();\n}\n\nfunction CommandsDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n", "customResources": [], @@ -1861,7 +1861,7 @@ "padding": "8px", "settings": { "useMarkdownTextFunction": false, - "markdownTextPattern": "
\r\n \r\n
", + "markdownTextPattern": "
\r\n \r\n
", "applyDefaultMarkdownStyle": false, "markdownCss": ".action-buttons-container {\r\n display: flex;\r\n flex-wrap: wrap;\r\n flex-direction: row;\r\n height: 100%;\r\n width: 100%;\r\n align-content: center;\r\n}\r\n\r\nbutton {\r\n flex-grow: 1;\r\n margin: 10px;\r\n min-width: 150px;\r\n height: auto;\r\n}" }, @@ -1887,7 +1887,7 @@ "useShowWidgetActionFunction": null, "showWidgetActionFunction": "return true;", "type": "customPretty", - "customHtml": "
\n \n

{{ 'gateway.launch-command' | translate }}

\n \n
\n \n
\n \n
\n \n
\n
\n", + "customHtml": "
\n \n

Launch command

\n \n
\n \n
\n \n
\n \n
\n
\n", "customCss": ".container {\n display: grid;\n grid-template-rows: min-content minmax(auto, 1fr) min-content;\n height: 100%;\n max-height: 100vh;\n width: 600px;\n max-width: 100%;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\n\nopenCommands();\n\nfunction openCommands() {\n customDialog.customDialog(htmlTemplate, CommandsDialogController, {panelClass: \"test\"}).subscribe();\n}\n\nfunction CommandsDialogController(instance) {\n let vm = instance;\n \n vm.entityId = entityId.id;\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n}\n", "customResources": [], 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 d0270a5c87..0fa09da5e4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2835,7 +2835,6 @@ "enable-remote-logging": "Enable remote logging", "ellipsis-chips-text": "+ {{count}} more", "launch-gateway": "Launch gateway", - "launch-command": "Launch command", "launch-docker-compose": "Start the gateway using the following command in the terminal from folder with docker-compose.yml file", "logs-configuration": "Logs configuration", "create-new-gateway": "Create a new gateway", From 4990db2dc02e6cba89407dd63811c380a490c06b Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 10:58:42 +0300 Subject: [PATCH 11/58] [PROD-3727] [FIX] refactoring --- .../gateway-service-rpc-connector.component.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts index cbb11fc059..6598b332ca 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts @@ -143,12 +143,7 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C } }); this.isMQTTWithResponse = this.fb.control(false); - this.isMQTTWithResponse.valueChanges.pipe( - tap(() => { - this.commandForm.get('responseTopicExpression').updateValueAndValidity(); - }), - takeUntil(this.destroy$), - ).subscribe(); + this.observeMQTTWithResponse(); } ngOnDestroy(): void { @@ -452,4 +447,13 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C return null; }; } + + private observeMQTTWithResponse(): void { + this.isMQTTWithResponse.valueChanges.pipe( + tap(() => { + this.commandForm.get('responseTopicExpression').updateValueAndValidity(); + }), + takeUntil(this.destroy$), + ).subscribe(); + } } From 58f677cd4fa2501376a8d9ed3ca8f64c7f2f567a Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 11:55:52 +0300 Subject: [PATCH 12/58] [PROD-3727] [FIX] refactoring --- ...teway-service-rpc-connector.component.html | 2 +- ...gateway-service-rpc-connector.component.ts | 20 +++++-------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html index 6f495984f5..2d992187f6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html @@ -35,7 +35,7 @@ {{ 'gateway.rpc.withResponse' | translate }} - {{ 'gateway.rpc.responseTopicExpression' | translate }}* + {{ 'gateway.rpc.responseTopicExpression' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts index 6598b332ca..fe3c600b50 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts @@ -159,7 +159,7 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C formGroup = this.fb.group({ methodFilter: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], requestTopicExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - responseTopicExpression: [null, [this.requiredOnWithResponse(), Validators.pattern(noLeadTrailSpacesRegex)]], + responseTopicExpression: [{ value: null, disabled: true }, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], responseTimeout: [null, [Validators.min(10), Validators.pattern(this.numbersOnlyPattern)]], valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], }) @@ -436,22 +436,12 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C } } - private requiredOnWithResponse(): ValidatorFn { - return (control: UntypedFormControl) => { - const withResponse: boolean = this.isMQTTWithResponse?.value; - - if (withResponse && !control.value) { - return { 'required': true }; - } - - return null; - }; - } - private observeMQTTWithResponse(): void { this.isMQTTWithResponse.valueChanges.pipe( - tap(() => { - this.commandForm.get('responseTopicExpression').updateValueAndValidity(); + tap((isActive: boolean) => { + const responseControl = this.commandForm.get('responseTopicExpression'); + isActive ? responseControl.enable() : responseControl.disable(); + responseControl.updateValueAndValidity(); }), takeUntil(this.destroy$), ).subscribe(); From 05e8db56824abb6c76d64a4e278f1177daa59ff8 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 16:00:33 +0300 Subject: [PATCH 13/58] Revert "[PROD-3407] [FIX] fixed gateway dashboard sorting by status" This reverts commit bb9e81db1e37a765872fd5f3b1c54cb3be260c0b. --- .../gateway/gateway-connectors.component.ts | 24 +++++-------------- .../src/app/shared/models/page/sort-order.ts | 3 --- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts index 0ee3a75ca6..4f1b6b72a9 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts @@ -35,7 +35,7 @@ import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetr import { PageComponent } from '@shared/components/page.component'; import { PageLink } from '@shared/models/page/page-link'; import { AttributeDatasource } from '@home/models/datasource/attribute-datasource'; -import { Direction, SORT_ASC_PINNED_VALUE, SORT_DESC_PINNED_VALUE, SortOrder } from '@shared/models/page/sort-order'; +import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { MatSort } from '@angular/material/sort'; import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; import { MatTableDataSource } from '@angular/material/table'; @@ -238,26 +238,14 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie this.dataSource.sort = this.sort; this.dataSource.sortingDataAccessor = (data: AttributeData, sortHeaderId: string) => { - switch (sortHeaderId) { - case 'syncStatus': - return this.isConnectorSynced(data) ? 1 : 0; - - case 'enabled': - return this.activeConnectors.includes(data.key) ? 1 : 0; - - case 'errors': - const errors = this.getErrorsCount(data); - if (typeof errors === 'string') { - return this.sort.direction.toUpperCase() === Direction.DESC ? SORT_DESC_PINNED_VALUE : SORT_ASC_PINNED_VALUE; - } - return errors; - - default: - return data[sortHeaderId] || data.value[sortHeaderId]; + if (sortHeaderId === 'syncStatus') { + return this.isConnectorSynced(data) ? 1 : 0; + } else if (sortHeaderId === 'enabled') { + return this.activeConnectors.includes(data.key) ? 1 : 0; } + return data[sortHeaderId] || data.value[sortHeaderId]; }; - if (this.device) { if (this.device.id === NULL_UUID) { return; diff --git a/ui-ngx/src/app/shared/models/page/sort-order.ts b/ui-ngx/src/app/shared/models/page/sort-order.ts index cece5e73e9..9174b0c970 100644 --- a/ui-ngx/src/app/shared/models/page/sort-order.ts +++ b/ui-ngx/src/app/shared/models/page/sort-order.ts @@ -25,9 +25,6 @@ export enum Direction { DESC = 'DESC' } -export const SORT_DESC_PINNED_VALUE = -1; -export const SORT_ASC_PINNED_VALUE = 9999; - export function sortOrderFromString(strSortOrder: string): SortOrder { let property: string; let direction = Direction.ASC; From 25ce2d0800cfd4ae01895da0c937c26a9b983784 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 16:05:55 +0300 Subject: [PATCH 14/58] [PROD-3727] [FIX] refactoring --- .../lib/gateway/gateway-service-rpc-connector.component.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts index fe3c600b50..0d9a8be239 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts @@ -22,8 +22,6 @@ import { FormControl, FormGroup, NG_VALUE_ACCESSOR, - UntypedFormControl, - ValidatorFn, Validators } from '@angular/forms'; import { From 3f01672284107bf1e25fd93650d65413274ef6df Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 16:08:26 +0300 Subject: [PATCH 15/58] [PROD-3727] [FIX] refactoring --- .../lib/gateway/gateway-service-rpc-connector.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts index 0d9a8be239..77802935fb 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts @@ -439,7 +439,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, OnDestroy, C tap((isActive: boolean) => { const responseControl = this.commandForm.get('responseTopicExpression'); isActive ? responseControl.enable() : responseControl.disable(); - responseControl.updateValueAndValidity(); }), takeUntil(this.destroy$), ).subscribe(); From 73d0ba91da1bf060837224c1409514a9c198c7a0 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 16:12:37 +0300 Subject: [PATCH 16/58] Revert "Revert "[PROD-3407] [FIX] fixed gateway dashboard sorting by status"" This reverts commit 05e8db56824abb6c76d64a4e278f1177daa59ff8. --- .../gateway/gateway-connectors.component.ts | 24 ++++++++++++++----- .../src/app/shared/models/page/sort-order.ts | 3 +++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts index 4f1b6b72a9..0ee3a75ca6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts @@ -35,7 +35,7 @@ import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetr import { PageComponent } from '@shared/components/page.component'; import { PageLink } from '@shared/models/page/page-link'; import { AttributeDatasource } from '@home/models/datasource/attribute-datasource'; -import { Direction, SortOrder } from '@shared/models/page/sort-order'; +import { Direction, SORT_ASC_PINNED_VALUE, SORT_DESC_PINNED_VALUE, SortOrder } from '@shared/models/page/sort-order'; import { MatSort } from '@angular/material/sort'; import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; import { MatTableDataSource } from '@angular/material/table'; @@ -238,14 +238,26 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie this.dataSource.sort = this.sort; this.dataSource.sortingDataAccessor = (data: AttributeData, sortHeaderId: string) => { - if (sortHeaderId === 'syncStatus') { - return this.isConnectorSynced(data) ? 1 : 0; - } else if (sortHeaderId === 'enabled') { - return this.activeConnectors.includes(data.key) ? 1 : 0; + switch (sortHeaderId) { + case 'syncStatus': + return this.isConnectorSynced(data) ? 1 : 0; + + case 'enabled': + return this.activeConnectors.includes(data.key) ? 1 : 0; + + case 'errors': + const errors = this.getErrorsCount(data); + if (typeof errors === 'string') { + return this.sort.direction.toUpperCase() === Direction.DESC ? SORT_DESC_PINNED_VALUE : SORT_ASC_PINNED_VALUE; + } + return errors; + + default: + return data[sortHeaderId] || data.value[sortHeaderId]; } - return data[sortHeaderId] || data.value[sortHeaderId]; }; + if (this.device) { if (this.device.id === NULL_UUID) { return; diff --git a/ui-ngx/src/app/shared/models/page/sort-order.ts b/ui-ngx/src/app/shared/models/page/sort-order.ts index 9174b0c970..cece5e73e9 100644 --- a/ui-ngx/src/app/shared/models/page/sort-order.ts +++ b/ui-ngx/src/app/shared/models/page/sort-order.ts @@ -25,6 +25,9 @@ export enum Direction { DESC = 'DESC' } +export const SORT_DESC_PINNED_VALUE = -1; +export const SORT_ASC_PINNED_VALUE = 9999; + export function sortOrderFromString(strSortOrder: string): SortOrder { let property: string; let direction = Direction.ASC; From b94a504046079b64b53b83a80eff084a5e1e1d74 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 16:16:02 +0300 Subject: [PATCH 17/58] [PROD-3407] [FIX] refactoring --- .../widget/lib/gateway/gateway-connectors.component.ts | 5 ++--- ui-ngx/src/app/shared/models/page/sort-order.ts | 3 --- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts index 0ee3a75ca6..bf94e6021d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts @@ -35,7 +35,7 @@ import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetr import { PageComponent } from '@shared/components/page.component'; import { PageLink } from '@shared/models/page/page-link'; import { AttributeDatasource } from '@home/models/datasource/attribute-datasource'; -import { Direction, SORT_ASC_PINNED_VALUE, SORT_DESC_PINNED_VALUE, SortOrder } from '@shared/models/page/sort-order'; +import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { MatSort } from '@angular/material/sort'; import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; import { MatTableDataSource } from '@angular/material/table'; @@ -248,7 +248,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie case 'errors': const errors = this.getErrorsCount(data); if (typeof errors === 'string') { - return this.sort.direction.toUpperCase() === Direction.DESC ? SORT_DESC_PINNED_VALUE : SORT_ASC_PINNED_VALUE; + return this.sort.direction.toUpperCase() === Direction.DESC ? -1 : Infinity; } return errors; @@ -257,7 +257,6 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie } }; - if (this.device) { if (this.device.id === NULL_UUID) { return; diff --git a/ui-ngx/src/app/shared/models/page/sort-order.ts b/ui-ngx/src/app/shared/models/page/sort-order.ts index cece5e73e9..9174b0c970 100644 --- a/ui-ngx/src/app/shared/models/page/sort-order.ts +++ b/ui-ngx/src/app/shared/models/page/sort-order.ts @@ -25,9 +25,6 @@ export enum Direction { DESC = 'DESC' } -export const SORT_DESC_PINNED_VALUE = -1; -export const SORT_ASC_PINNED_VALUE = 9999; - export function sortOrderFromString(strSortOrder: string): SortOrder { let property: string; let direction = Direction.ASC; From 0b73b8c35e92de29377b780100dd88296f2bac14 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 17:04:14 +0300 Subject: [PATCH 18/58] [PROD-3440] [FIX] changed status column title to 'level' in logs --- .../components/widget/lib/gateway/gateway-logs.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html index d36a4c0a51..ba6c627edf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html @@ -31,7 +31,7 @@ - {{ 'widgets.gateway.status' | translate }} + {{ 'widgets.liquid-level-card.level' | translate }} {{ attribute.status }} From e77e225bda4790ba520e43ac3b1f4448946c55bb Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 17:25:15 +0300 Subject: [PATCH 19/58] [PROD-3440] [FIX] refactoring --- .../components/widget/lib/gateway/gateway-logs.component.html | 2 +- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- ui-ngx/src/assets/locale/locale.constant-lt_LT.json | 1 - ui-ngx/src/assets/locale/locale.constant-pl_PL.json | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html index ba6c627edf..c77de6eb82 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html @@ -31,7 +31,7 @@ - {{ 'widgets.liquid-level-card.level' | translate }} + {{ 'widgets.gateway.level' | translate }} {{ attribute.status }} 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 0fa09da5e4..0e4a31bbc4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -6078,8 +6078,8 @@ "event-key-contains": "Event key contains...", "show-connector": "Show for the connector", "connector-state-param-key": "Connector state parameter key", - "status": "Status", "message": "Message", + "level": "Level", "created-time": "Created time" }, "gauge": { diff --git a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json index 5ca808146c..a993ff1146 100644 --- a/ui-ngx/src/assets/locale/locale.constant-lt_LT.json +++ b/ui-ngx/src/assets/locale/locale.constant-lt_LT.json @@ -6785,7 +6785,6 @@ "event-key-contains": "Event key contains...", "show-connector": "Show for the connector", "connector-state-param-key": "Connector state parameter key", - "status": "Status", "message": "Message", "created-time": "Created time" }, diff --git a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json index b30d4afc38..fef242268d 100644 --- a/ui-ngx/src/assets/locale/locale.constant-pl_PL.json +++ b/ui-ngx/src/assets/locale/locale.constant-pl_PL.json @@ -6808,7 +6808,6 @@ "event-key-contains": "Klucz zdarzenia zawiera...", "show-connector": "Show for the connector", "connector-state-param-key": "Connector state parameter key", - "status": "Status", "message": "Message", "created-time": "Created time" }, From 159c97ac67248216bbbd5afef39271ac2bfb9b63 Mon Sep 17 00:00:00 2001 From: Shvaika Dmytro Date: Wed, 12 Jun 2024 18:00:31 +0300 Subject: [PATCH 20/58] 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 e05df28ee309340fa92bd05056c680c004fc16b2 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 12 Jun 2024 18:04:19 +0300 Subject: [PATCH 21/58] [PROD-3752] [FIX] Removed dot from the message when connector created/updated successfully --- ui-ngx/src/assets/locale/locale.constant-en_US.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 0fa09da5e4..ede6a9916e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2804,8 +2804,8 @@ "copy-username": "Copy username", "copy-password": "Copy password", "copy-client-id": "Copy client ID", - "connector-created": "Connector created.", - "connector-updated": "Connector updated.", + "connector-created": "Connector created", + "connector-updated": "Connector updated", "rpc-command-save-template": "Save Template", "rpc-command-send": "Send", "rpc-command-result": "Response", From 41187693663b0ebb369ede6190562b8dd49ed09c Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 13 Jun 2024 11:49:01 +0300 Subject: [PATCH 22/58] [PROD-3709] [FIX] Added adaptive height for datamaping dialog --- .../lib/gateway/dialog/mapping-dialog.component.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss index db98bc07cb..2237716a7c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss @@ -35,6 +35,14 @@ .mat-mdc-dialog-content { max-height: 670px; height: 670px; + + @media screen and (max-height: 800px) { + $mat-toolbar-height: 64px; + $mat-mdc-dialog-actions: 53px; + $dialog-offset: 24px; + $content-height: calc(100vh - #{$mat-toolbar-height} - #{$mat-mdc-dialog-actions} - #{$dialog-offset}); + max-height: $content-height; + } } .ellipsis-chips-container { From b0d897c68db081670484a3d6df4baac9e9541563 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 13 Jun 2024 12:05:53 +0300 Subject: [PATCH 23/58] [PROD-3709] [FIX] refactoring --- .../lib/gateway/dialog/mapping-dialog.component.scss | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss index 2237716a7c..d002a0fc87 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/mapping-dialog.component.scss @@ -33,16 +33,7 @@ } .mat-mdc-dialog-content { - max-height: 670px; height: 670px; - - @media screen and (max-height: 800px) { - $mat-toolbar-height: 64px; - $mat-mdc-dialog-actions: 53px; - $dialog-offset: 24px; - $content-height: calc(100vh - #{$mat-toolbar-height} - #{$mat-mdc-dialog-actions} - #{$dialog-offset}); - max-height: $content-height; - } } .ellipsis-chips-container { From 9f0c998bb37db3fb4f008fa411aff4fd6f7e62f7 Mon Sep 17 00:00:00 2001 From: rusikv Date: Thu, 13 Jun 2024 12:26:25 +0300 Subject: [PATCH 24/58] UI: disabled autocomplete and added missing duplicate name error icon for connector name fields in gateway dashboard --- .../gateway/dialog/add-connector-dialog.component.html | 2 +- .../widget/lib/gateway/gateway-connectors.component.html | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html index ec1fa9a87f..3dfc831113 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html @@ -44,7 +44,7 @@
gateway.name
- + gateway.name
- + warning From 7420d66bb732c237f76f76fad6d5554fb71273fb Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 13 Jun 2024 12:48:02 +0300 Subject: [PATCH 25/58] [PROD-3687] [FIX] added asterisk symbol to fields 'name' and 'profile name' in MQTT Connector Device Configuration --- .../connectors-configuration/device-info-table.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table.component.html index 2a3108b8b4..c3fe478ef2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/device-info-table.component.html @@ -28,7 +28,7 @@
-
gateway.device-info.name
+
gateway.device-info.name
@@ -61,7 +61,7 @@
-
gateway.device-info.profile-name
+
gateway.device-info.profile-name
From f97d08c59a7834f96512c181f09b551875c89409 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 13 Jun 2024 14:46:30 +0300 Subject: [PATCH 26/58] [PROD-3650] [FIX] Changed method field in SNMP Connector --- ...-service-rpc-connector-templates.component.html | 5 ++++- ...ay-service-rpc-connector-templates.component.ts | 4 +++- .../widget/lib/gateway/gateway-widget.models.ts | 14 +++++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html index c0f6997a36..9a8a0b4152 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html @@ -47,7 +47,10 @@
- {{config.value}}
+ +
+ {{ config.value }} + {{ 'gateway.rpc.' + SNMPMethodsTitles[config.value] | translate }} 0; public readonly isObject = (value: any) => isLiteralObject(value); + public readonly SNMPMethodsTitles = SNMPMethodsTitles; constructor(private attributeService: AttributeService) { } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts index 0e143cff36..af349aa99e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts @@ -261,6 +261,18 @@ export enum SNMPMethods { WALKS = "walk" } +export enum SNMPMethodsTitles { + set = 'set', + multiset = "multiset", + get = "get", + bulkwalk = "bulk-walk", + table = "table", + multiget = "multi-get", + getnext = "get-next", + bulkget = "bulk-get", + walk = "walk" +} + export const SNMPMethodsTranslations = new Map([ [SNMPMethods.SET, 'gateway.rpc.set'], [SNMPMethods.MULTISET, 'gateway.rpc.multiset'], @@ -269,7 +281,7 @@ export const SNMPMethodsTranslations = new Map([ [SNMPMethods.TABLE, 'gateway.rpc.table'], [SNMPMethods.MULTIGET, 'gateway.rpc.multi-get'], [SNMPMethods.GETNEXT, 'gateway.rpc.get-next'], - [SNMPMethods.BULKGET, 'gateway.rpc.bul-kget'], + [SNMPMethods.BULKGET, 'gateway.rpc.bulk-get'], [SNMPMethods.WALKS, 'gateway.rpc.walk'] ]) From 797902ced9e4287937cee9e5023a4e4c91bcbde2 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 13 Jun 2024 15:33:13 +0300 Subject: [PATCH 27/58] [PROD-3650] [FIX] refactoring --- ...ay-service-rpc-connector-templates.component.html | 2 +- ...eway-service-rpc-connector-templates.component.ts | 4 ++-- .../widget/lib/gateway/gateway-widget.models.ts | 12 ------------ 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html index 9a8a0b4152..d161f402dd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html @@ -50,7 +50,7 @@
{{ config.value }} - {{ 'gateway.rpc.' + SNMPMethodsTitles[config.value] | translate }} + {{ SNMPMethodsTranslations.get(config.value) | translate }} 0; public readonly isObject = (value: any) => isLiteralObject(value); - public readonly SNMPMethodsTitles = SNMPMethodsTitles; + public readonly SNMPMethodsTranslations = SNMPMethodsTranslations; constructor(private attributeService: AttributeService) { } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts index af349aa99e..3c02deb49b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts @@ -261,18 +261,6 @@ export enum SNMPMethods { WALKS = "walk" } -export enum SNMPMethodsTitles { - set = 'set', - multiset = "multiset", - get = "get", - bulkwalk = "bulk-walk", - table = "table", - multiget = "multi-get", - getnext = "get-next", - bulkget = "bulk-get", - walk = "walk" -} - export const SNMPMethodsTranslations = new Map([ [SNMPMethods.SET, 'gateway.rpc.set'], [SNMPMethods.MULTISET, 'gateway.rpc.multiset'], From 17be2154ce193a65f6923f317223f89ef41c8b68 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 13 Jun 2024 16:04:55 +0300 Subject: [PATCH 28/58] 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 8e11d04a86fb6c87a825e1c54b8a92e8759d59ed Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 13 Jun 2024 16:24:49 +0300 Subject: [PATCH 29/58] [PROD-3645] [FIX] Added max width to the display of the template name --- ...y-service-rpc-connector-templates.component.html | 2 +- ...y-service-rpc-connector-templates.component.scss | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html index c0f6997a36..b2e0e3f1c2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html @@ -18,7 +18,7 @@
{{ 'gateway.rpc.templates-title' | translate }}
- + {{template.name}} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss index 7e5c4af1f1..02c2348bca 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.scss @@ -89,6 +89,19 @@ } } + .template-name { + max-width: 170px; + overflow: scroll; + } + + ::ng-deep .mat-expansion-panel-header-title { + flex: 1; + margin: 0; + } + + ::ng-deep .mat-expansion-panel-header-description { + flex: 0; + } } From 2d47c1350322bc2f083c3cccfbd13fc024579b24 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 13 Jun 2024 17:12:18 +0300 Subject: [PATCH 30/58] [PROD-3645] [FIX] changed to three dots with tooltip --- ...gateway-service-rpc-connector-templates.component.html | 2 +- ...gateway-service-rpc-connector-templates.component.scss | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html index b2e0e3f1c2..174d044a7b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector-templates.component.html @@ -19,7 +19,7 @@ - {{template.name}} + {{template.name}}
+
+
gateway.connectors-table-class
+
+ + + +
+
+
+
gateway.connectors-table-key
+
+ + + +
+
gateway.remote-logging-level
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts index 98261a8e57..eb55765887 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts @@ -66,6 +66,8 @@ export class AddConnectorDialogComponent extends DialogComponent
+
+
gateway.connectors-table-class
+
+ + + +
+
+
+
gateway.connectors-table-key
+
+ + + +
+
gateway.logs-configuration
From 150f57dc381bbffe9a8d823f25fe73650bee5844 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 14 Jun 2024 16:54:00 +0300 Subject: [PATCH 35/58] Removed toggle 'fill configuration with default values' for GRCP and Custom Connectors --- .../add-connector-dialog.component.html | 3 ++- .../dialog/add-connector-dialog.component.ts | 23 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html index ec1fa9a87f..2d2193667b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.html @@ -68,7 +68,8 @@
-
+
{{ 'gateway.fill-connector-defaults' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts index 98261a8e57..34250afc60 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/dialog/add-connector-dialog.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Inject, OnDestroy } from '@angular/core'; +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -33,6 +33,7 @@ import { } from '@home/components/widget/lib/gateway/gateway-widget.models'; import { Subject } from 'rxjs'; import { ResourcesService } from '@core/services/resources.service'; +import { takeUntil, tap } from "rxjs/operators"; @Component({ selector: 'tb-add-connector-dialog', @@ -40,7 +41,7 @@ import { ResourcesService } from '@core/services/resources.service'; styleUrls: ['./add-connector-dialog.component.scss'], providers: [], }) -export class AddConnectorDialogComponent extends DialogComponent> implements OnDestroy { +export class AddConnectorDialogComponent extends DialogComponent> implements OnInit, OnDestroy { connectorForm: UntypedFormGroup; @@ -69,6 +70,10 @@ export class AddConnectorDialogComponent extends DialogComponent { + const useDefaultControl = this.connectorForm.get('useDefaults'); + if (type === ConnectorType.GRPC || type === ConnectorType.CUSTOM) { + useDefaultControl.setValue(false); + } else if (!useDefaultControl.value) { + useDefaultControl.setValue(true); + } + }), + takeUntil(this.destroy$), + ).subscribe() + } } From 3ea1e5706ecdbd1ab7e5ab4bd907eaba1b999a5a Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Fri, 14 Jun 2024 17:42:54 +0300 Subject: [PATCH 36/58] 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 37/58] 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 4c0ee73a70ac02c1bbe51521e0ad7364fdec8a3c Mon Sep 17 00:00:00 2001 From: mpetrov Date: Fri, 14 Jun 2024 18:40:56 +0300 Subject: [PATCH 38/58] Fixed Command launcher pop up and dashboard open after creation --- application/src/main/data/json/tenant/dashboards/gateways.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json index 42c4f199b5..46b4cff8b0 100644 --- a/application/src/main/data/json/tenant/dashboards/gateways.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -248,7 +248,7 @@ "type": "customPretty", "customHtml": "
\r\n \r\n

Add gateway

\r\n \r\n \r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n \r\n Name\r\n \r\n \r\n Gateway name is required.\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n", "customCss": ".add-entity-form {\r\n min-width: 400px !important;\r\n}\r\n\r\n.add-entity-form .boolean-value-input {\r\n padding-left: 5px;\r\n}\r\n\r\n.add-entity-form .boolean-value-input .checkbox-label {\r\n margin-bottom: 8px;\r\n color: rgba(0,0,0,0.54);\r\n font-size: 12px;\r\n}\r\n\r\n.relations-list .header {\r\n padding-right: 5px;\r\n padding-bottom: 5px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .header .cell {\r\n padding-right: 5px;\r\n padding-left: 5px;\r\n font-size: 12px;\r\n font-weight: 700;\r\n color: rgba(0, 0, 0, .54);\r\n white-space: nowrap;\r\n}\r\n\r\n.relations-list .mat-form-field-infix {\r\n width: auto !important;\r\n}\r\n\r\n.relations-list .body {\r\n padding-right: 5px;\r\n padding-bottom: 15px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .body .row {\r\n padding-top: 5px;\r\n}\r\n\r\n.relations-list .body .cell {\r\n padding-right: 5px;\r\n padding-left: 5px;\r\n}\r\n\r\n.relations-list .body .md-button {\r\n margin: 0;\r\n}\r\n\r\n", - "customFunction": "let $injector = widgetContext.$scope.$injector;\r\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\r\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\r\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\r\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\r\nlet entityRelationService = $injector.get(widgetContext.servicesMap.get('entityRelationService'));\r\nlet userSettingsService = $injector.get(widgetContext.servicesMap.get('userSettingsService'));\r\n\r\nopenAddEntityDialog();\r\n\r\nfunction openAddEntityDialog() {\r\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\r\n}\r\n\r\nfunction AddEntityDialogController(instance) {\r\n let vm = instance;\r\n let userSettings;\r\n userSettingsService.loadUserSettings().subscribe(settings=> {\r\n userSettings = settings;\r\n if (!userSettings.createdGatewaysCount) userSettings.createdGatewaysCount = 0;\r\n });\r\n \r\n\r\n vm.addEntityFormGroup = vm.fb.group({\r\n entityName: ['', [vm.validators.required]],\r\n entityType: ['DEVICE'],\r\n entityLabel: [''],\r\n type: ['', [vm.validators.required]],\r\n });\r\n\r\n vm.cancel = function() {\r\n vm.dialogRef.close(null);\r\n };\r\n\r\n\r\n vm.save = function($event) {\r\n vm.addEntityFormGroup.markAsPristine();\r\n saveEntityObservable().subscribe(\r\n function (device) {\r\n widgetContext.updateAliases();\r\n userSettingsService.putUserSettings({ createdGatewaysCount: ++userSettings.createdGatewaysCount }).subscribe(_=>{\r\n });\r\n vm.dialogRef.close(null);\r\n openCommandDialog(device, $event);\r\n }\r\n );\r\n };\r\n \r\n function openCommandDialog(device, $event) {\r\n vm.device = device;\r\n let openCommandAction = widgetContext.actionsApi.getActionDescriptors(\"actionCellButton\").find(action => action.name == \"Docker commands\");\r\n widgetContext.actionsApi.handleWidgetAction($event, openCommandAction, device.id, device.name, {newDevice: true});\r\n goToConfigState();\r\n }\r\n\r\n \r\n function goToConfigState() {\r\n const stateParams = {};\r\n stateParams.entityId = vm.device.id;\r\n stateParams.entityName = vm.device.name;\r\n const newStateParams = {\r\n targetEntityParamName: 'default',\r\n new_gateway: {\r\n entityId: vm.device.id,\r\n entityName: vm.device.name\r\n }\r\n }\r\n const params = {...stateParams, ...newStateParams};\r\n widgetContext.stateController.openState('gateway_details', params, false);\r\n }\r\n\r\n function saveEntityObservable() {\r\n const formValues = vm.addEntityFormGroup.value;\r\n let entity = {\r\n name: formValues.entityName,\r\n type: formValues.type,\r\n label: formValues.entityLabel,\r\n additionalInfo: {\r\n gateway: true\r\n }\r\n };\r\n return deviceService.saveDevice(entity);\r\n }\r\n}\r\n", + "customFunction": "let $injector = widgetContext.$scope.$injector;\r\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\r\nlet assetService = $injector.get(widgetContext.servicesMap.get('assetService'));\r\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\r\nlet attributeService = $injector.get(widgetContext.servicesMap.get('attributeService'));\r\nlet entityRelationService = $injector.get(widgetContext.servicesMap.get('entityRelationService'));\r\nlet userSettingsService = $injector.get(widgetContext.servicesMap.get('userSettingsService'));\r\n\r\nopenAddEntityDialog();\r\n\r\nfunction openAddEntityDialog() {\r\n customDialog.customDialog(htmlTemplate, AddEntityDialogController).subscribe();\r\n}\r\n\r\nfunction AddEntityDialogController(instance) {\r\n let vm = instance;\r\n let userSettings;\r\n userSettingsService.loadUserSettings().subscribe(settings=> {\r\n userSettings = settings;\r\n if (!userSettings.createdGatewaysCount) userSettings.createdGatewaysCount = 0;\r\n });\r\n \r\n\r\n vm.addEntityFormGroup = vm.fb.group({\r\n entityName: ['', [vm.validators.required]],\r\n entityType: ['DEVICE'],\r\n entityLabel: [''],\r\n type: ['', [vm.validators.required]],\r\n });\r\n\r\n vm.cancel = function() {\r\n vm.dialogRef.close(null);\r\n };\r\n\r\n\r\n vm.save = function($event) {\r\n vm.addEntityFormGroup.markAsPristine();\r\n saveEntityObservable().subscribe(\r\n function (device) {\r\n widgetContext.updateAliases();\r\n userSettingsService.putUserSettings({ createdGatewaysCount: ++userSettings.createdGatewaysCount }).subscribe(_=>{\r\n });\r\n vm.dialogRef.close(null);\r\n openCommandDialog(device, $event);\r\n }\r\n );\r\n };\r\n \r\n function openCommandDialog(device, $event) {\r\n vm.device = device;\r\n let openCommandAction = widgetContext.actionsApi.getActionDescriptors(\"actionCellButton\").find(action => action.name == \"Launch command\");\r\n widgetContext.actionsApi.handleWidgetAction($event, openCommandAction, device.id, device.name, {newDevice: true});\r\n goToConfigState();\r\n }\r\n\r\n \r\n function goToConfigState() {\r\n const stateParams = {};\r\n stateParams.entityId = vm.device.id;\r\n stateParams.entityName = vm.device.name;\r\n const newStateParams = {\r\n targetEntityParamName: 'default',\r\n new_gateway: {\r\n entityId: vm.device.id,\r\n entityName: vm.device.name\r\n }\r\n }\r\n const params = {...stateParams, ...newStateParams};\r\n widgetContext.stateController.openState('gateway_details', params, false);\r\n }\r\n\r\n function saveEntityObservable() {\r\n const formValues = vm.addEntityFormGroup.value;\r\n let entity = {\r\n name: formValues.entityName,\r\n type: formValues.type,\r\n label: formValues.entityLabel,\r\n additionalInfo: {\r\n gateway: true\r\n }\r\n };\r\n return deviceService.saveDevice(entity);\r\n }\r\n}\r\n", "customResources": [], "openInSeparateDialog": false, "openInPopover": false, From d39d866698908d1c919c30045aaefed5dd7fdc70 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 17 Jun 2024 11:38:38 +0300 Subject: [PATCH 39/58] Optimized the 'Commands' table in the statistics tab for the 'General configuration' pop-up window --- .../widget/lib/gateway/gateway-configuration.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss index f7562c748e..9d72d1d759 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.scss @@ -55,7 +55,7 @@ } .statistics-container { - width: 50%; + width: 100%; .command-container { width: 100%; From bab37b3a11fa87a92e4efa9787e61c87a531f527 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 17 Jun 2024 12:22:43 +0300 Subject: [PATCH 40/58] Fixed mistakes in tooltips - IoT Gateway dashboard - General Configuration --- ui-ngx/src/assets/locale/locale.constant-en_US.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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..0898020fe8 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3237,17 +3237,17 @@ "password": "MQTT password for the gateway form platform server", "ca-cert": "Path to CA certificate file", "date-form": "Date format in log message", - "data-folder": "Path to folder, that will contains data (Relative or Absolute)", + "data-folder": "Path to the folder that will contain data (Relative or Absolute)", "log-format": "Log message format", "remote-log": "Enables remote logging and logs reading from the gateway", "backup-count": "If backup count is > 0, when a rollover is done, no more than backup count files are kept - the oldest ones are deleted", "storage": "Provides configuration for saving incoming data before it is sent to the platform", - "max-file-count": "Maximum count of file that will be created", - "max-read-count": "Count of messages to get from storage and send to platform", + "max-file-count": "Maximum number of files that will be created", + "max-read-count": "Number of messages to retrieve from the storage and send to platform", "max-records": "Maximum count of records that will be stored in one file", "read-record-count": "Count of messages to get from storage and send to platform", - "max-records-count": "Maximum count of data in storage before send to platform", - "ttl-check-hour": "How often will Gateway check data for obsolescence", + "max-records-count": "Maximum number of data entries in storage before sending to platform", + "ttl-check-hour": "How often will the Gateway check data for obsolescence", "ttl-messages-day": "Maximum days that storage will save data", "commands": "Commands for collecting additional statistic", "attribute": "Statistic telemetry key", From 5185a68a68bc811c3df35d02246c915c9b96f766 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 17 Jun 2024 12:37:00 +0300 Subject: [PATCH 41/58] refactoring --- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0898020fe8..28321f7fce 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3245,7 +3245,7 @@ "max-file-count": "Maximum number of files that will be created", "max-read-count": "Number of messages to retrieve from the storage and send to platform", "max-records": "Maximum count of records that will be stored in one file", - "read-record-count": "Count of messages to get from storage and send to platform", + "read-record-count": "Number of messages to retrieve from the storage and send to platform", "max-records-count": "Maximum number of data entries in storage before sending to platform", "ttl-check-hour": "How often will the Gateway check data for obsolescence", "ttl-messages-day": "Maximum days that storage will save data", From 5b9cb59eb162d10872d58f80c4c8bbaa232aa217 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 17 Jun 2024 12:50:06 +0300 Subject: [PATCH 42/58] refactoring --- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 28321f7fce..4708cab860 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3248,7 +3248,7 @@ "read-record-count": "Number of messages to retrieve from the storage and send to platform", "max-records-count": "Maximum number of data entries in storage before sending to platform", "ttl-check-hour": "How often will the Gateway check data for obsolescence", - "ttl-messages-day": "Maximum days that storage will save data", + "ttl-messages-day": "Maximum number of days that the storage will retain data", "commands": "Commands for collecting additional statistic", "attribute": "Statistic telemetry key", "timeout": "Timeout for command executing", From 1df93833cb94fc4b3c781f598e2bb2c790c739bf Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 17 Jun 2024 12:49:28 +0300 Subject: [PATCH 43/58] 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 522c926ca51f2d5b3f758e80313915c4aaa877fb Mon Sep 17 00:00:00 2001 From: mpetrov Date: Mon, 17 Jun 2024 17:31:29 +0300 Subject: [PATCH 44/58] Fixed jumping rows when updating data in Gateway Logs table --- .../widget/lib/gateway/gateway-logs.component.html | 2 +- .../components/widget/lib/gateway/gateway-logs.component.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html index d36a4c0a51..0382d0ebbd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.html @@ -21,7 +21,7 @@ [active]="activeLink.name === link.name"> {{ link.name }} - diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts index 0607268edc..d029279c2b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-logs.component.ts @@ -164,6 +164,10 @@ export class GatewayLogsComponent implements AfterViewInit { } } + trackByLogTs(_: number, log: GatewayLogData): number { + return log.ts; + } + private changeSubscription() { if (this.ctx.datasources && this.ctx.datasources[0].entity && this.ctx.defaultSubscription.options.datasources) { this.ctx.defaultSubscription.options.datasources[0].dataKeys = [{ @@ -178,5 +182,4 @@ export class GatewayLogsComponent implements AfterViewInit { }; } } - } 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 45/58] 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 e0aa8f8f958e2404dc9a5b2091db07721078634f Mon Sep 17 00:00:00 2001 From: mpetrov Date: Tue, 18 Jun 2024 17:24:45 +0300 Subject: [PATCH 46/58] Added optimization for the fields in Gateway Configuration --- .../gateway-configuration.component.html | 1250 +++++++++-------- 1 file changed, 631 insertions(+), 619 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html index 8aeaeef2ed..d622afa789 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-configuration.component.html @@ -20,737 +20,749 @@ -
-
-
- - {{ 'gateway.remote-configuration' | translate }} - -
-
- - {{ 'gateway.remote-shell' | translate }} - -
-
- - gateway.thingsboard-host - - info_outlined - - - {{ 'gateway.thingsboard-host-required' | translate }} - - - - gateway.thingsboard-port - - - {{ 'gateway.thingsboard-port-required' | translate }} - - - {{ 'gateway.thingsboard-port-min' | translate }} - - - {{ 'gateway.thingsboard-port-max' | translate }} - - - {{ 'gateway.thingsboard-port-pattern' | translate }} - - info_outlined - - + +
+
+
+ + {{ 'gateway.remote-configuration' | translate }} + +
+
+ + {{ 'gateway.remote-shell' | translate }} + +
+
+ + gateway.thingsboard-host + + info_outlined + + + {{ 'gateway.thingsboard-host-required' | translate }} + + + + gateway.thingsboard-port + + + {{ 'gateway.thingsboard-port-required' | translate }} + + + {{ 'gateway.thingsboard-port-min' | translate }} + + + {{ 'gateway.thingsboard-port-max' | translate }} + + + {{ 'gateway.thingsboard-port-pattern' | translate }} + + info_outlined + + +
-
-
-
security.security
- - - {{ securityType.value | translate }} - - - security.access-token - - - {{ 'security.access-token-required' | translate }} - - - - info_outlined - - -
- - security.clientId - - - {{ 'security.clientId-required' | translate }} +
+
security.security
+ + + {{ securityType.value | translate }} + + + security.access-token + + + {{ 'security.access-token-required' | translate }} info_outlined + matTooltip="{{ 'gateway.hints.token' | translate }}">info_outlined - - security.username - - - {{ 'security.username-required' | translate }} - +
+ + security.clientId + + + {{ 'security.clientId-required' | translate }} + + + + info_outlined + + + + security.username + + + {{ 'security.username-required' | translate }} + + + + info_outlined + + +
+ + gateway.password + info_outlined + matTooltip="{{ 'gateway.hints.password' | translate }}">info_outlined -
- - gateway.password - - - - info_outlined - - - - - - + + + +
-
+ -
-
-
- - gateway.logs.date-format - - - {{ 'gateway.logs.date-format-required' | translate }} - - info_outlined - - - - gateway.logs.log-format - - - {{ 'gateway.logs.log-format-required' | translate }} - - info_outlined - - -
-
-
-
gateway.logs.remote
-
- - {{ 'gateway.logs.remote-logs' | translate }} - -
- - gateway.logs.level - - {{ logLevel }} - - -
-
-
gateway.logs.local
- - {{ localLogsConfigTranslateMap.get(logConfig) }} - - -
- - gateway.logs.level - - {{ logLevel }} - + +
+
+
+ + gateway.logs.date-format + + + {{ 'gateway.logs.date-format-required' | translate }} + + info_outlined + - - gateway.logs.file-path - - - {{ 'gateway.logs.file-path-required' | translate }} + + gateway.logs.log-format + + + {{ 'gateway.logs.log-format-required' | translate }} + info_outlined +
-
-
+
+
+
gateway.logs.remote
+
+ + {{ 'gateway.logs.remote-logs' | translate }} + +
+ + gateway.logs.level + + {{ logLevel }} + + +
+
+
gateway.logs.local
+ + {{ localLogsConfigTranslateMap.get(logConfig) }} + + +
+ + gateway.logs.level + + {{ logLevel }} + + - gateway.logs.saving-period - + gateway.logs.file-path + + + {{ 'gateway.logs.file-path-required' | translate }} + + +
+
+
+ + gateway.logs.saving-period + + + {{ 'gateway.logs.saving-period-required' | translate }} + + + {{ 'gateway.logs.saving-period-min' | translate }} + + + + + + {{ period.value | translate }} + + + +
+ + gateway.logs.backup-count + - {{ 'gateway.logs.saving-period-required' | translate }} + *ngIf="gatewayConfigGroup.get('logs.local.' + logSelector.value + '.backupCount').hasError('required')"> + {{ 'gateway.logs.backup-count-required' | translate }} - {{ 'gateway.logs.saving-period-min' | translate }} + *ngIf="gatewayConfigGroup.get('logs.local.' + logSelector.value + '.backupCount').hasError('min')"> + {{ 'gateway.logs.backup-count-min' | translate }} - - - - - {{ period.value | translate }} - - + info_outlined +
- - gateway.logs.backup-count - - - {{ 'gateway.logs.backup-count-required' | translate }} - - - {{ 'gateway.logs.backup-count-min' | translate }} - - info_outlined - - -
- + +
-
+
-
-
-
gateway.storage
-
gateway.hints.storage
- - - {{ storageTypesTranslationMap.get(storageType) | translate }} - - -
{{ 'gateway.hints.' + gatewayConfigGroup.get('storage.type').value | translate }}
- -
- - gateway.storage-read-record-count - - - {{ 'gateway.storage-read-record-count-required' | translate }} - - - {{ 'gateway.storage-read-record-count-min' | translate }} - - - {{ 'gateway.storage-read-record-count-pattern' | translate }} - - info_outlined - - - - gateway.storage-max-records - - - {{ 'gateway.storage-max-records-required' | translate }} - - - {{ 'gateway.storage-max-records-min' | translate }} - - - {{ 'gateway.storage-max-records-pattern' | translate }} - - info_outlined - - -
-
-
+ +
+
+
gateway.storage
+
gateway.hints.storage
+ + + {{ storageTypesTranslationMap.get(storageType) | translate }} + + +
{{ 'gateway.hints.' + gatewayConfigGroup.get('storage.type').value | translate }}
+ +
- gateway.storage-data-folder-path - - - {{ 'gateway.storage-data-folder-path-required' | translate }} - - info_outlined + gateway.storage-read-record-count + + + {{ 'gateway.storage-read-record-count-required' | translate }} + + + {{ 'gateway.storage-read-record-count-min' | translate }} + + + {{ 'gateway.storage-read-record-count-pattern' | translate }} + + info_outlined - gateway.storage-max-files - - - {{ 'gateway.storage-max-files-required' | translate }} + gateway.storage-max-records + + + {{ 'gateway.storage-max-records-required' | translate }} - - {{ 'gateway.storage-max-files-min' | translate }} + + {{ 'gateway.storage-max-records-min' | translate }} - - {{ 'gateway.storage-max-files-pattern' | translate }} + + {{ 'gateway.storage-max-records-pattern' | translate }} info_outlined + matTooltip="{{ 'gateway.hints.max-records-count' | translate }}">info_outlined -
-
- - gateway.storage-max-read-record-count - - - {{ 'gateway.storage-max-read-record-count-required' | translate }} +
+
+
+ + gateway.storage-data-folder-path + + + {{ 'gateway.storage-data-folder-path-required' | translate }} + + info_outlined + + + + gateway.storage-max-files + + + {{ 'gateway.storage-max-files-required' | translate }} + + + {{ 'gateway.storage-max-files-min' | translate }} + + + {{ 'gateway.storage-max-files-pattern' | translate }} + + info_outlined + + +
+
+ + gateway.storage-max-read-record-count + + + {{ 'gateway.storage-max-read-record-count-required' | translate }} + + + {{ 'gateway.storage-max-read-record-count-min' | translate }} + + + {{ 'gateway.storage-max-read-record-count-pattern' | translate }} + + info_outlined + + + + gateway.storage-max-file-records + + + {{ 'gateway.storage-max-records-required' | translate }} + + + {{ 'gateway.storage-max-records-min' | translate }} + + + {{ 'gateway.storage-max-records-pattern' | translate }} + + info_outlined + + +
+
+
+
+ + gateway.storage-path + + + {{ 'gateway.storage-path-required' | translate }} + + info_outlined + + + + gateway.messages-ttl-check-in-hours + + + {{ 'gateway.messages-ttl-check-in-hours-required' | translate }} + + + {{ 'gateway.messages-ttl-check-in-hours-min' | translate }} + + + {{ 'gateway.messages-ttl-check-in-hours-pattern' | translate }} + + info_outlined + + +
+ + gateway.messages-ttl-in-days + + + {{ 'gateway.messages-ttl-in-days-required' | translate }} - - {{ 'gateway.storage-max-read-record-count-min' | translate }} + + {{ 'gateway.messages-ttl-in-days-min' | translate }} - - {{ 'gateway.storage-max-read-record-count-pattern' | translate }} + + {{ 'gateway.messages-ttl-in-days-pattern' | translate }} info_outlined + matTooltip="{{ 'gateway.hints.ttl-messages-day' | translate }}">info_outlined +
+
+
+
+ +
+ + +
+
+ + {{ 'gateway.grpc' | translate }} + +
+ + {{ 'gateway.permit-without-calls' | translate }} + +
+
+
- gateway.storage-max-file-records - - - {{ 'gateway.storage-max-records-required' | translate }} + gateway.server-port + + info_outlined + + + {{ 'gateway.thingsboard-port-required' | translate }} - - {{ 'gateway.storage-max-records-min' | translate }} + + {{ 'gateway.thingsboard-port-min' | translate }} - - {{ 'gateway.storage-max-records-pattern' | translate }} + + {{ 'gateway.thingsboard-port-max' | translate }} + + + {{ 'gateway.thingsboard-port-pattern' | translate }} + + + gateway.grpc-keep-alive-timeout + info_outlined + matTooltip="{{ 'gateway.hints.grpc-keep-alive-timeout' | translate }}">info_outlined + + {{ 'gateway.grpc-keep-alive-timeout-required' | translate }} + + + {{ 'gateway.grpc-keep-alive-timeout-min' | translate }} + + + {{ 'gateway.grpc-keep-alive-timeout-pattern' | translate }} + -
- -
-
+
+
- gateway.storage-path - - - {{ 'gateway.storage-path-required' | translate }} + gateway.grpc-keep-alive + + info_outlined + + + {{ 'gateway.grpc-keep-alive-required' | translate }} + + + {{ 'gateway.grpc-keep-alive-min' | translate }} + + {{ 'gateway.grpc-keep-alive-pattern' | translate }} + + + + gateway.grpc-min-time-between-pings + info_outlined + matTooltip="{{ 'gateway.hints.grpc-min-time-between-pings' | translate }}">info_outlined + + {{ 'gateway.grpc-min-time-between-pings-required' | translate }} + + + {{ 'gateway.grpc-min-time-between-pings-min' | translate }} + + + {{ 'gateway.grpc-min-time-between-pings-pattern' | translate }} + +
+
- gateway.messages-ttl-check-in-hours - - - {{ 'gateway.messages-ttl-check-in-hours-required' | translate }} + gateway.grpc-max-pings-without-data + + info_outlined + + + {{ 'gateway.grpc-max-pings-without-data-required' | translate }} - - {{ 'gateway.messages-ttl-check-in-hours-min' | translate }} + + {{ 'gateway.grpc-max-pings-without-data-min' | translate }} - - {{ 'gateway.messages-ttl-check-in-hours-pattern' | translate }} + + {{ 'gateway.grpc-max-pings-without-data-pattern' | translate }} + + + gateway.grpc-min-ping-interval-without-data + info_outlined + matTooltip="{{ 'gateway.hints.grpc-min-ping-interval-without-data' | translate }}">info_outlined + + {{ 'gateway.grpc-min-ping-interval-without-data-required' | translate }} + + + {{ 'gateway.grpc-min-ping-interval-without-data-min' | translate }} + + + {{ 'gateway.grpc-min-ping-interval-without-data-pattern' | translate }} + -
- - gateway.messages-ttl-in-days - - - {{ 'gateway.messages-ttl-in-days-required' | translate }} - - - {{ 'gateway.messages-ttl-in-days-min' | translate }} - - - {{ 'gateway.messages-ttl-in-days-pattern' | translate }} - - info_outlined - - + - +
-
+
- -
-
- - {{ 'gateway.grpc' | translate }} - -
- - {{ 'gateway.permit-without-calls' | translate }} + + +
+
+ + {{ 'gateway.statistics.statistics' | translate }} + + gateway.statistics.send-period + + + {{ 'gateway.statistics.send-period-required' | translate }} + + + {{ 'gateway.statistics.send-period-min' | translate }} + + + {{ 'gateway.statistics.send-period-pattern' | translate }} + +
-
-
+
+
gateway.statistics.commands
+
gateway.hints.commands
+ +
+
+
+ + gateway.statistics.attribute-name + + + {{ 'gateway.statistics.attribute-name-required' | translate }} + + info_outlined + + + + gateway.statistics.timeout + + + {{ 'gateway.statistics.timeout-required' | translate }} + + + {{ 'gateway.statistics.timeout-min' | translate }} + + + {{ 'gateway.statistics.timeout-pattern' | translate }} + + info_outlined + + +
+ + gateway.statistics.command + + + {{ 'gateway.statistics.command-required' | translate }} + + + {{ 'gateway.statistics.command-pattern' | translate }} + + info_outlined + + +
+ +
+ +
+
+
+
+
+ + +
+
+
+ + {{ 'gateway.checking-device-activity' | translate }} + +
+
- gateway.server-port - - info_outlined - - - {{ 'gateway.thingsboard-port-required' | translate }} - - - {{ 'gateway.thingsboard-port-min' | translate }} + gateway.inactivity-timeout-seconds + + + {{ 'gateway.inactivity-timeout-seconds-required' | translate }} - - {{ 'gateway.thingsboard-port-max' | translate }} + + {{ 'gateway.inactivity-timeout-seconds-min' | translate }} - - {{ 'gateway.thingsboard-port-pattern' | translate }} + + {{ 'gateway.inactivity-timeout-seconds-pattern' | translate }} - - - gateway.grpc-keep-alive-timeout - info_outlined + matTooltip="{{ 'gateway.hints.inactivity-timeout' | translate }}">info_outlined - - {{ 'gateway.grpc-keep-alive-timeout-required' | translate }} - - - {{ 'gateway.grpc-keep-alive-timeout-min' | translate }} - - - {{ 'gateway.grpc-keep-alive-timeout-pattern' | translate }} - -
-
- gateway.grpc-keep-alive - - info_outlined - - - {{ 'gateway.grpc-keep-alive-required' | translate }} + gateway.inactivity-check-period-seconds + + + {{ 'gateway.inactivity-check-period-seconds-required' | translate }} - - {{ 'gateway.grpc-keep-alive-min' | translate }} + + {{ 'gateway.inactivity-check-period-seconds-min' | translate }} - - {{ 'gateway.grpc-keep-alive-pattern' | translate }} + + {{ 'gateway.inactivity-check-period-seconds-pattern' | translate }} - - - gateway.grpc-min-time-between-pings - info_outlined + matTooltip="{{ 'gateway.hints.inactivity-period' | translate }}">info_outlined - - {{ 'gateway.grpc-min-time-between-pings-required' | translate }} - - - {{ 'gateway.grpc-min-time-between-pings-min' | translate }} - - - {{ 'gateway.grpc-min-time-between-pings-pattern' | translate }} -
+
+
+
gateway.advanced
- gateway.grpc-max-pings-without-data - - info_outlined - - - {{ 'gateway.grpc-max-pings-without-data-required' | translate }} - - - {{ 'gateway.grpc-max-pings-without-data-min' | translate }} + gateway.min-pack-send-delay + + + {{ 'gateway.min-pack-send-delay-required' | translate }} - - {{ 'gateway.grpc-max-pings-without-data-pattern' | translate }} + + {{ 'gateway.min-pack-send-delay-min' | translate }} - - - gateway.grpc-min-ping-interval-without-data - info_outlined + matTooltip="{{ 'gateway.hints.minimal-pack-delay' | translate }}">info_outlined - - {{ 'gateway.grpc-min-ping-interval-without-data-required' | translate }} + + + gateway.mqtt-qos + + + {{ 'gateway.mqtt-qos-required' | translate }} - - {{ 'gateway.grpc-min-ping-interval-without-data-min' | translate }} + + {{ 'gateway.mqtt-qos-range' | translate }} - - {{ 'gateway.grpc-min-ping-interval-without-data-pattern' | translate }} + + {{ 'gateway.mqtt-qos-range' | translate }} + info_outlined +
- -
-
-
- -
-
- - {{ 'gateway.statistics.statistics' | translate }} - - - gateway.statistics.send-period - - - {{ 'gateway.statistics.send-period-required' | translate }} - - - {{ 'gateway.statistics.send-period-min' | translate }} - - - {{ 'gateway.statistics.send-period-pattern' | translate }} - - -
-
-
gateway.statistics.commands
-
gateway.hints.commands
- -
-
-
- - gateway.statistics.attribute-name - - - {{ 'gateway.statistics.attribute-name-required' | translate }} - - info_outlined - - - - gateway.statistics.timeout - - - {{ 'gateway.statistics.timeout-required' | translate }} - - - {{ 'gateway.statistics.timeout-min' | translate }} - - - {{ 'gateway.statistics.timeout-pattern' | translate }} - - info_outlined - - -
- - gateway.statistics.command - - - {{ 'gateway.statistics.command-required' | translate }} - - - {{ 'gateway.statistics.command-pattern' | translate }} - - info_outlined - - -
- -
- -
-
-
-
- -
-
-
- - {{ 'gateway.checking-device-activity' | translate }} - -
-
- - gateway.inactivity-timeout-seconds - - - {{ 'gateway.inactivity-timeout-seconds-required' | translate }} - - - {{ 'gateway.inactivity-timeout-seconds-min' | translate }} - - - {{ 'gateway.inactivity-timeout-seconds-pattern' | translate }} - - info_outlined - - - - gateway.inactivity-check-period-seconds - + + gateway.statistics.check-connectors-configuration + - {{ 'gateway.inactivity-check-period-seconds-required' | translate }} + *ngIf="gatewayConfigGroup.get('thingsboard.checkConnectorsConfigurationInSeconds').hasError('required')"> + {{ 'gateway.statistics.check-connectors-configuration-required' | translate }} - {{ 'gateway.inactivity-check-period-seconds-min' | translate }} + *ngIf="gatewayConfigGroup.get('thingsboard.checkConnectorsConfigurationInSeconds').hasError('min')"> + {{ 'gateway.statistics.check-connectors-configuration-min' | translate }} - {{ 'gateway.inactivity-check-period-seconds-pattern' | translate }} - - info_outlined - - -
-
-
-
gateway.advanced
-
- - gateway.min-pack-send-delay - - - {{ 'gateway.min-pack-send-delay-required' | translate }} - - - {{ 'gateway.min-pack-send-delay-min' | translate }} + *ngIf="gatewayConfigGroup.get('thingsboard.checkConnectorsConfigurationInSeconds').hasError('pattern')"> + {{ 'gateway.statistics.check-connectors-configuration-pattern' | translate }} - info_outlined - - - gateway.mqtt-qos - - - {{ 'gateway.mqtt-qos-required' | translate }} - - - {{ 'gateway.mqtt-qos-range' | translate }} - - - {{ 'gateway.mqtt-qos-range' | translate }} - - info_outlined - - -
- - gateway.statistics.check-connectors-configuration - - - {{ 'gateway.statistics.check-connectors-configuration-required' | translate }} - - - {{ 'gateway.statistics.check-connectors-configuration-min' | translate }} - - - {{ 'gateway.statistics.check-connectors-configuration-pattern' | translate }} - - +
-
+
From e0e5fa12c9f9a10d2b08dd6af40ab0311add8613 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 19 Jun 2024 12:28:20 +0300 Subject: [PATCH 47/58] "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 99104167901d28b738e5d323b1ca3e6eca8f2853 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 19 Jun 2024 13:11:54 +0300 Subject: [PATCH 48/58] Added Gateway Dashboard Connector optimization for multiple connector retoggle --- .../gateway/gateway-connectors.component.html | 2 +- .../gateway/gateway-connectors.component.ts | 83 +++++++++++++------ 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html index 3a7f5403b9..d1267cae3a 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.html @@ -46,7 +46,7 @@ + (click)="$event.stopPropagation(); onEnableConnector(attribute)"> diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts index 4f1b6b72a9..cd13916357 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-connectors.component.ts @@ -62,7 +62,7 @@ import { } from './gateway-widget.models'; import { MatDialog } from '@angular/material/dialog'; import { AddConnectorDialogComponent } from '@home/components/widget/lib/gateway/dialog/add-connector-dialog.component'; -import { takeUntil } from 'rxjs/operators'; +import { debounceTime, take, takeUntil, tap } from 'rxjs/operators'; import { ErrorStateMatcher } from '@angular/material/core'; import { PageData } from '@shared/models/page/page-data'; @@ -148,6 +148,7 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie private destroy$ = new Subject(); private subscription: IWidgetSubscription; + private attributeUpdateSubject = new Subject(); constructor(protected store: Store, private fb: FormBuilder, @@ -180,6 +181,8 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie basicConfig: this.fb.group({}) }); this.connectorForm.disable(); + + this.observeAttributeChange(); } get portErrorTooltip(): string { @@ -524,31 +527,12 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie } - enableConnector(attribute: AttributeData): void { - const wasEnabled = this.activeConnectors.includes(attribute.key); - const scopeOld = wasEnabled ? AttributeScope.SHARED_SCOPE : AttributeScope.SERVER_SCOPE; - const scopeNew = !wasEnabled ? AttributeScope.SHARED_SCOPE : AttributeScope.SERVER_SCOPE; + onEnableConnector(attribute: AttributeData): void { attribute.value.ts = new Date().getTime(); - const tasks = [this.attributeService.saveEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, [{ - key: 'active_connectors', - value: this.activeConnectors - }]), this.attributeService.saveEntityAttributes(this.device, AttributeScope.SERVER_SCOPE, [{ - key: 'inactive_connectors', - value: this.inactiveConnectors - }]), this.attributeService.deleteEntityAttributes(this.device, scopeOld, [attribute]), - this.attributeService.saveEntityAttributes(this.device, scopeNew, [attribute])]; - if (wasEnabled) { - const index = this.activeConnectors.indexOf(attribute.key); - this.activeConnectors.splice(index, 1); - this.inactiveConnectors.push(attribute.key); - } else { - const index = this.inactiveConnectors.indexOf(attribute.key); - this.inactiveConnectors.splice(index, 1); - this.activeConnectors.push(attribute.key); - } - forkJoin(tasks).subscribe(_ => { - this.updateData(true); - }); + + this.updateActiveConnectorKeys(attribute.key); + + this.attributeUpdateSubject.next(attribute); } getErrorsCount(attribute: AttributeData): string { @@ -599,6 +583,55 @@ export class GatewayConnectorComponent extends PageComponent implements AfterVie this.connectorForm.get(formControlName)?.patchValue('tb_gw_' + generateSecret(5)); } + private observeAttributeChange(): void { + this.attributeUpdateSubject.pipe( + debounceTime(300), + tap((attribute: AttributeData) => this.executeAttributeUpdates(attribute)), + takeUntil(this.destroy$), + ).subscribe(); + } + + private updateActiveConnectorKeys(key: string): void { + const wasEnabled = this.activeConnectors.includes(key); + if (wasEnabled) { + const index = this.activeConnectors.indexOf(key); + if (index !== -1) this.activeConnectors.splice(index, 1); + this.inactiveConnectors.push(key); + } else { + const index = this.inactiveConnectors.indexOf(key); + if (index !== -1) this.inactiveConnectors.splice(index, 1); + this.activeConnectors.push(key); + } + } + + private executeAttributeUpdates(attribute: AttributeData): void { + forkJoin(this.getAttributeExecutionTasks(attribute)) + .pipe( + take(1), + tap(() => this.updateData(true)), + takeUntil(this.destroy$), + ) + .subscribe(); + } + + private getAttributeExecutionTasks(attribute: AttributeData): Observable[] { + const isActive = this.activeConnectors.includes(attribute.key); + const scopeOld = isActive ? AttributeScope.SERVER_SCOPE : AttributeScope.SHARED_SCOPE; + const scopeNew = isActive ? AttributeScope.SHARED_SCOPE : AttributeScope.SERVER_SCOPE; + + return [ + this.attributeService.saveEntityAttributes(this.device, AttributeScope.SHARED_SCOPE, [{ + key: 'active_connectors', + value: this.activeConnectors + }]), + this.attributeService.saveEntityAttributes(this.device, AttributeScope.SERVER_SCOPE, [{ + key: 'inactive_connectors', + value: this.inactiveConnectors + }]), + this.attributeService.deleteEntityAttributes(this.device, scopeOld, [attribute]), + this.attributeService.saveEntityAttributes(this.device, scopeNew, [attribute])]; + } + private onDataUpdateError(e: any): void { const exceptionData = this.utils.parseException(e); let errorText = exceptionData.name; 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 49/58] 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 580f3b5b3c86c20508bd3c8332331627c12e8f53 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 19 Jun 2024 17:20:15 +0300 Subject: [PATCH 50/58] Changed Security block for REST Connector --- .../rest-connector-security.component.html | 65 +++++++++ .../rest-connector-security.component.scss | 29 ++++ .../rest-connector-security.component.ts | 132 ++++++++++++++++++ ...teway-service-rpc-connector.component.html | 33 +---- ...gateway-service-rpc-connector.component.ts | 28 +--- .../lib/gateway/gateway-widget.models.ts | 12 ++ .../widget/widget-components.module.ts | 16 ++- 7 files changed, 249 insertions(+), 66 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.html create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.scss create mode 100644 ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.html new file mode 100644 index 0000000000..afabe53499 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.html @@ -0,0 +1,65 @@ + +
+
+
gateway.security
+ + + {{ SecurityTypeTranslationsMap.get(type) | translate }} + + +
+ +
+
gateway.username
+
+ + + + warning + + +
+
+
+
gateway.password
+
+ + + + warning + +
+ +
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.scss new file mode 100644 index 0000000000..f014f56588 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.scss @@ -0,0 +1,29 @@ +/** + * 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. + */ +:host { + width: 100%; + height: 100%; + display: block; + margin-bottom: 10px; + + .fields-label { + font-weight: 500; + } + + .hide-toggle { + display: none; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts new file mode 100644 index 0000000000..65b3620ac7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts @@ -0,0 +1,132 @@ +/// +/// 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. +/// + +import { + ChangeDetectionStrategy, + Component, + forwardRef, + OnDestroy, +} from '@angular/core'; +import { Subject } from 'rxjs'; +import { + ControlValueAccessor, + FormBuilder, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormGroup, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; +import { takeUntil } from 'rxjs/operators'; +import { + noLeadTrailSpacesRegex, + RestSecurityType, + RestSecurityTypeTranslationsMap +} from '@home/components/widget/lib/gateway/gateway-widget.models'; +import { SharedModule } from '@shared/shared.module'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'tb-rest-connector-security', + templateUrl: './rest-connector-security.component.html', + styleUrls: ['./rest-connector-security.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => RestConnectorSecurityComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => RestConnectorSecurityComponent), + multi: true + } + ], + standalone: true, + imports: [ + SharedModule, + CommonModule, + ] +}) +export class RestConnectorSecurityComponent implements ControlValueAccessor, Validator, OnDestroy { + BrokerSecurityType = RestSecurityType; + securityTypes: RestSecurityType[] = Object.values(RestSecurityType); + SecurityTypeTranslationsMap = RestSecurityTypeTranslationsMap; + securityFormGroup: UntypedFormGroup; + + private destroy$ = new Subject(); + private propagateChange = (_: any) => {}; + + constructor(private fb: FormBuilder) { + this.securityFormGroup = this.fb.group({ + type: [RestSecurityType.ANONYMOUS, []], + username: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + password: ['', [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], + }); + this.observeSecurityForm(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void {} + + writeValue(deviceInfo: any): void { + if (!deviceInfo.type) { + deviceInfo.type = RestSecurityType.ANONYMOUS; + } + this.securityFormGroup.reset(deviceInfo); + this.updateView(deviceInfo); + } + + validate(): ValidationErrors | null { + return this.securityFormGroup.valid ? null : { + securityForm: { valid: false } + }; + } + + updateView(value: any): void { + this.propagateChange(value); + } + + private updateValidators(type: RestSecurityType): void { + if (type === RestSecurityType.BASIC) { + this.securityFormGroup.get('username').enable({emitEvent: false}); + this.securityFormGroup.get('password').enable({emitEvent: false}); + } else { + this.securityFormGroup.get('username').disable({emitEvent: false}); + this.securityFormGroup.get('password').disable({emitEvent: false}); + } + } + + private observeSecurityForm(): void { + this.securityFormGroup.valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(value => this.updateView(value)); + + this.securityFormGroup.get('type').valueChanges + .pipe(takeUntil(this.destroy$)) + .subscribe(type => this.updateValidators(type)); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html index 2d992187f6..b3d38d6655 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html @@ -368,38 +368,7 @@ {{ 'gateway.rpc.add-header' | translate }} -
- {{ 'gateway.rpc.security' | translate }} -
-
- {{ 'gateway.rpc.security-name' | translate }} - {{ 'gateway.rpc.value' | translate }} - -
- -
- - - - - - - - delete - - -
-
- -
+ diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts index 9838fd2df2..96380d3108 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts @@ -114,17 +114,12 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue this.commandForm = this.connectorParamsFormGroupByType(this.connectorType); this.commandForm.valueChanges.subscribe(value => { const httpHeaders = {}; - const security = {}; switch (this.connectorType) { case ConnectorType.REST: value.httpHeaders.forEach(data => { httpHeaders[data.headerName] = data.value; }) value.httpHeaders = httpHeaders; - value.security.forEach(data => { - security[data.securityName] = data.value; - }) - value.security = security; break; case ConnectorType.REQUEST: value.httpHeaders.forEach(data => { @@ -252,7 +247,7 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue tries: [null, [Validators.required, Validators.min(1), Validators.pattern(this.numbersOnlyPattern)]], valueExpression: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], httpHeaders: this.fb.array([]), - security: this.fb.array([]) + security: [{}, [Validators.required]] }) break; case ConnectorType.REQUEST: @@ -312,22 +307,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue oidsFA.removeAt(index); } - addHTTPSecurity(value: { securityName: string, value: string } = {securityName: null, value: null}) { - const securityFA = this.commandForm.get('security') as FormArray; - const formGroup = this.fb.group({ - securityName: [value.securityName, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], - value: [value.value, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]] - }) - if (securityFA) { - securityFA.push(formGroup, {emitEvent: false}); - } - } - - removeHTTPSecurity(index: number) { - const oidsFA = this.commandForm.get('security') as FormArray; - oidsFA.removeAt(index); - } - getFormArrayControls(path: string) { return (this.commandForm.get(path) as FormArray).controls as FormControl[]; } @@ -402,11 +381,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue break; case ConnectorType.REST: this.clearFromArrayByName("httpHeaders"); - this.clearFromArrayByName("security"); - value.security && Object.entries(value.security).forEach(securityHeader => { - this.addHTTPSecurity({securityName: securityHeader[0], value: securityHeader[1] as string}) - }) - delete value.security; value.httpHeaders && Object.entries(value.httpHeaders).forEach(httpHeader => { this.addHTTPHeader({headerName: httpHeader[0], value: httpHeader[1] as string}) }) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts index 0e143cff36..2bf55275c2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts @@ -360,6 +360,18 @@ export const BrokerSecurityTypeTranslationsMap = new Map( + [ + [RestSecurityType.ANONYMOUS, 'gateway.broker.security-types.anonymous'], + [RestSecurityType.BASIC, 'gateway.broker.security-types.basic'], + ] +); + export const MqttVersions = [ { name: 3.1, value: 3 }, { name: 3.11, value: 4 }, diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index d345f5954f..6d2cfb6fe3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -100,6 +100,7 @@ import { BarChartWidgetComponent } from '@home/components/widget/lib/chart/bar-c import { PolarAreaWidgetComponent } from '@home/components/widget/lib/chart/polar-area-widget.component'; import { RadarChartWidgetComponent } from '@home/components/widget/lib/chart/radar-chart-widget.component'; import { MobileAppQrcodeWidgetComponent } from '@home/components/widget/lib/mobile-app-qrcode-widget.component'; +import { RestConnectorSecurityComponent } from '@home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component'; @NgModule({ declarations: @@ -168,13 +169,14 @@ import { MobileAppQrcodeWidgetComponent } from '@home/components/widget/lib/mobi PolarAreaWidgetComponent, RadarChartWidgetComponent ], - imports: [ - CommonModule, - SharedModule, - RpcWidgetsModule, - HomePageWidgetsModule, - SharedHomeComponentsModule - ], + imports: [ + CommonModule, + SharedModule, + RpcWidgetsModule, + HomePageWidgetsModule, + SharedHomeComponentsModule, + RestConnectorSecurityComponent + ], exports: [ EntitiesTableWidgetComponent, AlarmsTableWidgetComponent, From e8fb1375ac19586dd2c440c784c30efbfe8c207d Mon Sep 17 00:00:00 2001 From: mpetrov Date: Wed, 19 Jun 2024 17:44:31 +0300 Subject: [PATCH 51/58] refactoring --- .../rest-connector-security.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts index 65b3620ac7..d00813bfd6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/connectors-configuration/rest-connector-secuirity/rest-connector-security.component.ts @@ -106,7 +106,7 @@ export class RestConnectorSecurityComponent implements ControlValueAccessor, Val }; } - updateView(value: any): void { + private updateView(value: any): void { this.propagateChange(value); } From 468c00788268b6680b42485b9e78ec028e83277a Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 20 Jun 2024 15:53:41 +0300 Subject: [PATCH 52/58] 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 From c095a3728a6420863079c7e596cc19f667e00bce Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 20 Jun 2024 16:31:32 +0300 Subject: [PATCH 53/58] Removed 'OPCUA ASYNCIO' Connector Type --- .../lib/gateway/gateway-service-rpc-connector.component.html | 3 --- .../lib/gateway/gateway-service-rpc-connector.component.ts | 2 -- .../widget/lib/gateway/gateway-service-rpc.component.ts | 1 - .../components/widget/lib/gateway/gateway-widget.models.ts | 2 -- 4 files changed, 8 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html index 2d992187f6..e243f7b757 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.html @@ -478,9 +478,6 @@ - - - {{ 'gateway.rpc.method' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts index 9838fd2df2..02e8532364 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc-connector.component.ts @@ -269,7 +269,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue }) break; case ConnectorType.OPCUA: - case ConnectorType.OPCUA_ASYNCIO: formGroup = this.fb.group({ method: [null, [Validators.required, Validators.pattern(noLeadTrailSpacesRegex)]], arguments: this.fb.array([]), @@ -413,7 +412,6 @@ export class GatewayServiceRPCConnectorComponent implements OnInit, ControlValue delete value.httpHeaders; break; case ConnectorType.OPCUA: - case ConnectorType.OPCUA_ASYNCIO: this.clearFromArrayByName("arguments"); value.arguments.forEach(value => { this.addOCPUAArguments(value) diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts index b5cde980f9..dadd1a38d2 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-service-rpc.component.ts @@ -150,7 +150,6 @@ export class GatewayServiceRPCComponent implements OnInit { case ConnectorType.BACNET: case ConnectorType.CAN: case ConnectorType.OPCUA: - case ConnectorType.OPCUA_ASYNCIO: return params.method; case ConnectorType.BLE: case ConnectorType.OCPP: diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts index 0e143cff36..0e56433987 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/gateway/gateway-widget.models.ts @@ -125,7 +125,6 @@ export enum ConnectorType { MODBUS = 'modbus', GRPC = 'grpc', OPCUA = 'opcua', - OPCUA_ASYNCIO = 'opcua_asyncio', BLE = 'ble', REQUEST = 'request', CAN = 'can', @@ -145,7 +144,6 @@ export const GatewayConnectorDefaultTypesTranslatesMap = new Map Date: Thu, 20 Jun 2024 17:33:49 +0300 Subject: [PATCH 54/58] adjustments --- application/src/main/data/json/tenant/dashboards/gateways.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json index 42c4f199b5..d9aafe7ea4 100644 --- a/application/src/main/data/json/tenant/dashboards/gateways.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -2134,7 +2134,7 @@ "padding": "0px", "settings": { "useMarkdownTextFunction": false, - "markdownTextPattern": "
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n", + "markdownTextPattern": "
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n", "applyDefaultMarkdownStyle": false, "markdownCss": ".mat-mdc-form-field-subscript-wrapper {\n display: none !important;\n}" }, From a6d9f40e6099a4e543ae6428d187e514da464f08 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 20 Jun 2024 17:40:49 +0300 Subject: [PATCH 55/58] adjustments --- application/src/main/data/json/tenant/dashboards/gateways.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json index d9aafe7ea4..a367080484 100644 --- a/application/src/main/data/json/tenant/dashboards/gateways.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -2134,7 +2134,7 @@ "padding": "0px", "settings": { "useMarkdownTextFunction": false, - "markdownTextPattern": "
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n", + "markdownTextPattern": "
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n", "applyDefaultMarkdownStyle": false, "markdownCss": ".mat-mdc-form-field-subscript-wrapper {\n display: none !important;\n}" }, From 1132448d9f2cac3abe558d0584ed3be2e004d7f8 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 20 Jun 2024 17:42:59 +0300 Subject: [PATCH 56/58] adjustments --- .../data/json/tenant/dashboards/gateways.json | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json index a367080484..9b02a19905 100644 --- a/application/src/main/data/json/tenant/dashboards/gateways.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -5632,33 +5632,6 @@ } } }, - "gateway_devices_5": { - "name": "gateway_devices_opcua_async", - "root": false, - "layouts": { - "main": { - "widgets": { - "e3a9539d-2ccb-96e0-2afa-36f93e40233d": { - "sizeX": 24, - "sizeY": 11, - "row": 0, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "columns": 24, - "margin": 0, - "outerMargin": true, - "backgroundSizeMode": "100%", - "autoFillHeight": false, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70 - } - } - } - }, "gateway_devices_6": { "name": "gateway_devices_ble", "root": false, From 81d34670e8b4d684ca18ac4046b58a152c4eb1f0 Mon Sep 17 00:00:00 2001 From: mpetrov Date: Thu, 20 Jun 2024 17:57:03 +0300 Subject: [PATCH 57/58] adjustments --- .../data/json/tenant/dashboards/gateways.json | 166 ------------------ 1 file changed, 166 deletions(-) diff --git a/application/src/main/data/json/tenant/dashboards/gateways.json b/application/src/main/data/json/tenant/dashboards/gateways.json index 9b02a19905..d4613eaa62 100644 --- a/application/src/main/data/json/tenant/dashboards/gateways.json +++ b/application/src/main/data/json/tenant/dashboards/gateways.json @@ -2996,172 +2996,6 @@ "id": "bb27723a-989c-2327-5808-b56d490b93ab", "typeFullFqn": "system.cards.entities_table" }, - "e3a9539d-2ccb-96e0-2afa-36f93e40233d": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 6.5, - "config": { - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 86400000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1684327643501, - "endTimeMs": 1684414043501 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "NONE", - "limit": 200 - } - }, - "showTitle": true, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "4px", - "settings": { - "entitiesTitle": "Devices", - "enableSearch": true, - "enableSelectColumnDisplay": true, - "enableStickyHeader": true, - "enableStickyAction": true, - "reserveSpaceForHiddenAction": "true", - "displayEntityName": true, - "entityNameColumnTitle": "Device Name", - "displayEntityLabel": false, - "displayEntityType": false, - "displayPagination": true, - "defaultPageSize": 10, - "defaultSortOrder": "entityName", - "useRowStyleFunction": false - }, - "title": "Devices", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400, - "padding": "5px 10px 5px 10px" - }, - "useDashboardTimewindow": false, - "showLegend": false, - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "a75d9031-ba51-8da4-81be-de65061b72f4", - "filterId": "3931abd5-2205-9386-6ea9-8e8a8131bb9d", - "dataKeys": [ - { - "name": "type", - "type": "entityField", - "label": "Device Type", - "color": "#2196f3", - "settings": {}, - "_hash": 0.3129929097366162, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "active", - "type": "attribute", - "label": "Status", - "color": "#4caf50", - "settings": { - "columnWidth": "0px", - "useCellStyleFunction": false, - "useCellContentFunction": true, - "cellContentFunction": "let cssClass;\r\nswitch (value) {\r\n case \"Active\":\r\n cssClass = \"status status-active\";\r\n break;\r\n case \"Inactive\":\r\n default:\r\n cssClass = \"status status-inactive\";\r\n break;\r\n }\r\n \r\n return `${value}`;", - "defaultColumnVisibility": "visible", - "columnSelectionToDisplay": "enabled" - }, - "_hash": 0.5969880627410065, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value == 'true' ? \"Active\": \"Inactive\";" - }, - { - "name": "connectorName", - "type": "attribute", - "label": "Connector Name", - "color": "#f44336", - "settings": {}, - "_hash": 0.012483045440007778, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "connectorType", - "type": "attribute", - "label": "Connector Type", - "color": "#ffc107", - "settings": {}, - "_hash": 0.6004192233378134, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "alarmFilterConfig": { - "statusList": [ - "ACTIVE" - ] - } - } - ], - "displayTimewindow": true, - "showTitleIcon": false, - "titleTooltip": "", - "widgetStyle": {}, - "widgetCss": ".status {\r\n border-radius: 20px;\r\n font-weight: 500;\r\n padding: 5px 15px;\r\n }\r\n\r\n .status-active {\r\n color: green;\r\n background: rgba(0, 128, 0, 0.1);\r\n }\r\n\r\n .status-inactive {\r\n color: red;\r\n background: rgba(255, 0, 0, 0.1);\r\n }\r\n", - "pageSize": 1024, - "noDataDisplayMessage": "", - "enableDataExport": false, - "actions": { - "actionCellButton": [ - { - "name": "Show Device Info", - "icon": "info", - "useShowWidgetActionFunction": null, - "showWidgetActionFunction": "return true;", - "type": "custom", - "customFunction": "const url = `${window.location.origin + widgetContext.utils.getEntityDetailsPageURL(entityId.id, entityId.entityType)}`;\nwindow.open(url, '_blank');", - "openInSeparateDialog": false, - "openInPopover": false, - "id": "94de7690-f91d-b032-6771-85af99abd749" - } - ] - } - }, - "row": 0, - "col": 0, - "id": "e3a9539d-2ccb-96e0-2afa-36f93e40233d", - "typeFullFqn": "system.cards.entities_table" - }, "cf2eba6b-44f6-9cc2-6089-35c735f54898": { "type": "latest", "sizeX": 7.5, From 727c9b8fbc47687b37ab4c96bc4431ae9590f1e6 Mon Sep 17 00:00:00 2001 From: Volodymyr Babak Date: Thu, 20 Jun 2024 18:34:06 +0300 Subject: [PATCH 58/58] Edge install instructions update. Use Java 17. Fix tag usage --- .../install/centos/instructions.md | 14 ++-- .../install/ubuntu/instructions.md | 14 ++-- ...EdgeInstallUpgradeInstructionsService.java | 65 +++++++++++++++++ ...DefaultEdgeInstallInstructionsService.java | 71 +++++-------------- ...DefaultEdgeUpgradeInstructionsService.java | 55 ++++---------- 5 files changed, 110 insertions(+), 109 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/edge/instructions/BaseEdgeInstallUpgradeInstructionsService.java diff --git a/application/src/main/data/json/edge/instructions/install/centos/instructions.md b/application/src/main/data/json/edge/instructions/install/centos/instructions.md index 22bb3f63a0..bcc1967dfd 100644 --- a/application/src/main/data/json/edge/instructions/install/centos/instructions.md +++ b/application/src/main/data/json/edge/instructions/install/centos/instructions.md @@ -8,15 +8,15 @@ sudo yum install -y nano wget sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm ``` -#### Install Java 11 (OpenJDK) -ThingsBoard service is running on Java 11. Follow these instructions to install OpenJDK 11: +#### Install Java 17 (OpenJDK) +ThingsBoard service is running on Java 17. Follow these instructions to install OpenJDK 17: ```bash -sudo yum install java-11-openjdk +sudo yum install java-17-openjdk {:copy-code} ``` -Please don't forget to configure your operating system to use OpenJDK 11 by default. +Please don't forget to configure your operating system to use OpenJDK 17 by default. You can configure which version is the default using the following command: ```bash @@ -34,7 +34,7 @@ java -version Expected command output is: ```text -openjdk version "11.0.xx" +openjdk version "17.x.xx" OpenJDK Runtime Environment (...) OpenJDK 64-Bit Server VM (build ...) ``` @@ -144,14 +144,14 @@ CREATE DATABASE tb_edge; Download installation package: ```bash -wget https://github.com/thingsboard/thingsboard-edge/releases/download/v${TB_EDGE_VERSION}/tb-edge-${TB_EDGE_VERSION}.rpm +wget https://github.com/thingsboard/thingsboard-edge/releases/download/v${TB_EDGE_TAG}/tb-edge-${TB_EDGE_TAG}.rpm {:copy-code} ``` Go to the download repository and install ThingsBoard Edge service: ```bash -sudo rpm -Uvh tb-edge-${TB_EDGE_VERSION}.rpm +sudo rpm -Uvh tb-edge-${TB_EDGE_TAG}.rpm {:copy-code} ``` diff --git a/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md b/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md index 6fcdcd50f2..fe9601443f 100644 --- a/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md +++ b/application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md @@ -1,15 +1,15 @@ Here is the list of commands, that can be used to quickly install ThingsBoard Edge on Ubuntu Server and connect to the server. -#### Install Java 11 (OpenJDK) -ThingsBoard service is running on Java 11. Follow these instructions to install OpenJDK 11: +#### Install Java 17 (OpenJDK) +ThingsBoard service is running on Java 17. Follow these instructions to install OpenJDK 17: ```bash sudo apt update -sudo apt install openjdk-11-jdk +sudo apt install openjdk-17-jdk {:copy-code} ``` -Please don't forget to configure your operating system to use OpenJDK 11 by default. +Please don't forget to configure your operating system to use OpenJDK 17 by default. You can configure which version is the default using the following command: ```bash @@ -27,7 +27,7 @@ java -version Expected command output is: ```text -openjdk version "11.0.xx" +openjdk version "17.x.xx" OpenJDK Runtime Environment (...) OpenJDK 64-Bit Server VM (build ...) ``` @@ -76,14 +76,14 @@ CREATE DATABASE tb_edge; Download installation package: ```bash -wget https://github.com/thingsboard/thingsboard-edge/releases/download/v${TB_EDGE_VERSION}/tb-edge-${TB_EDGE_VERSION}.deb +wget https://github.com/thingsboard/thingsboard-edge/releases/download/v${TB_EDGE_TAG}/tb-edge-${TB_EDGE_TAG}.deb {:copy-code} ``` Go to the download repository and install ThingsBoard Edge service: ```bash -sudo dpkg -i tb-edge-${TB_EDGE_VERSION}.deb +sudo dpkg -i tb-edge-${TB_EDGE_TAG}.deb {:copy-code} ``` diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/BaseEdgeInstallUpgradeInstructionsService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/BaseEdgeInstallUpgradeInstructionsService.java new file mode 100644 index 0000000000..c82f7babc5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/edge/instructions/BaseEdgeInstallUpgradeInstructionsService.java @@ -0,0 +1,65 @@ +/** + * 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.server.service.edge.instructions; + +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.thingsboard.server.service.install.InstallScripts; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +@Slf4j +@RequiredArgsConstructor +public abstract class BaseEdgeInstallUpgradeInstructionsService { + + private static final String EDGE_DIR = "edge"; + private static final String INSTRUCTIONS_DIR = "instructions"; + + private final InstallScripts installScripts; + + @Value("${app.version:unknown}") + @Setter + protected String appVersion; + + protected String readFile(Path file) { + try { + return Files.readString(file); + } catch (IOException e) { + log.warn("Failed to read file: {}", file, e); + throw new RuntimeException(e); + } + } + + protected String getTagVersion(String version) { + return version.endsWith(".0") ? version.substring(0, version.length() - 2) : version; + } + + protected Path resolveFile(String subDir, String... subDirs) { + return getEdgeInstructionsDir().resolve(Paths.get(subDir, subDirs)); + } + + protected Path getEdgeInstructionsDir() { + return Paths.get(installScripts.getDataDir(), InstallScripts.JSON_DIR, EDGE_DIR, INSTRUCTIONS_DIR, getBaseDirName()); + } + + protected abstract String getBaseDirName(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java index fd0d9fc2dd..64f96aa4e4 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeInstallInstructionsService.java @@ -15,8 +15,7 @@ */ package org.thingsboard.server.service.edge.instructions; -import lombok.RequiredArgsConstructor; -import lombok.Setter; +import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -27,47 +26,32 @@ import org.thingsboard.server.dao.util.DeviceConnectivityUtil; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.install.InstallScripts; -import jakarta.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; - @Service @Slf4j -@RequiredArgsConstructor @ConditionalOnProperty(prefix = "edges", value = "enabled", havingValue = "true") @TbCoreComponent -public class DefaultEdgeInstallInstructionsService implements EdgeInstallInstructionsService { +public class DefaultEdgeInstallInstructionsService extends BaseEdgeInstallUpgradeInstructionsService implements EdgeInstallInstructionsService { - private static final String EDGE_DIR = "edge"; - private static final String INSTRUCTIONS_DIR = "instructions"; private static final String INSTALL_DIR = "install"; - private final InstallScripts installScripts; - @Value("${edges.rpc.port}") private int rpcPort; @Value("${edges.rpc.ssl.enabled}") private boolean sslEnabled; - @Value("${app.version:unknown}") - @Setter - private String appVersion; + public DefaultEdgeInstallInstructionsService(InstallScripts installScripts) { + super(installScripts); + } @Override public EdgeInstructions getInstallInstructions(Edge edge, String installationMethod, HttpServletRequest request) { - switch (installationMethod.toLowerCase()) { - case "docker": - return getDockerInstallInstructions(edge, request); - case "ubuntu": - return getUbuntuInstallInstructions(edge, request); - case "centos": - return getCentosInstallInstructions(edge, request); - default: - throw new IllegalArgumentException("Unsupported installation method for Edge: " + installationMethod); - } + return switch (installationMethod.toLowerCase()) { + case "docker" -> getDockerInstallInstructions(edge, request); + case "ubuntu", "centos" -> getLinuxInstallInstructions(edge, request, installationMethod.toLowerCase()); + default -> + throw new IllegalArgumentException("Unsupported installation method for Edge: " + installationMethod); + }; } private EdgeInstructions getDockerInstallInstructions(Edge edge, HttpServletRequest request) { @@ -88,25 +72,16 @@ public class DefaultEdgeInstallInstructionsService implements EdgeInstallInstruc return new EdgeInstructions(dockerInstallInstructions); } - private EdgeInstructions getUbuntuInstallInstructions(Edge edge, HttpServletRequest request) { - String ubuntuInstallInstructions = readFile(resolveFile("ubuntu", "instructions.md")); + private EdgeInstructions getLinuxInstallInstructions(Edge edge, HttpServletRequest request, String os) { + String ubuntuInstallInstructions = readFile(resolveFile(os, "instructions.md")); ubuntuInstallInstructions = replacePlaceholders(ubuntuInstallInstructions, edge); ubuntuInstallInstructions = ubuntuInstallInstructions.replace("${BASE_URL}", request.getServerName()); String edgeVersion = appVersion.replace("-SNAPSHOT", ""); ubuntuInstallInstructions = ubuntuInstallInstructions.replace("${TB_EDGE_VERSION}", edgeVersion); + ubuntuInstallInstructions = ubuntuInstallInstructions.replace("${TB_EDGE_TAG}", getTagVersion(edgeVersion)); return new EdgeInstructions(ubuntuInstallInstructions); } - - private EdgeInstructions getCentosInstallInstructions(Edge edge, HttpServletRequest request) { - String centosInstallInstructions = readFile(resolveFile("centos", "instructions.md")); - centosInstallInstructions = replacePlaceholders(centosInstallInstructions, edge); - centosInstallInstructions = centosInstallInstructions.replace("${BASE_URL}", request.getServerName()); - String edgeVersion = appVersion.replace("-SNAPSHOT", ""); - centosInstallInstructions = centosInstallInstructions.replace("${TB_EDGE_VERSION}", edgeVersion); - return new EdgeInstructions(centosInstallInstructions); - } - private String replacePlaceholders(String instructions, Edge edge) { instructions = instructions.replace("${CLOUD_ROUTING_KEY}", edge.getRoutingKey()); instructions = instructions.replace("${CLOUD_ROUTING_SECRET}", edge.getSecret()); @@ -115,20 +90,8 @@ public class DefaultEdgeInstallInstructionsService implements EdgeInstallInstruc return instructions; } - private String readFile(Path file) { - try { - return Files.readString(file); - } catch (IOException e) { - log.warn("Failed to read file: {}", file, e); - throw new RuntimeException(e); - } - } - - private Path resolveFile(String subDir, String... subDirs) { - return getEdgeInstallInstructionsDir().resolve(Paths.get(subDir, subDirs)); - } - - private Path getEdgeInstallInstructionsDir() { - return Paths.get(installScripts.getDataDir(), InstallScripts.JSON_DIR, EDGE_DIR, INSTRUCTIONS_DIR, INSTALL_DIR); + @Override + protected String getBaseDirName() { + return INSTALL_DIR; } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java index 3ff8ae784a..7113123a45 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/instructions/DefaultEdgeUpgradeInstructionsService.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.edge.instructions; -import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -32,47 +31,37 @@ import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.install.InstallScripts; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.Optional; @Service @Slf4j -@RequiredArgsConstructor @ConditionalOnProperty(prefix = "edges", value = "enabled", havingValue = "true") @TbCoreComponent -public class DefaultEdgeUpgradeInstructionsService implements EdgeUpgradeInstructionsService { +public class DefaultEdgeUpgradeInstructionsService extends BaseEdgeInstallUpgradeInstructionsService implements EdgeUpgradeInstructionsService { private static final Map upgradeVersionHashMap = new HashMap<>(); - private static final String EDGE_DIR = "edge"; - private static final String INSTRUCTIONS_DIR = "instructions"; private static final String UPGRADE_DIR = "upgrade"; - private final InstallScripts installScripts; private final AttributesService attributesService; - @Value("${app.version:unknown}") - @Setter - private String appVersion; + public DefaultEdgeUpgradeInstructionsService(AttributesService attributesService, InstallScripts installScripts) { + super(installScripts); + this.attributesService = attributesService; + } @Override public EdgeInstructions getUpgradeInstructions(String edgeVersion, String upgradeMethod) { String tbVersion = appVersion.replace("-SNAPSHOT", ""); String currentEdgeVersion = convertEdgeVersionToDocsFormat(edgeVersion); - switch (upgradeMethod.toLowerCase()) { - case "docker": - return getDockerUpgradeInstructions(tbVersion, currentEdgeVersion); - case "ubuntu": - case "centos": - return getLinuxUpgradeInstructions(tbVersion, currentEdgeVersion, upgradeMethod.toLowerCase()); - default: - throw new IllegalArgumentException("Unsupported upgrade method for Edge: " + upgradeMethod); - } + return switch (upgradeMethod.toLowerCase()) { + case "docker" -> getDockerUpgradeInstructions(tbVersion, currentEdgeVersion); + case "ubuntu", "centos" -> + getLinuxUpgradeInstructions(tbVersion, currentEdgeVersion, upgradeMethod.toLowerCase()); + default -> throw new IllegalArgumentException("Unsupported upgrade method for Edge: " + upgradeMethod); + }; } @Override @@ -167,28 +156,12 @@ public class DefaultEdgeUpgradeInstructionsService implements EdgeUpgradeInstruc return new EdgeInstructions(result.toString()); } - private String getTagVersion(String version) { - return version.endsWith(".0") ? version.substring(0, version.length() - 2) : version; - } - private String convertEdgeVersionToDocsFormat(String edgeVersion) { return edgeVersion.replace("_", ".").substring(2); } - private String readFile(Path file) { - try { - return Files.readString(file); - } catch (IOException e) { - log.warn("Failed to read file: {}", file, e); - throw new RuntimeException(e); - } - } - - private Path resolveFile(String subDir, String... subDirs) { - return getEdgeInstallInstructionsDir().resolve(Paths.get(subDir, subDirs)); - } - - private Path getEdgeInstallInstructionsDir() { - return Paths.get(installScripts.getDataDir(), InstallScripts.JSON_DIR, EDGE_DIR, INSTRUCTIONS_DIR, UPGRADE_DIR); + @Override + protected String getBaseDirName() { + return UPGRADE_DIR; } }