Browse Source

Merge with master

pull/12723/head
Igor Kulikov 2 years ago
parent
commit
4a108d7c3f
  1. 2
      application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java
  2. 15
      application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
  3. 38
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  4. 6
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
  5. 1
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java
  6. 2
      application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java
  7. 3
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java
  8. 81
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java
  9. 4
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/StoreInfo.java
  10. 2
      common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java
  11. 1
      common/edge-api/src/main/proto/edge.proto
  12. 14
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java
  13. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java
  14. 29
      common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java
  15. 10
      common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java
  16. 2
      dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingServiceImpl.java
  17. 4
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  18. 6
      dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java
  19. 16
      dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java
  20. 3
      packaging/java/scripts/control/template.service
  21. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java
  22. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java
  23. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
  24. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java
  25. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java
  26. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java
  27. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeviceStateNode.java
  28. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java
  29. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java
  30. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java
  31. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java
  32. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java
  33. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java
  34. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java
  35. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java
  36. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java
  37. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java
  38. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToCloudNode.java
  39. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java
  40. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNode.java
  41. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java
  42. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java
  43. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java
  44. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNode.java
  45. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java
  46. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java
  47. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java
  48. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java
  49. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java
  50. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java
  51. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbAckNode.java
  52. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java
  53. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNode.java
  54. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNode.java
  55. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java
  56. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java
  57. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java
  58. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java
  59. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java
  60. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java
  61. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java
  62. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java
  63. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java
  64. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java
  65. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java
  66. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java
  67. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java
  68. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java
  69. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java
  70. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java
  71. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java
  72. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java
  73. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java
  74. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/azure/TbAzureIotHubNode.java
  75. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java
  76. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java
  77. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java
  78. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java
  79. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeConfiguration.java
  80. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java
  81. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNode.java
  82. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNode.java
  83. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java
  84. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java
  85. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java
  86. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java
  87. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
  88. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationBeginNode.java
  89. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationEndNode.java
  90. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java
  91. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java
  92. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java
  93. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java
  94. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java
  95. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java
  96. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java
  97. 1
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  98. 2
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java
  99. 9
      ui-ngx/src/app/core/http/domain.service.ts
  100. 6
      ui-ngx/src/app/core/http/rule-chain.service.ts

2
application/src/test/java/org/thingsboard/server/edge/DeviceProfileEdgeTest.java

@ -359,7 +359,7 @@ public class DeviceProfileEdgeTest extends AbstractEdgeTest {
transportConfiguration.setBootstrapServerUpdateEnable(true); transportConfiguration.setBootstrapServerUpdateEnable(true);
TelemetryMappingConfiguration observeAttrConfiguration = TelemetryMappingConfiguration observeAttrConfiguration =
JacksonUtil.fromString(AbstractLwM2MIntegrationTest.OBSERVE_ATTRIBUTES_WITH_PARAMS, TelemetryMappingConfiguration.class); JacksonUtil.fromString(AbstractLwM2MIntegrationTest.TELEMETRY_WITHOUT_OBSERVE, TelemetryMappingConfiguration.class);
transportConfiguration.setObserveAttr(observeAttrConfiguration); transportConfiguration.setObserveAttr(observeAttrConfiguration);
List<LwM2MBootstrapServerCredential> bootstrap = new ArrayList<>(); List<LwM2MBootstrapServerCredential> bootstrap = new ArrayList<>();

15
application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java

@ -66,7 +66,9 @@ import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
@ -78,6 +80,7 @@ import java.util.stream.Collectors;
@Slf4j @Slf4j
public class EdgeImitator { public class EdgeImitator {
private static final int MAX_DOWNLINK_FAILS = 2;
private final String routingKey; private final String routingKey;
private final String routingSecret; private final String routingSecret;
@ -93,6 +96,7 @@ public class EdgeImitator {
private boolean randomFailuresOnTimeseriesDownlink = false; private boolean randomFailuresOnTimeseriesDownlink = false;
@Setter @Setter
private double failureProbability = 0.0; private double failureProbability = 0.0;
private final Map<Integer, Integer> downlinkFailureCountMap = new HashMap<>();
@Getter @Getter
private EdgeConfiguration configuration; private EdgeConfiguration configuration;
@ -244,8 +248,11 @@ public class EdgeImitator {
if (downlinkMsg.getEntityDataCount() > 0) { if (downlinkMsg.getEntityDataCount() > 0) {
for (EntityDataProto entityData : downlinkMsg.getEntityDataList()) { for (EntityDataProto entityData : downlinkMsg.getEntityDataList()) {
if (randomFailuresOnTimeseriesDownlink) { if (randomFailuresOnTimeseriesDownlink) {
if (getRandomBoolean()) { int downlinkMsgId = downlinkMsg.getDownlinkMsgId();
if (getRandomBoolean() && checkFailureThreshold(downlinkMsgId)) {
result.add(Futures.immediateFailedFuture(new RuntimeException("Random failure. This is expected error for edge test"))); result.add(Futures.immediateFailedFuture(new RuntimeException("Random failure. This is expected error for edge test")));
downlinkFailureCountMap.put(downlinkMsgId, downlinkFailureCountMap.getOrDefault(downlinkMsgId, 0) + 1);
} else { } else {
result.add(saveDownlinkMsg(entityData)); result.add(saveDownlinkMsg(entityData));
} }
@ -354,6 +361,12 @@ public class EdgeImitator {
return Futures.allAsList(result); return Futures.allAsList(result);
} }
private boolean checkFailureThreshold(int downlinkMsgId) {
return failureProbability == 100 ||
downlinkFailureCountMap.get(downlinkMsgId) == null ||
downlinkFailureCountMap.get(downlinkMsgId) < MAX_DOWNLINK_FAILS;
}
private boolean getRandomBoolean() { private boolean getRandomBoolean() {
double randomValue = ThreadLocalRandom.current().nextDouble() * 100; double randomValue = ThreadLocalRandom.current().nextDouble() * 100;
return randomValue <= this.failureProbability; return randomValue <= this.failureProbability;

38
application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java

@ -147,8 +147,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
" \"telemetry\": [],\n" + " \"telemetry\": [],\n" +
" \"attributeLwm2m\": {}\n" + " \"attributeLwm2m\": {}\n" +
" }"; " }";
public static String OBSERVE_ATTRIBUTES_WITH_PARAMS = public static String TELEMETRY_WITHOUT_OBSERVE =
" {\n" + " {\n" +
" \"keyName\": {\n" + " \"keyName\": {\n" +
" \"/3_1.2/0/9\": \"batteryLevel\"\n" + " \"/3_1.2/0/9\": \"batteryLevel\"\n" +
@ -161,6 +160,39 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
" ],\n" + " ],\n" +
" \"attributeLwm2m\": {}\n" + " \"attributeLwm2m\": {}\n" +
" }"; " }";
public static String TELEMETRY_WITH_ONE_OBSERVE =
" {\n" +
" \"keyName\": {\n" +
" \"/3_1.2/0/9\": \"batteryLevel\"\n" +
" },\n" +
" \"observe\": [\n" +
" \"/3_1.2/0/9\"\n" +
" ],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"/3_1.2/0/9\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" }";
public static String TELEMETRY_WITH_MANY_OBSERVE =
" {\n" +
" \"keyName\": {\n" +
" \"/3_1.2/0/9\": \"batteryLevel\",\n" +
" \"/3_1.2/0/20\": \"batteryStatus\"\n" +
" },\n" +
" \"observe\": [\n" +
" \"/3_1.2/0/9\",\n" +
" \"/3_1.2/0/20\"\n" +
" ],\n" +
" \"attribute\": [],\n" +
" \"telemetry\": [\n" +
" \"/3_1.2/0/9\",\n" +
" \"/3_1.2/0/20\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" }";
public static final String CLIENT_LWM2M_SETTINGS = public static final String CLIENT_LWM2M_SETTINGS =
" {\n" + " {\n" +
@ -217,7 +249,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
LwM2MDeviceCredentials deviceCredentials, LwM2MDeviceCredentials deviceCredentials,
String endpoint, String endpoint,
boolean queueMode) throws Exception { boolean queueMode) throws Exception {
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITH_PARAMS, getBootstrapServerCredentialsNoSec(NONE)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITHOUT_OBSERVE, getBootstrapServerCredentialsNoSec(NONE));
DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + endpoint, transportConfiguration); DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + endpoint, transportConfiguration);
Device device = createLwm2mDevice(deviceCredentials, endpoint, deviceProfile.getId()); Device device = createLwm2mDevice(deviceCredentials, endpoint, deviceProfile.getId());

6
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java

@ -458,6 +458,12 @@ public class LwM2MTestClient {
} }
} }
public void stop(boolean deregister) {
if (leshanClient != null) {
leshanClient.stop(deregister);
}
}
private void awaitClientAfterStartConnectLw() { private void awaitClientAfterStartConnectLw() {
LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(endpoint); LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(endpoint);
Mockito.doAnswer(invocationOnMock -> null).when(defaultLwM2mUplinkMsgHandlerTest).initAttributes(lwM2MClient, true); Mockito.doAnswer(invocationOnMock -> null).when(defaultLwM2mUplinkMsgHandlerTest).initAttributes(lwM2MClient, true);

1
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/SimpleLwM2MDevice.java

@ -93,6 +93,7 @@ public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyabl
try { try {
executorService.scheduleWithFixedDelay(() -> { executorService.scheduleWithFixedDelay(() -> {
fireResourceChange(9); fireResourceChange(9);
fireResourceChange(20);
} }
, 1, 1, TimeUnit.SECONDS); // 2 sec , 1, 1, TimeUnit.SECONDS); // 2 sec
// , 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN // , 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN

2
application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java

@ -51,7 +51,7 @@ public class Ota5LwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
@Test @Test
public void testFirmwareUpdateWithClientWithoutFirmwareOtaInfoFromProfile_IsNotSupported() throws Exception { public void testFirmwareUpdateWithClientWithoutFirmwareOtaInfoFromProfile_IsNotSupported() throws Exception {
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(OBSERVE_ATTRIBUTES_WITH_PARAMS, getBootstrapServerCredentialsNoSec(NONE)); Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITHOUT_OBSERVE, getBootstrapServerCredentialsNoSec(NONE));
DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + this.CLIENT_ENDPOINT_WITHOUT_FW_INFO, transportConfiguration); DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + this.CLIENT_ENDPOINT_WITHOUT_FW_INFO, transportConfiguration);
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO)); LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO));
final Device device = createLwm2mDevice(deviceCredentials, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO, deviceProfile.getId()); final Device device = createLwm2mDevice(deviceCredentials, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO, deviceProfile.getId());

3
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java

@ -196,7 +196,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
false); false);
} }
protected void basicTestConnection(Security security, Security securityBs, protected Device basicTestConnection(Security security, Security securityBs,
LwM2MDeviceCredentials deviceCredentials, LwM2MDeviceCredentials deviceCredentials,
String endpoint, String endpoint,
Lwm2mDeviceProfileTransportConfiguration transportConfiguration, Lwm2mDeviceProfileTransportConfiguration transportConfiguration,
@ -227,6 +227,7 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
return lwM2MTestClient.getClientStates().contains(finishState) || lwM2MTestClient.getClientStates().contains(ON_UPDATE_SUCCESS); return lwM2MTestClient.getClientStates().contains(finishState) || lwM2MTestClient.getClientStates().contains(ON_UPDATE_SUCCESS);
}); });
Assert.assertTrue(lwM2MTestClient.getClientStates().containsAll(expectedStatuses)); Assert.assertTrue(lwM2MTestClient.getClientStates().containsAll(expectedStatuses));
return device;
} }

81
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java

@ -16,10 +16,13 @@
package org.thingsboard.server.transport.lwm2m.security.sql; package org.thingsboard.server.transport.lwm2m.security.sql;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.client.object.Security; import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.Hex;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.MvcResult;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredential; import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredential;
@ -27,7 +30,6 @@ import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTrans
import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest; import org.thingsboard.server.transport.lwm2m.security.AbstractSecurityLwM2MIntegrationTest;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import static org.eclipse.leshan.client.object.Security.psk; import static org.eclipse.leshan.client.object.Security.psk;
import static org.eclipse.leshan.client.object.Security.pskBootstrap; import static org.eclipse.leshan.client.object.Security.pskBootstrap;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -37,6 +39,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClient
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.BOTH;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE;
@Slf4j
public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTest { public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTest {
//Lwm2m only //Lwm2m only
@ -66,6 +69,82 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); true);
} }
@Test
public void testWithPskConnectLwm2mOneObserveSuccessUpdateProfileManyObserveUpdateRegistrationSuccess() throws Exception {
String clientEndpoint = CLIENT_ENDPOINT_PSK;
String identity = CLIENT_PSK_IDENTITY;
String keyPsk = CLIENT_PSK_KEY;
PSKClientCredential clientCredentials = new PSKClientCredential();
clientCredentials.setEndpoint(clientEndpoint);
clientCredentials.setIdentity(identity);
clientCredentials.setKey(keyPsk);
Security security = psk(SECURE_URI,
shortServerId,
identity.getBytes(StandardCharsets.UTF_8),
Hex.decodeHex(keyPsk.toCharArray()));
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_ONE_OBSERVE, getBootstrapServerCredentialsSecure(PSK, NONE));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false);
String awaitAlias = "await on client state (Psk_Lwm2m)";
Device lwm2mDevice = this.basicTestConnection(security,
null,
deviceCredentials,
clientEndpoint,
transportConfiguration,
awaitAlias,
expectedStatusesRegistrationLwm2mSuccess,
false,
ON_REGISTRATION_SUCCESS,
true);
awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString());
DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class);
transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_MANY_OBSERVE, getBootstrapServerCredentialsSecure(PSK, NONE));
foundDeviceProfile.getProfileData().setTransportConfiguration(transportConfiguration);
DeviceProfile lwm2mDeviceProfileManyParams = doPost("/api/deviceProfile", foundDeviceProfile, DeviceProfile.class);
Assert.assertNotNull(lwm2mDeviceProfileManyParams);
awaitObserveReadAll(2, lwm2mDevice.getId().getId().toString());
awaitUpdateReg(3);
}
@Test
public void testWithPskConnectLwm2mSuccessObserveSuccessUnRegClientUpdateProfileObserveConnectLwm2mSuccessOWithNewObserve() throws Exception {
String clientEndpoint = CLIENT_ENDPOINT_PSK;
String identity = CLIENT_PSK_IDENTITY;
String keyPsk = CLIENT_PSK_KEY;
PSKClientCredential clientCredentials = new PSKClientCredential();
clientCredentials.setEndpoint(clientEndpoint);
clientCredentials.setIdentity(identity);
clientCredentials.setKey(keyPsk);
Security security = psk(SECURE_URI,
shortServerId,
identity.getBytes(StandardCharsets.UTF_8),
Hex.decodeHex(keyPsk.toCharArray()));
Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_ONE_OBSERVE, getBootstrapServerCredentialsSecure(PSK, NONE));
LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsSecure(clientCredentials, null, null, PSK, false);
String awaitAlias = "await on client state (Psk_Lwm2m)";
Device lwm2mDevice = this.basicTestConnection(security,
null,
deviceCredentials,
clientEndpoint,
transportConfiguration,
awaitAlias,
expectedStatusesRegistrationLwm2mSuccess,
false,
ON_REGISTRATION_SUCCESS,
true);
awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString());
lwM2MTestClient.stop(true);
DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class);
transportConfiguration = getTransportConfiguration(TELEMETRY_WITH_MANY_OBSERVE, getBootstrapServerCredentialsSecure(PSK, NONE));
foundDeviceProfile.getProfileData().setTransportConfiguration(transportConfiguration);
DeviceProfile lwm2mDeviceProfileManyParams = doPost("/api/deviceProfile", foundDeviceProfile, DeviceProfile.class);
Assert.assertNotNull(lwm2mDeviceProfileManyParams);
lwM2MTestClient.start(true);
awaitObserveReadAll(2, lwm2mDevice.getId().getId().toString());
awaitUpdateReg(3);
}
@Test @Test
public void testWithPskConnectLwm2mBadPskKeyByLength_BAD_REQUEST() throws Exception { public void testWithPskConnectLwm2mBadPskKeyByLength_BAD_REQUEST() throws Exception {

4
common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/StoreInfo.java

@ -15,12 +15,16 @@
*/ */
package org.thingsboard.server.common.data.mobile.app; package org.thingsboard.server.common.data.mobile.app;
import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
@Data @Data
@Builder @Builder
@NoArgsConstructor
@AllArgsConstructor
public class StoreInfo { public class StoreInfo {
@NoXss @NoXss

2
common/edge-api/src/main/java/org/thingsboard/edge/rpc/EdgeGrpcClient.java

@ -136,7 +136,7 @@ public class EdgeGrpcClient implements EdgeRpcClient {
.setConnectRequestMsg(ConnectRequestMsg.newBuilder() .setConnectRequestMsg(ConnectRequestMsg.newBuilder()
.setEdgeRoutingKey(edgeKey) .setEdgeRoutingKey(edgeKey)
.setEdgeSecret(edgeSecret) .setEdgeSecret(edgeSecret)
.setEdgeVersion(EdgeVersion.V_3_9_0) .setEdgeVersion(EdgeVersion.V_4_0_0)
.setMaxInboundMessageSize(maxInboundMessageSize) .setMaxInboundMessageSize(maxInboundMessageSize)
.build()) .build())
.build()); .build());

1
common/edge-api/src/main/proto/edge.proto

@ -41,6 +41,7 @@ enum EdgeVersion {
V_3_7_0 = 7; V_3_7_0 = 7;
V_3_8_0 = 8; V_3_8_0 = 8;
V_3_9_0 = 9; V_3_9_0 = 9;
V_4_0_0 = 10;
} }
/** /**

14
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java

@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.server.store.util;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
@ -57,7 +56,7 @@ public class LwM2MClientSerDes {
@SneakyThrows @SneakyThrows
public static byte[] serialize(LwM2mClient client) { public static byte[] serialize(LwM2mClient client) {
JsonObject o = new JsonObject(); JsonObject o = new JsonObject();
o.addProperty("nodeId", client.getNodeId()); o.addProperty("nodeId", client.getNodeId());
o.addProperty("endpoint", client.getEndpoint()); o.addProperty("endpoint", client.getEndpoint());
@ -111,14 +110,7 @@ public class LwM2MClientSerDes {
o.addProperty("defaultObjectIDVer", client.getDefaultObjectIDVer().toString()); o.addProperty("defaultObjectIDVer", client.getDefaultObjectIDVer().toString());
if (client.getRegistration() != null) { if (client.getRegistration() != null) {
String registrationAddress = client.getRegistration().getAddress().toString();
JsonNode registrationNode = registrationSerDes.jSerialize(client.getRegistration()); JsonNode registrationNode = registrationSerDes.jSerialize(client.getRegistration());
if (!registrationAddress.equals(registrationNode.get("transportdata").get("address").asText())){
ObjectNode actualRegAddress = (ObjectNode)registrationNode.get("transportdata");
actualRegAddress.put("address", registrationAddress);
ObjectNode actualIdentity = (ObjectNode) actualRegAddress.get("identity");
actualIdentity.put("address", registrationAddress);
}
o.addProperty("registration", registrationNode.toString()); o.addProperty("registration", registrationNode.toString());
} }
o.addProperty("asleep", client.isAsleep()); o.addProperty("asleep", client.isAsleep());
@ -188,7 +180,7 @@ public class LwM2MClientSerDes {
case STRING: case STRING:
return value.getAsString(); return value.getAsString();
case TIME: case TIME:
return Instant.ofEpochMilli(value.getAsLong()); return new Date(value.getAsLong());
case OBJLNK: case OBJLNK:
return ObjectLink.decodeFromString(value.getAsString()); return ObjectLink.decodeFromString(value.getAsString());
case UNSIGNED_INTEGER: case UNSIGNED_INTEGER:
@ -249,7 +241,7 @@ public class LwM2MClientSerDes {
o.addProperty(VALUE, ((ObjectLink) value).encodeToString()); o.addProperty(VALUE, ((ObjectLink) value).encodeToString());
break; break;
case UNSIGNED_INTEGER: case UNSIGNED_INTEGER:
o.addProperty(VALUE, Integer.toUnsignedString((int)value)); o.addProperty(VALUE, Integer.toUnsignedString((int) value));
break; break;
default: default:
throw new LwM2mNodeException(String.format("Type %s is not supported", type.name())); throw new LwM2mNodeException(String.format("Type %s is not supported", type.name()));

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java

@ -279,6 +279,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl
clientContext.unregister(client, registration); clientContext.unregister(client, registration);
SessionInfoProto sessionInfo = client.getSession(); SessionInfoProto sessionInfo = client.getSession();
if (sessionInfo != null) { if (sessionInfo != null) {
securityStore.remove(client.getEndpoint(), client.getRegistration().getId());
sessionManager.deregister(sessionInfo); sessionManager.deregister(sessionInfo);
sessionStore.remove(registration.getEndpoint()); sessionStore.remove(registration.getEndpoint());
log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
@ -401,7 +402,6 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl
.stream().filter(e -> e.getProfileId() != null) .stream().filter(e -> e.getProfileId() != null)
.filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toList()); .filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toList());
clients.forEach(client -> { clients.forEach(client -> {
this.securityStore.remove(client.getEndpoint(), client.getRegistration().getId());
client.onDeviceProfileUpdate(deviceProfile); client.onDeviceProfileUpdate(deviceProfile);
}); });
if (clients.size() > 0) { if (clients.size() > 0) {

29
common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java

@ -18,13 +18,17 @@ package org.thingsboard.server.transport.lwm2m.server.store.util;
import org.eclipse.leshan.core.LwM2m.LwM2mVersion; import org.eclipse.leshan.core.LwM2m.LwM2mVersion;
import org.eclipse.leshan.core.endpoint.EndpointUriUtil; import org.eclipse.leshan.core.endpoint.EndpointUriUtil;
import org.eclipse.leshan.core.link.Link; import org.eclipse.leshan.core.link.Link;
import org.eclipse.leshan.core.link.attributes.AttributeSet;
import org.eclipse.leshan.core.link.attributes.ResourceTypeAttribute;
import org.eclipse.leshan.core.node.LwM2mMultipleResource; import org.eclipse.leshan.core.node.LwM2mMultipleResource;
import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mResource;
import org.eclipse.leshan.core.node.LwM2mSingleResource; import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.peer.IpPeer; import org.eclipse.leshan.core.peer.IpPeer;
import org.eclipse.leshan.core.request.WriteRequest; import org.eclipse.leshan.core.request.WriteRequest;
import org.eclipse.leshan.server.registration.DefaultRegistrationDataExtractor;
import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.registration.Registration;
import org.eclipse.leshan.server.registration.RegistrationDataExtractor;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.device.data.PowerMode; import org.thingsboard.server.common.data.device.data.PowerMode;
@ -83,12 +87,22 @@ public class LwM2MClientSerDesTest {
client.init(credentialsResponse, UUID.randomUUID()); client.init(credentialsResponse, UUID.randomUUID());
AttributeSet attrs = new AttributeSet( //
new ResourceTypeAttribute("oma.lwm2m"));
Link[] objs = new Link[]{new Link("/15", attrs), new Link("/17")};
RegistrationDataExtractor.RegistrationData dataFromObjectLinks = new DefaultRegistrationDataExtractor().extractDataFromObjectLinks(objs,
LwM2mVersion.V1_0);
Registration registration = new Registration Registration registration = new Registration
.Builder("test", "endpoint", new IpPeer(new InetSocketAddress(Inet4Address.getLoopbackAddress(), 1000)), .Builder("test", "endpoint", new IpPeer(new InetSocketAddress(Inet4Address.getLoopbackAddress(), 1000)),
EndpointUriUtil.createUri("coap://localhost:5685")) EndpointUriUtil.createUri("coap://localhost:5685"))
.supportedContentFormats() .objectLinks(objs)
.supportedObjects(Map.of(15, LwM2mVersion.V1_0, 17, LwM2mVersion.V1_0)) .rootPath(dataFromObjectLinks.getAlternatePath())
.objectLinks(new Link[] { new Link("/15"), new Link("/17") }) .supportedContentFormats(dataFromObjectLinks.getSupportedContentFormats())
.supportedObjects(dataFromObjectLinks.getSupportedObjects())
.availableInstances(dataFromObjectLinks.getAvailableInstances())
.build(); .build();
client.setRegistration(registration); client.setRegistration(registration);
@ -135,12 +149,7 @@ public class LwM2MClientSerDesTest {
assertEquals(client.getPsmActivityTimer(), desClient.getPsmActivityTimer()); assertEquals(client.getPsmActivityTimer(), desClient.getPsmActivityTimer());
assertEquals(client.getPagingTransmissionWindow(), desClient.getPagingTransmissionWindow()); assertEquals(client.getPagingTransmissionWindow(), desClient.getPagingTransmissionWindow());
assertEquals(client.getEdrxCycle(), desClient.getEdrxCycle()); assertEquals(client.getEdrxCycle(), desClient.getEdrxCycle());
if (((IpPeer)desClient.getRegistration().getClientTransportData()).getSocketAddress().isUnresolved()) { assertEquals(client.getRegistration(), desClient.getRegistration());
String actualReg = desClient.getRegistration().toString().replaceAll("/<unresolved>", "");
assertEquals(client.getRegistration().toString(), actualReg);
} else {
assertEquals(client.getRegistration(), desClient.getRegistration());
}
assertEquals(client.isAsleep(), desClient.isAsleep()); assertEquals(client.isAsleep(), desClient.isAsleep());
assertEquals(client.getLastUplinkTime(), desClient.getLastUplinkTime()); assertEquals(client.getLastUplinkTime(), desClient.getLastUplinkTime());
assertEquals(client.getSleepTask(), desClient.getSleepTask()); assertEquals(client.getSleepTask(), desClient.getSleepTask());

10
common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/GitRepository.java

@ -94,6 +94,10 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.eclipse.jgit.api.ListBranchCommand.ListMode; import static org.eclipse.jgit.api.ListBranchCommand.ListMode;
import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.REJECTED_NODELETE;
import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.REJECTED_NONFASTFORWARD;
import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.REJECTED_OTHER_REASON;
import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED;
@Slf4j @Slf4j
public class GitRepository { public class GitRepository {
@ -358,8 +362,10 @@ public class GitRepository {
result.forEach(pushResult -> { result.forEach(pushResult -> {
for (RemoteRefUpdate update : pushResult.getRemoteUpdates()) { for (RemoteRefUpdate update : pushResult.getRemoteUpdates()) {
RemoteRefUpdate.Status status = update.getStatus(); RemoteRefUpdate.Status status = update.getStatus();
if (status != RemoteRefUpdate.Status.OK && status != RemoteRefUpdate.Status.UP_TO_DATE) { if (status == REJECTED_NONFASTFORWARD || status == REJECTED_NODELETE ||
throw new RuntimeException("Failed to push changes: " + Optional.ofNullable(update.getMessage()).orElseGet(status::name)); status == REJECTED_REMOTE_CHANGED || status == REJECTED_OTHER_REASON) {
throw new RuntimeException("Remote repository answered with error: " +
Optional.ofNullable(update.getMessage()).orElseGet(status::name));
} }
} }
}); });

2
dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingServiceImpl.java

@ -115,7 +115,7 @@ public class QrCodeSettingServiceImpl extends AbstractCachedEntityService<Tenant
qrCodeSettings.setQrCodeConfig(qrCodeConfig); qrCodeSettings.setQrCodeConfig(qrCodeConfig);
qrCodeSettings.setMobileAppBundleId(qrCodeSettings.getMobileAppBundleId()); qrCodeSettings.setMobileAppBundleId(qrCodeSettings.getMobileAppBundleId());
} }
if (qrCodeSettings.isUseDefaultApp()) { if (qrCodeSettings.isUseDefaultApp() || qrCodeSettings.getMobileAppBundleId() == null) {
qrCodeSettings.setGooglePlayLink(googlePlayLink); qrCodeSettings.setGooglePlayLink(googlePlayLink);
qrCodeSettings.setAppStoreLink(appStoreLink); qrCodeSettings.setAppStoreLink(appStoreLink);
} else { } else {

4
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -19,6 +19,8 @@ import com.datastax.oss.driver.api.core.uuid.Uuids;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.Aggregation; import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.mobile.app.MobileAppVersionInfo;
import org.thingsboard.server.common.data.mobile.app.StoreInfo;
import java.util.UUID; import java.util.UUID;
@ -454,6 +456,8 @@ public class ModelConstants {
public static final String MOBILE_APP_STATUS_PROPERTY = "status"; public static final String MOBILE_APP_STATUS_PROPERTY = "status";
public static final String MOBILE_APP_VERSION_INFO_PROPERTY = "version_info"; public static final String MOBILE_APP_VERSION_INFO_PROPERTY = "version_info";
public static final String MOBILE_APP_STORE_INFO_PROPERTY = "store_info"; public static final String MOBILE_APP_STORE_INFO_PROPERTY = "store_info";
public static final MobileAppVersionInfo MOBILE_APP_VERSION_INFO_EMPTY_OBJECT = new MobileAppVersionInfo();
public static final StoreInfo MOBILE_APP_STORE_INFO_EMPTY_OBJECT = new StoreInfo();
/** /**
* Mobile application bundle constants. * Mobile application bundle constants.

6
dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java

@ -37,6 +37,8 @@ import org.thingsboard.server.dao.util.mapping.JsonConverter;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.MOBILE_APP_STORE_INFO_EMPTY_OBJECT;
import static org.thingsboard.server.dao.model.ModelConstants.MOBILE_APP_VERSION_INFO_EMPTY_OBJECT;
import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN;
@Data @Data
@ -99,8 +101,8 @@ public class MobileAppEntity extends BaseSqlEntity<MobileApp> {
mobile.setAppSecret(appSecret); mobile.setAppSecret(appSecret);
mobile.setPlatformType(platformType); mobile.setPlatformType(platformType);
mobile.setStatus(status); mobile.setStatus(status);
mobile.setVersionInfo(fromJson(versionInfo, MobileAppVersionInfo.class)); mobile.setVersionInfo(versionInfo != null ? fromJson(versionInfo, MobileAppVersionInfo.class) : MOBILE_APP_VERSION_INFO_EMPTY_OBJECT);
mobile.setStoreInfo(fromJson(storeInfo, StoreInfo.class)); mobile.setStoreInfo(storeInfo != null ? fromJson(storeInfo, StoreInfo.class) : MOBILE_APP_STORE_INFO_EMPTY_OBJECT);
return mobile; return mobile;
} }
} }

16
dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java

@ -30,21 +30,17 @@ public class MobileAppDataValidator extends DataValidator<MobileApp> {
@Override @Override
protected void validateDataImpl(TenantId tenantId, MobileApp mobileApp) { protected void validateDataImpl(TenantId tenantId, MobileApp mobileApp) {
if (mobileApp.getPlatformType() == PlatformType.ANDROID) { if (mobileApp.getStatus() == MobileAppStatus.PUBLISHED) {
if (mobileApp.getStoreInfo() != null && if (mobileApp.getStoreInfo() == null) {
throw new DataValidationException("Store info is required for published apps");
}
if (mobileApp.getPlatformType() == PlatformType.ANDROID &&
(mobileApp.getStoreInfo().getSha256CertFingerprints() == null || mobileApp.getStoreInfo().getStoreLink() == null)) { (mobileApp.getStoreInfo().getSha256CertFingerprints() == null || mobileApp.getStoreInfo().getStoreLink() == null)) {
throw new DataValidationException("Sha256CertFingerprints and store link are required"); throw new DataValidationException("Sha256CertFingerprints and store link are required");
} } else if (mobileApp.getPlatformType() == PlatformType.IOS &&
} else if (mobileApp.getPlatformType() == PlatformType.IOS) {
if (mobileApp.getStoreInfo() != null &&
(mobileApp.getStoreInfo().getAppId() == null || mobileApp.getStoreInfo().getStoreLink() == null)) { (mobileApp.getStoreInfo().getAppId() == null || mobileApp.getStoreInfo().getStoreLink() == null)) {
throw new DataValidationException("AppId and store link are required"); throw new DataValidationException("AppId and store link are required");
} }
} else {
throw new DataValidationException("Wrong application platform type");
}
if (mobileApp.getStatus() == MobileAppStatus.PUBLISHED && mobileApp.getStoreInfo() == null) {
throw new DataValidationException("Store info is required for published apps");
} }
} }
} }

3
packaging/java/scripts/control/template.service

@ -8,7 +8,8 @@ ExecStart=${pkg.installFolder}/bin/${pkg.name}.jar
SuccessExitStatus=143 SuccessExitStatus=143
# Service shall be restarted in 30 seconds when the service process exits, is killed, or a timeout is reached. # Service shall be restarted in 30 seconds when the service process exits, is killed, or a timeout is reached.
Restart=always # default 'no' # Restart parameter default value 'no'
Restart=always
RestartSec=30 RestartSec=30
[Install] [Install]

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAssignToCustomerNode.java

@ -40,7 +40,6 @@ import org.thingsboard.server.common.msg.TbMsg;
nodeDescription = "Assign message originator entity to customer", nodeDescription = "Assign message originator entity to customer",
nodeDetails = "Finds target customer by title and assign message originator entity to this customer. " + nodeDetails = "Finds target customer by title and assign message originator entity to this customer. " +
"Rule node will create a new customer if it doesn't exist, and 'Create new customer if it doesn't exist' enabled.", "Rule node will create a new customer if it doesn't exist, and 'Create new customer if it doesn't exist' enabled.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeAssignToCustomerConfig", configDirective = "tbActionNodeAssignToCustomerConfig",
icon = "add_circle", icon = "add_circle",
version = 1 version = 1

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java

@ -43,7 +43,6 @@ import org.thingsboard.server.common.msg.TbMsg;
"If alarm was not cleared, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains 'isClearedAlarm' property. " + "If alarm was not cleared, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains 'isClearedAlarm' property. " +
"Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. " + "Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. " +
"Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.", "Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeClearAlarmConfig", configDirective = "tbActionNodeClearAlarmConfig",
icon = "notifications_off" icon = "notifications_off"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java

@ -63,7 +63,6 @@ import static org.thingsboard.server.common.data.msg.TbNodeConnectionType.SUCCES
nodeDetails = "Copy attributes from asset/device to related entity view according to entity view configuration. \n " + nodeDetails = "Copy attributes from asset/device to related entity view according to entity view configuration. \n " +
"Copy will be done only for attributes that are between start and end dates and according to attribute keys configuration. \n" + "Copy will be done only for attributes that are between start and end dates and according to attribute keys configuration. \n" +
"Changes message originator to related entity view and produces new messages according to count of updated entity views", "Changes message originator to related entity view and produces new messages according to count of updated entity views",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbNodeEmptyConfig", configDirective = "tbNodeEmptyConfig",
icon = "content_copy" icon = "content_copy"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java

@ -51,7 +51,6 @@ import java.util.List;
"If alarm was not created, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains one of those properties 'isNewAlarm/isExistingAlarm'. " + "If alarm was not created, original message is returned. Otherwise new Message returned with type 'ALARM', Alarm object in 'msg' property and 'metadata' will contains one of those properties 'isNewAlarm/isExistingAlarm'. " +
"Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. " + "Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. " +
"Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.", "Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeCreateAlarmConfig", configDirective = "tbActionNodeCreateAlarmConfig",
icon = "notifications_active" icon = "notifications_active"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java

@ -61,7 +61,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
"Useful in GPS tracking use cases where relation acts as a temporary indicator of a tracker presence in specific geofence.</li>" + "Useful in GPS tracking use cases where relation acts as a temporary indicator of a tracker presence in specific geofence.</li>" +
"<li><strong>Change originator to target entity</strong> - useful when you need to process submitted message as a message from target entity.</li></ul>" + "<li><strong>Change originator to target entity</strong> - useful when you need to process submitted message as a message from target entity.</li></ul>" +
"Output connections: <code>Success</code> - if the relation already exists or successfully created, otherwise <code>Failure</code>.", "Output connections: <code>Success</code> - if the relation already exists or successfully created, otherwise <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeCreateRelationConfig", configDirective = "tbActionNodeCreateRelationConfig",
icon = "add_circle", icon = "add_circle",
version = 1 version = 1

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java

@ -53,7 +53,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
"<li><strong>User</strong> - use a user with the specified email as the target entity to delete relation with.</li>" + "<li><strong>User</strong> - use a user with the specified email as the target entity to delete relation with.</li>" +
"<li><strong>Edge</strong> - use an edge with the specified name as the target entity to delete relation with.</li></ul>" + "<li><strong>Edge</strong> - use an edge with the specified name as the target entity to delete relation with.</li></ul>" +
"Output connections: <code>Success</code> - If the relation(s) successfully deleted, otherwise <code>Failure</code>.", "Output connections: <code>Success</code> - If the relation(s) successfully deleted, otherwise <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeDeleteRelationConfig", configDirective = "tbActionNodeDeleteRelationConfig",
icon = "remove_circle", icon = "remove_circle",
version = 1 version = 1

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeviceStateNode.java

@ -58,7 +58,6 @@ import java.util.Set;
"This node is particularly useful when device isn't using transports to receive data, such as when fetching data from external API or computing new data within the rule chain.", "This node is particularly useful when device isn't using transports to receive data, such as when fetching data from external API or computing new data within the rule chain.",
configClazz = TbDeviceStateNodeConfiguration.class, configClazz = TbDeviceStateNodeConfiguration.class,
relationTypes = {TbNodeConnectionType.SUCCESS, TbNodeConnectionType.FAILURE, "Rate limited"}, relationTypes = {TbNodeConnectionType.SUCCESS, TbNodeConnectionType.FAILURE, "Rate limited"},
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeDeviceStateConfig" configDirective = "tbActionNodeDeviceStateConfig"
) )
public class TbDeviceStateNode implements TbNode { public class TbDeviceStateNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java

@ -43,7 +43,6 @@ import java.util.Objects;
nodeDetails = "Transform incoming Message with configured JS function to String and log final value into Thingsboard log file. " + nodeDetails = "Transform incoming Message with configured JS function to String and log final value into Thingsboard log file. " +
"Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. " + "Message payload can be accessed via <code>msg</code> property. For example <code>'temperature = ' + msg.temperature ;</code>. " +
"Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.", "Message metadata can be accessed via <code>metadata</code> property. For example <code>'name = ' + metadata.customerName;</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeLogConfig", configDirective = "tbActionNodeLogConfig",
icon = "menu" icon = "menu"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbMsgCountNode.java

@ -42,7 +42,6 @@ import java.util.concurrent.atomic.AtomicLong;
nodeDescription = "Count incoming messages", nodeDescription = "Count incoming messages",
nodeDetails = "Count incoming messages for specified interval and produces POST_TELEMETRY_REQUEST msg with messages count", nodeDetails = "Count incoming messages for specified interval and produces POST_TELEMETRY_REQUEST msg with messages count",
icon = "functions", icon = "functions",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeMsgCountConfig" configDirective = "tbActionNodeMsgCountConfig"
) )
public class TbMsgCountNode implements TbNode { public class TbMsgCountNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java

@ -69,7 +69,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
"<b>Note:</b>If the mapping key is <b>$entity_id</b>, that is identified by the Message Originator, then to the appropriate column name(mapping value) will be write the message originator id.<br><br>" + "<b>Note:</b>If the mapping key is <b>$entity_id</b>, that is identified by the Message Originator, then to the appropriate column name(mapping value) will be write the message originator id.<br><br>" +
"If specified message field does not exist or is not a JSON Primitive, the outbound message will be routed via <b>failure</b> chain," + "If specified message field does not exist or is not a JSON Primitive, the outbound message will be routed via <b>failure</b> chain," +
" otherwise, the message will be routed via <b>success</b> chain.", " otherwise, the message will be routed via <b>success</b> chain.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeCustomTableConfig", configDirective = "tbActionNodeCustomTableConfig",
icon = "file_upload", icon = "file_upload",
ruleChainTypes = RuleChainType.CORE) ruleChainTypes = RuleChainType.CORE)

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbUnassignFromCustomerNode.java

@ -42,7 +42,6 @@ import org.thingsboard.server.common.msg.TbMsg;
"If the incoming message originator is a dashboard, will try to search for the customer by title specified in the configuration. " + "If the incoming message originator is a dashboard, will try to search for the customer by title specified in the configuration. " +
"If customer doesn't exist, the exception will be thrown. Otherwise will unassign the dashboard from retrieved customer.<br><br>" + "If customer doesn't exist, the exception will be thrown. Otherwise will unassign the dashboard from retrieved customer.<br><br>" +
"Other entities can be assigned only to one customer, so specified customer title in the configuration will be ignored if the originator isn't a dashboard.", "Other entities can be assigned only to one customer, so specified customer title in the configuration will be ignored if the originator isn't a dashboard.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeUnAssignToCustomerConfig", configDirective = "tbActionNodeUnAssignToCustomerConfig",
icon = "remove_circle", icon = "remove_circle",
version = 1 version = 1

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/lambda/TbAwsLambdaNode.java

@ -53,7 +53,6 @@ import static org.thingsboard.server.dao.service.ConstraintValidator.validateFie
"It sends messages using a RequestResponse invocation type. " + "It sends messages using a RequestResponse invocation type. " +
"The node uses a pre-configured client and specified function to run.<br><br>" + "The node uses a pre-configured client and specified function to run.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeLambdaConfig", configDirective = "tbExternalNodeLambdaConfig",
iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+" iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sns/TbSnsNode.java

@ -46,7 +46,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
nodeDetails = "Will publish message payload to the AWS SNS topic. Outbound message will contain response fields " + nodeDetails = "Will publish message payload to the AWS SNS topic. Outbound message will contain response fields " +
"(<code>messageId</code>, <code>requestId</code>) in the Message Metadata from the AWS SNS. " + "(<code>messageId</code>, <code>requestId</code>) in the Message Metadata from the AWS SNS. " +
"For example <b>requestId</b> field can be accessed with <code>metadata.requestId</code>.", "For example <b>requestId</b> field can be accessed with <code>metadata.requestId</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeSnsConfig", configDirective = "tbExternalNodeSnsConfig",
iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+" iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/aws/sqs/TbSqsNode.java

@ -52,7 +52,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
"response fields (<code>messageId</code>, <code>requestId</code>, <code>messageBodyMd5</code>, <code>messageAttributesMd5</code>" + "response fields (<code>messageId</code>, <code>requestId</code>, <code>messageBodyMd5</code>, <code>messageAttributesMd5</code>" +
", <code>sequenceNumber</code>) in the Message Metadata from the AWS SQS." + ", <code>sequenceNumber</code>) in the Message Metadata from the AWS SQS." +
" For example <b>requestId</b> field can be accessed with <code>metadata.requestId</code>.", " For example <b>requestId</b> field can be accessed with <code>metadata.requestId</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeSqsConfig", configDirective = "tbExternalNodeSqsConfig",
iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+" iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjQ4IiBoZWlnaHQ9IjQ4Ij48cGF0aCBkPSJNMTMuMjMgMTAuNTZWMTBjLTEuOTQgMC0zLjk5LjM5LTMuOTkgMi42NyAwIDEuMTYuNjEgMS45NSAxLjYzIDEuOTUuNzYgMCAxLjQzLS40NyAxLjg2LTEuMjIuNTItLjkzLjUtMS44LjUtMi44NG0yLjcgNi41M2MtLjE4LjE2LS40My4xNy0uNjMuMDYtLjg5LS43NC0xLjA1LTEuMDgtMS41NC0xLjc5LTEuNDcgMS41LTIuNTEgMS45NS00LjQyIDEuOTUtMi4yNSAwLTQuMDEtMS4zOS00LjAxLTQuMTcgMC0yLjE4IDEuMTctMy42NCAyLjg2LTQuMzggMS40Ni0uNjQgMy40OS0uNzYgNS4wNC0uOTNWNy41YzAtLjY2LjA1LTEuNDEtLjMzLTEuOTYtLjMyLS40OS0uOTUtLjctMS41LS43LTEuMDIgMC0xLjkzLjUzLTIuMTUgMS42MS0uMDUuMjQtLjI1LjQ4LS40Ny40OWwtMi42LS4yOGMtLjIyLS4wNS0uNDYtLjIyLS40LS41Ni42LTMuMTUgMy40NS00LjEgNi00LjEgMS4zIDAgMyAuMzUgNC4wMyAxLjMzQzE3LjExIDQuNTUgMTcgNi4xOCAxNyA3Ljk1djQuMTdjMCAxLjI1LjUgMS44MSAxIDIuNDguMTcuMjUuMjEuNTQgMCAuNzFsLTIuMDYgMS43OGgtLjAxIj48L3BhdGg+PHBhdGggZD0iTTIwLjE2IDE5LjU0QzE4IDIxLjE0IDE0LjgyIDIyIDEyLjEgMjJjLTMuODEgMC03LjI1LTEuNDEtOS44NS0zLjc2LS4yLS4xOC0uMDItLjQzLjI1LS4yOSAyLjc4IDEuNjMgNi4yNSAyLjYxIDkuODMgMi42MSAyLjQxIDAgNS4wNy0uNSA3LjUxLTEuNTMuMzctLjE2LjY2LjI0LjMyLjUxIj48L3BhdGg+PHBhdGggZD0iTTIxLjA3IDE4LjVjLS4yOC0uMzYtMS44NS0uMTctMi41Ny0uMDgtLjE5LjAyLS4yMi0uMTYtLjAzLS4zIDEuMjQtLjg4IDMuMjktLjYyIDMuNTMtLjMzLjI0LjMtLjA3IDIuMzUtMS4yNCAzLjMyLS4xOC4xNi0uMzUuMDctLjI2LS4xMS4yNi0uNjcuODUtMi4xNC41Ny0yLjV6Ij48L3BhdGg+PC9zdmc+"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/debug/TbMsgGeneratorNode.java

@ -62,7 +62,6 @@ import static org.thingsboard.server.common.data.DataConstants.QUEUE_NAME;
nodeDescription = "Periodically generates messages", nodeDescription = "Periodically generates messages",
nodeDetails = "Generates messages with configurable period. Javascript function used for message generation.", nodeDetails = "Generates messages with configurable period. Javascript function used for message generation.",
inEnabled = false, inEnabled = false,
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeGeneratorConfig", configDirective = "tbActionNodeGeneratorConfig",
icon = "repeat" icon = "repeat"
) )

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/deduplication/TbMsgDeduplicationNode.java

@ -59,8 +59,7 @@ import static org.thingsboard.server.common.data.DataConstants.QUEUE_NAME;
"<li><strong>ALL</strong> - return all messages as a single JSON array message. " + "<li><strong>ALL</strong> - return all messages as a single JSON array message. " +
"Where each element represents object with <strong><i>msg</i></strong> and <strong><i>metadata</i></strong> inner properties.</li></ul>", "Where each element represents object with <strong><i>msg</i></strong> and <strong><i>metadata</i></strong> inner properties.</li></ul>",
icon = "content_copy", icon = "content_copy",
uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbTransformationNodeDeduplicationConfig"
configDirective = "tbActionNodeMsgDeduplicationConfig"
) )
@Slf4j @Slf4j
public class TbMsgDeduplicationNode implements TbNode { public class TbMsgDeduplicationNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/delay/TbMsgDelayNode.java

@ -45,7 +45,6 @@ import java.util.concurrent.TimeUnit;
"Deprecated because the acknowledged message still stays in memory (to be delayed) and this " + "Deprecated because the acknowledged message still stays in memory (to be delayed) and this " +
"does not guarantee that message will be processed even if the \"retry failures and timeouts\" processing strategy will be chosen.", "does not guarantee that message will be processed even if the \"retry failures and timeouts\" processing strategy will be chosen.",
icon = "pause", icon = "pause",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeMsgDelayConfig" configDirective = "tbActionNodeMsgDelayConfig"
) )
public class TbMsgDelayNode implements TbNode { public class TbMsgDelayNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToCloudNode.java

@ -46,7 +46,6 @@ import java.util.UUID;
"<br><code>ALARM</code><br><br>" + "<br><code>ALARM</code><br><br>" +
"Message will be routed via <b>Failure</b> route if node was not able to save cloud event to database or unsupported originator type/message type arrived. " + "Message will be routed via <b>Failure</b> route if node was not able to save cloud event to database or unsupported originator type/message type arrived. " +
"In case successful storage cloud event to database message will be routed via <b>Success</b> route.", "In case successful storage cloud event to database message will be routed via <b>Success</b> route.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodePushToCloudConfig", configDirective = "tbActionNodePushToCloudConfig",
icon = "cloud_upload", icon = "cloud_upload",
ruleChainTypes = RuleChainType.EDGE ruleChainTypes = RuleChainType.EDGE

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/edge/TbMsgPushToEdgeNode.java

@ -61,7 +61,6 @@ import static org.thingsboard.server.dao.edge.BaseRelatedEdgesService.RELATED_ED
"<br><code>ALARM</code><br><br>" + "<br><code>ALARM</code><br><br>" +
"Message will be routed via <b>Failure</b> route if node was not able to save edge event to database or unsupported message type arrived. " + "Message will be routed via <b>Failure</b> route if node was not able to save edge event to database or unsupported message type arrived. " +
"In case successful storage edge event to database message will be routed via <b>Success</b> route.", "In case successful storage edge event to database message will be routed via <b>Success</b> route.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodePushToEdgeConfig", configDirective = "tbActionNodePushToEdgeConfig",
icon = "cloud_download", icon = "cloud_download",
ruleChainTypes = RuleChainType.CORE ruleChainTypes = RuleChainType.CORE

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbAssetTypeSwitchNode.java

@ -36,7 +36,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
nodeDescription = "Route incoming messages based on the name of the asset profile", nodeDescription = "Route incoming messages based on the name of the asset profile",
nodeDetails = "Route incoming messages based on the name of the asset profile. The asset profile name is case-sensitive.<br><br>" + nodeDetails = "Route incoming messages based on the name of the asset profile. The asset profile name is case-sensitive.<br><br>" +
"Output connections: <i>Asset profile name</i> or <code>Failure</code>", "Output connections: <i>Asset profile name</i> or <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbNodeEmptyConfig") configDirective = "tbNodeEmptyConfig")
public class TbAssetTypeSwitchNode extends TbAbstractTypeSwitchNode { public class TbAssetTypeSwitchNode extends TbAbstractTypeSwitchNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckAlarmStatusNode.java

@ -43,7 +43,6 @@ import java.util.Objects;
nodeDescription = "Checks alarm status.", nodeDescription = "Checks alarm status.",
nodeDetails = "Checks the alarm status to match one of the specified statuses.<br><br>" + nodeDetails = "Checks the alarm status to match one of the specified statuses.<br><br>" +
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>.", "Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeCheckAlarmStatusConfig") configDirective = "tbFilterNodeCheckAlarmStatusConfig")
public class TbCheckAlarmStatusNode implements TbNode { public class TbCheckAlarmStatusNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckMessageNode.java

@ -40,7 +40,6 @@ import java.util.Map;
nodeDetails = "By default, the rule node checks that all specified fields are present. " + nodeDetails = "By default, the rule node checks that all specified fields are present. " +
"Uncheck the 'Check that all selected fields are present' if the presence of at least one field is sufficient.<br><br>" + "Uncheck the 'Check that all selected fields are present' if the presence of at least one field is sufficient.<br><br>" +
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>", "Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeCheckMessageConfig") configDirective = "tbFilterNodeCheckMessageConfig")
public class TbCheckMessageNode implements TbNode { public class TbCheckMessageNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbCheckRelationNode.java

@ -56,7 +56,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
"Otherwise, the rule node checks the presence of a relation to any entity. " + "Otherwise, the rule node checks the presence of a relation to any entity. " +
"In both cases, relation lookup is based on configured direction and type.<br><br>" + "In both cases, relation lookup is based on configured direction and type.<br><br>" +
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>", "Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeCheckRelationConfig") configDirective = "tbFilterNodeCheckRelationConfig")
public class TbCheckRelationNode implements TbNode { public class TbCheckRelationNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbDeviceTypeSwitchNode.java

@ -36,7 +36,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
nodeDescription = "Route incoming messages based on the name of the device profile", nodeDescription = "Route incoming messages based on the name of the device profile",
nodeDetails = "Route incoming messages based on the name of the device profile. The device profile name is case-sensitive<br><br>" + nodeDetails = "Route incoming messages based on the name of the device profile. The device profile name is case-sensitive<br><br>" +
"Output connections: <i>Device profile name</i> or <code>Failure</code>", "Output connections: <i>Device profile name</i> or <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbNodeEmptyConfig") configDirective = "tbNodeEmptyConfig")
public class TbDeviceTypeSwitchNode extends TbAbstractTypeSwitchNode { public class TbDeviceTypeSwitchNode extends TbAbstractTypeSwitchNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsFilterNode.java

@ -44,7 +44,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
"Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code><br/>" + "Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code><br/>" +
"Message type can be accessed via <code>msgType</code> property.<br><br>" + "Message type can be accessed via <code>msgType</code> property.<br><br>" +
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>", "Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeScriptConfig" configDirective = "tbFilterNodeScriptConfig"
) )
public class TbJsFilterNode implements TbNode { public class TbJsFilterNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbJsSwitchNode.java

@ -46,7 +46,6 @@ import java.util.Set;
"Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code><br/>" + "Message metadata can be accessed via <code>metadata</code> property. For example <code>metadata.customerName === 'John';</code><br/>" +
"Message type can be accessed via <code>msgType</code> property.<br><br>" + "Message type can be accessed via <code>msgType</code> property.<br><br>" +
"Output connections: <i>Custom connection(s) defined by switch node</i> or <code>Failure</code>", "Output connections: <i>Custom connection(s) defined by switch node</i> or <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeSwitchConfig") configDirective = "tbFilterNodeSwitchConfig")
public class TbJsSwitchNode implements TbNode { public class TbJsSwitchNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeFilterNode.java

@ -38,7 +38,6 @@ import org.thingsboard.server.common.msg.TbMsg;
nodeDescription = "Filter incoming messages by Message Type", nodeDescription = "Filter incoming messages by Message Type",
nodeDetails = "If incoming message type is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.<br><br>" + nodeDetails = "If incoming message type is expected - send Message via <b>True</b> chain, otherwise <b>False</b> chain is used.<br><br>" +
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>", "Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeMessageTypeConfig") configDirective = "tbFilterNodeMessageTypeConfig")
public class TbMsgTypeFilterNode implements TbNode { public class TbMsgTypeFilterNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java

@ -36,7 +36,6 @@ import org.thingsboard.server.common.msg.TbMsg;
nodeDetails = "Sends messages with message types <b>\"Post attributes\", \"Post telemetry\", \"RPC Request\"</b>" + nodeDetails = "Sends messages with message types <b>\"Post attributes\", \"Post telemetry\", \"RPC Request\"</b>" +
" etc. via corresponding chain, otherwise <b>Other</b> chain is used.<br><br>" + " etc. via corresponding chain, otherwise <b>Other</b> chain is used.<br><br>" +
"Output connections: <i>Message type connection</i>, <code>Other</code> - if message type is custom or <code>Failure</code>", "Output connections: <i>Message type connection</i>, <code>Other</code> - if message type is custom or <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbNodeEmptyConfig") configDirective = "tbNodeEmptyConfig")
public class TbMsgTypeSwitchNode implements TbNode { public class TbMsgTypeSwitchNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeFilterNode.java

@ -36,7 +36,6 @@ import org.thingsboard.server.common.msg.TbMsg;
nodeDescription = "Filter incoming messages by the type of message originator entity", nodeDescription = "Filter incoming messages by the type of message originator entity",
nodeDetails = "Checks that the entity type of the incoming message originator matches one of the values specified in the filter.<br><br>" + nodeDetails = "Checks that the entity type of the incoming message originator matches one of the values specified in the filter.<br><br>" +
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>", "Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeOriginatorTypeConfig") configDirective = "tbFilterNodeOriginatorTypeConfig")
public class TbOriginatorTypeFilterNode implements TbNode { public class TbOriginatorTypeFilterNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbOriginatorTypeSwitchNode.java

@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
nodeDescription = "Route incoming messages by Message Originator Type", nodeDescription = "Route incoming messages by Message Originator Type",
nodeDetails = "Routes messages to chain according to the entity type ('Device', 'Asset', etc.).<br><br>" + nodeDetails = "Routes messages to chain according to the entity type ('Device', 'Asset', etc.).<br><br>" +
"Output connections: <i>Message originator type</i> or <code>Failure</code>", "Output connections: <i>Message originator type</i> or <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbNodeEmptyConfig") configDirective = "tbNodeEmptyConfig")
public class TbOriginatorTypeSwitchNode extends TbAbstractTypeSwitchNode { public class TbOriginatorTypeSwitchNode extends TbAbstractTypeSwitchNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbAckNode.java

@ -33,7 +33,6 @@ import org.thingsboard.server.common.msg.TbMsg;
configClazz = EmptyNodeConfiguration.class, configClazz = EmptyNodeConfiguration.class,
nodeDescription = "Acknowledges the incoming message", nodeDescription = "Acknowledges the incoming message",
nodeDetails = "After acknowledgement, the message is pushed to related rule nodes. Useful if you don't care what happens to this message next.", nodeDetails = "After acknowledgement, the message is pushed to related rule nodes. Useful if you don't care what happens to this message next.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbNodeEmptyConfig" configDirective = "tbNodeEmptyConfig"
) )
public class TbAckNode implements TbNode { public class TbAckNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbCheckpointNode.java

@ -40,7 +40,6 @@ import static org.thingsboard.server.common.data.DataConstants.QUEUE_NAME;
hasQueueName = true, hasQueueName = true,
nodeDescription = "transfers the message to another queue", nodeDescription = "transfers the message to another queue",
nodeDetails = "After successful transfer incoming message is automatically acknowledged. Queue name is configurable.", nodeDetails = "After successful transfer incoming message is automatically acknowledged. Queue name is configurable.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbNodeEmptyConfig" configDirective = "tbNodeEmptyConfig"
) )
public class TbCheckpointNode implements TbNode { public class TbCheckpointNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainInputNode.java

@ -46,7 +46,6 @@ import java.util.UUID;
"then target rule chain might be resolved dynamically based on incoming message originator. " + "then target rule chain might be resolved dynamically based on incoming message originator. " +
"In this case rule chain specified in the configuration will be used as fallback rule chain.<br><br>" + "In this case rule chain specified in the configuration will be used as fallback rule chain.<br><br>" +
"Output connections: <i>Any connection(s) produced by output node(s) in the target rule chain.</i>", "Output connections: <i>Any connection(s) produced by output node(s) in the target rule chain.</i>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFlowNodeRuleChainInputConfig", configDirective = "tbFlowNodeRuleChainInputConfig",
relationTypes = {}, relationTypes = {},
ruleChainNode = true, ruleChainNode = true,

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/flow/TbRuleChainOutputNode.java

@ -34,7 +34,6 @@ import org.thingsboard.server.common.msg.TbMsg;
nodeDetails = "Produces output of the rule chain processing. " + nodeDetails = "Produces output of the rule chain processing. " +
"The output is forwarded to the caller rule chain, as an output of the corresponding \"input\" rule node. " + "The output is forwarded to the caller rule chain, as an output of the corresponding \"input\" rule node. " +
"The output rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain. ", "The output rule node name corresponds to the relation type of the output message, and it is used to forward messages to other rule nodes in the caller rule chain. ",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFlowNodeRuleChainOutputConfig", configDirective = "tbFlowNodeRuleChainOutputConfig",
outEnabled = false outEnabled = false
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/gcp/pubsub/TbPubSubNode.java

@ -52,7 +52,6 @@ import java.util.concurrent.TimeUnit;
nodeDetails = "Will publish message payload to the Google Cloud Platform PubSub topic. Outbound message will contain response fields " + nodeDetails = "Will publish message payload to the Google Cloud Platform PubSub topic. Outbound message will contain response fields " +
"(<code>messageId</code> in the Message Metadata from the GCP PubSub. " + "(<code>messageId</code> in the Message Metadata from the GCP PubSub. " +
"<b>messageId</b> field can be accessed with <code>metadata.messageId</code>.", "<b>messageId</b> field can be accessed with <code>metadata.messageId</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodePubSubConfig", configDirective = "tbExternalNodePubSubConfig",
iconUrl = "data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+Cjx0aXRsZT5DbG91ZCBQdWJTdWI8L3RpdGxlPgo8Zz4KPHBhdGggZD0iTTEyNi40Nyw1OC4xMmwtMjYuMy00NS43NEExMS41NiwxMS41NiwwLDAsMCw5MC4zMSw2LjVIMzcuN2ExMS41NSwxMS41NSwwLDAsMC05Ljg2LDUuODhMMS41Myw1OGExMS40OCwxMS40OCwwLDAsMCwwLDExLjQ0bDI2LjMsNDZhMTEuNzcsMTEuNzcsMCwwLDAsOS44Niw2LjA5SDkwLjNhMTEuNzMsMTEuNzMsMCwwLDAsOS44Ny02LjA2bDI2LjMtNDUuNzRBMTEuNzMsMTEuNzMsMCwwLDAsMTI2LjQ3LDU4LjEyWiIgc3R5bGU9ImZpbGw6ICM3MzViMmYiLz4KPHBhdGggZD0iTTg5LjIyLDQ3Ljc0LDgzLjM2LDQ5bC0xNC42LTE0LjZMNjQuMDksNDMuMSw2MS41NSw1My4ybDQuMjksNC4yOUw1Ny42LDU5LjE4LDQ2LjMsNDcuODhsLTcuNjcsNy4zOEw1Mi43Niw2OS4zN2wtMTUsMTEuOUw3OCwxMjEuNUg5MC4zYTExLjczLDExLjczLDAsMCwwLDkuODctNi4wNmwyMC43Mi0zNloiIHN0eWxlPSJvcGFjaXR5OiAwLjA3MDAwMDAwMDI5ODAyMztpc29sYXRpb246IGlzb2xhdGUiLz4KPHBhdGggZD0iTTgyLjg2LDQ3YTUuMzIsNS4zMiwwLDEsMS0xLjk1LDcuMjdBNS4zMiw1LjMyLDAsMCwxLDgyLjg2LDQ3IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNMzkuODIsNTYuMThhNS4zMiw1LjMyLDAsMSwxLDcuMjctMS45NSw1LjMyLDUuMzIsMCwwLDEtNy4yNywxLjk1IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNjkuMzIsODguODVBNS4zMiw1LjMyLDAsMSwxLDY0LDgzLjUyYTUuMzIsNS4zMiwwLDAsMSw1LjMyLDUuMzIiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxnPgo8cGF0aCBkPSJNNjQsNTIuOTRhMTEuMDYsMTEuMDYsMCwwLDEsMi40Ni4yOFYzOS4xNUg2MS41NFY1My4yMkExMS4wNiwxMS4wNiwwLDAsMSw2NCw1Mi45NFoiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik03NC41Nyw2Ny4yNmExMSwxMSwwLDAsMS0yLjQ3LDQuMjVsMTIuMTksNywyLjQ2LTQuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNTMuNDMsNjcuMjZsLTEyLjE4LDcsMi40Niw0LjI2LDEyLjE5LTdBMTEsMTEsMCwwLDEsNTMuNDMsNjcuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi42LDY0QTguNiw4LjYsMCwxLDEsNjQsNTUuNCw4LjYsOC42LDAsMCwxLDcyLjYsNjQiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik0zOS4xLDcwLjU3YTYuNzYsNi43NiwwLDEsMS0yLjQ3LDkuMjMsNi43Niw2Ljc2LDAsMCwxLDIuNDctOS4yMyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTgyLjE0LDgyLjI3YTYuNzYsNi43NiwwLDEsMSw5LjIzLTIuNDcsNi43NSw2Ljc1LDAsMCwxLTkuMjMsMi40NyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTcwLjc2LDM5LjE1QTYuNzYsNi43NiwwLDEsMSw2NCwzMi4zOWE2Ljc2LDYuNzYsMCwwLDEsNi43Niw2Ljc2IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+Cjwvc3ZnPgo=" iconUrl = "data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB2aWV3Qm94PSIwIDAgMTI4IDEyOCI+Cjx0aXRsZT5DbG91ZCBQdWJTdWI8L3RpdGxlPgo8Zz4KPHBhdGggZD0iTTEyNi40Nyw1OC4xMmwtMjYuMy00NS43NEExMS41NiwxMS41NiwwLDAsMCw5MC4zMSw2LjVIMzcuN2ExMS41NSwxMS41NSwwLDAsMC05Ljg2LDUuODhMMS41Myw1OGExMS40OCwxMS40OCwwLDAsMCwwLDExLjQ0bDI2LjMsNDZhMTEuNzcsMTEuNzcsMCwwLDAsOS44Niw2LjA5SDkwLjNhMTEuNzMsMTEuNzMsMCwwLDAsOS44Ny02LjA2bDI2LjMtNDUuNzRBMTEuNzMsMTEuNzMsMCwwLDAsMTI2LjQ3LDU4LjEyWiIgc3R5bGU9ImZpbGw6ICM3MzViMmYiLz4KPHBhdGggZD0iTTg5LjIyLDQ3Ljc0LDgzLjM2LDQ5bC0xNC42LTE0LjZMNjQuMDksNDMuMSw2MS41NSw1My4ybDQuMjksNC4yOUw1Ny42LDU5LjE4LDQ2LjMsNDcuODhsLTcuNjcsNy4zOEw1Mi43Niw2OS4zN2wtMTUsMTEuOUw3OCwxMjEuNUg5MC4zYTExLjczLDExLjczLDAsMCwwLDkuODctNi4wNmwyMC43Mi0zNloiIHN0eWxlPSJvcGFjaXR5OiAwLjA3MDAwMDAwMDI5ODAyMztpc29sYXRpb246IGlzb2xhdGUiLz4KPHBhdGggZD0iTTgyLjg2LDQ3YTUuMzIsNS4zMiwwLDEsMS0xLjk1LDcuMjdBNS4zMiw1LjMyLDAsMCwxLDgyLjg2LDQ3IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNMzkuODIsNTYuMThhNS4zMiw1LjMyLDAsMSwxLDcuMjctMS45NSw1LjMyLDUuMzIsMCwwLDEtNy4yNywxLjk1IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNjkuMzIsODguODVBNS4zMiw1LjMyLDAsMSwxLDY0LDgzLjUyYTUuMzIsNS4zMiwwLDAsMSw1LjMyLDUuMzIiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxnPgo8cGF0aCBkPSJNNjQsNTIuOTRhMTEuMDYsMTEuMDYsMCwwLDEsMi40Ni4yOFYzOS4xNUg2MS41NFY1My4yMkExMS4wNiwxMS4wNiwwLDAsMSw2NCw1Mi45NFoiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik03NC41Nyw2Ny4yNmExMSwxMSwwLDAsMS0yLjQ3LDQuMjVsMTIuMTksNywyLjQ2LTQuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8cGF0aCBkPSJNNTMuNDMsNjcuMjZsLTEyLjE4LDcsMi40Niw0LjI2LDEyLjE5LTdBMTEsMTEsMCwwLDEsNTMuNDMsNjcuMjZaIiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi42LDY0QTguNiw4LjYsMCwxLDEsNjQsNTUuNCw4LjYsOC42LDAsMCwxLDcyLjYsNjQiIHN0eWxlPSJmaWxsOiAjZmZmIi8+CjxwYXRoIGQ9Ik0zOS4xLDcwLjU3YTYuNzYsNi43NiwwLDEsMS0yLjQ3LDkuMjMsNi43Niw2Ljc2LDAsMCwxLDIuNDctOS4yMyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTgyLjE0LDgyLjI3YTYuNzYsNi43NiwwLDEsMSw5LjIzLTIuNDcsNi43NSw2Ljc1LDAsMCwxLTkuMjMsMi40NyIgc3R5bGU9ImZpbGw6ICNmZmYiLz4KPHBhdGggZD0iTTcwLjc2LDM5LjE1QTYuNzYsNi43NiwwLDEsMSw2NCwzMi4zOWE2Ljc2LDYuNzYsMCwwLDEsNi43Niw2Ljc2IiBzdHlsZT0iZmlsbDogI2ZmZiIvPgo8L2c+Cjwvc3ZnPgo="
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingActionNode.java

@ -66,7 +66,6 @@ import static org.thingsboard.rule.engine.util.GpsGeofencingEvents.OUTSIDE;
"If the presence monitoring strategy <b>\"On each message\"</b> is selected, sends messages via rule node connection type <code>Inside</code> or <code>Outside</code> every time the geofencing condition is satisfied. " + "If the presence monitoring strategy <b>\"On each message\"</b> is selected, sends messages via rule node connection type <code>Inside</code> or <code>Outside</code> every time the geofencing condition is satisfied. " +
"<br><br>" + "<br><br>" +
"Output connections: <code>Entered</code>, <code>Left</code>, <code>Inside</code>, <code>Outside</code>, <code>Success</code>", "Output connections: <code>Entered</code>, <code>Left</code>, <code>Inside</code>, <code>Outside</code>, <code>Success</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeGpsGeofencingConfig" configDirective = "tbActionNodeGpsGeofencingConfig"
) )
public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofencingActionNodeConfiguration> { public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofencingActionNodeConfiguration> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/geo/TbGpsGeofencingFilterNode.java

@ -60,7 +60,6 @@ import org.thingsboard.server.common.msg.TbMsg;
"</br></br>" + "</br></br>" +
"Available radius units: METER, KILOMETER, FOOT, MILE, NAUTICAL_MILE;<br><br>" + "Available radius units: METER, KILOMETER, FOOT, MILE, NAUTICAL_MILE;<br><br>" +
"Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>", "Output connections: <code>True</code>, <code>False</code>, <code>Failure</code>",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbFilterNodeGpsGeofencingConfig") configDirective = "tbFilterNodeGpsGeofencingConfig")
public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode<TbGpsGeofencingFilterNodeConfiguration> { public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode<TbGpsGeofencingFilterNodeConfiguration> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/kafka/TbKafkaNode.java

@ -52,7 +52,6 @@ import java.util.Properties;
nodeDetails = "Will send record via Kafka producer to Kafka server. " + nodeDetails = "Will send record via Kafka producer to Kafka server. " +
"Outbound message will contain response fields (<code>offset</code>, <code>partition</code>, <code>topic</code>)" + "Outbound message will contain response fields (<code>offset</code>, <code>partition</code>, <code>topic</code>)" +
" from the Kafka in the Message Metadata. For example <b>partition</b> field can be accessed with <code>metadata.partition</code>.", " from the Kafka in the Message Metadata. For example <b>partition</b> field can be accessed with <code>metadata.partition</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeKafkaConfig", configDirective = "tbExternalNodeKafkaConfig",
iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUzOCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDQxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTIwMS44MTYgMjMwLjIxNmMtMTYuMTg2IDAtMzAuNjk3IDcuMTcxLTQwLjYzNCAxOC40NjFsLTI1LjQ2My0xOC4wMjZjMi43MDMtNy40NDIgNC4yNTUtMTUuNDMzIDQuMjU1LTIzLjc5NyAwLTguMjE5LTEuNDk4LTE2LjA3Ni00LjExMi0yMy40MDhsMjUuNDA2LTE3LjgzNWM5LjkzNiAxMS4yMzMgMjQuNDA5IDE4LjM2NSA0MC41NDggMTguMzY1IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI5Ljg3OS0yNC4zMDktNTQuMTg0LTU0LjE4NC01NC4xODQtMjkuODc1IDAtNTQuMTg0IDI0LjMwNS01NC4xODQgNTQuMTg0IDAgNS4zNDguODA4IDEwLjUwNSAyLjI1OCAxNS4zODlsLTI1LjQyMyAxNy44NDRjLTEwLjYyLTEzLjE3NS0yNS45MTEtMjIuMzc0LTQzLjMzMy0yNS4xODJ2LTMwLjY0YzI0LjU0NC01LjE1NSA0My4wMzctMjYuOTYyIDQzLjAzNy01My4wMTlDMTI0LjE3MSAyNC4zMDUgOTkuODYyIDAgNjkuOTg3IDAgNDAuMTEyIDAgMTUuODAzIDI0LjMwNSAxNS44MDMgNTQuMTg0YzAgMjUuNzA4IDE4LjAxNCA0Ny4yNDYgNDIuMDY3IDUyLjc2OXYzMS4wMzhDMjUuMDQ0IDE0My43NTMgMCAxNzIuNDAxIDAgMjA2Ljg1NGMwIDM0LjYyMSAyNS4yOTIgNjMuMzc0IDU4LjM1NSA2OC45NHYzMi43NzRjLTI0LjI5OSA1LjM0MS00Mi41NTIgMjcuMDExLTQyLjU1MiA1Mi44OTQgMCAyOS44NzkgMjQuMzA5IDU0LjE4NCA1NC4xODQgNTQuMTg0IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI1Ljg4My0xOC4yNTMtNDcuNTUzLTQyLjU1Mi01Mi44OTR2LTMyLjc3NWE2OS45NjUgNjkuOTY1IDAgMCAwIDQyLjYtMjQuNzc2bDI1LjYzMyAxOC4xNDNjLTEuNDIzIDQuODQtMi4yMiA5Ljk0Ni0yLjIyIDE1LjI0IDAgMjkuODc5IDI0LjMwOSA1NC4xODQgNTQuMTg0IDU0LjE4NCAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yOS44NzktMjQuMzA5LTU0LjE4NC01NC4xODQtNTQuMTg0em0wLTEyNi42OTVjMTQuNDg3IDAgMjYuMjcgMTEuNzg4IDI2LjI3IDI2LjI3MXMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3LTI2LjI3LTExLjc4Ny0yNi4yNy0yNi4yN2MwLTE0LjQ4MyAxMS43ODMtMjYuMjcxIDI2LjI3LTI2LjI3MXptLTE1OC4xLTQ5LjMzN2MwLTE0LjQ4MyAxMS43ODQtMjYuMjcgMjYuMjcxLTI2LjI3czI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN2MwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3em01Mi41NDEgMzA3LjI3OGMwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3YzAtMTQuNDgzIDExLjc4NC0yNi4yNyAyNi4yNzEtMjYuMjdzMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3em0tMjYuMjcyLTExNy45N2MtMjAuMjA1IDAtMzYuNjQyLTE2LjQzNC0zNi42NDItMzYuNjM4IDAtMjAuMjA1IDE2LjQzNy0zNi42NDIgMzYuNjQyLTM2LjY0MiAyMC4yMDQgMCAzNi42NDEgMTYuNDM3IDM2LjY0MSAzNi42NDIgMCAyMC4yMDQtMTYuNDM3IDM2LjYzOC0zNi42NDEgMzYuNjM4em0xMzEuODMxIDY3LjE3OWMtMTQuNDg3IDAtMjYuMjctMTEuNzg4LTI2LjI3LTI2LjI3MXMxMS43ODMtMjYuMjcgMjYuMjctMjYuMjcgMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3YzAgMTQuNDgzLTExLjc4MyAyNi4yNzEtMjYuMjcgMjYuMjcxeiIvPjwvc3ZnPg==" iconUrl = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUzOCIgaGVpZ2h0PSIyNTAwIiB2aWV3Qm94PSIwIDAgMjU2IDQxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+PHBhdGggZD0iTTIwMS44MTYgMjMwLjIxNmMtMTYuMTg2IDAtMzAuNjk3IDcuMTcxLTQwLjYzNCAxOC40NjFsLTI1LjQ2My0xOC4wMjZjMi43MDMtNy40NDIgNC4yNTUtMTUuNDMzIDQuMjU1LTIzLjc5NyAwLTguMjE5LTEuNDk4LTE2LjA3Ni00LjExMi0yMy40MDhsMjUuNDA2LTE3LjgzNWM5LjkzNiAxMS4yMzMgMjQuNDA5IDE4LjM2NSA0MC41NDggMTguMzY1IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI5Ljg3OS0yNC4zMDktNTQuMTg0LTU0LjE4NC01NC4xODQtMjkuODc1IDAtNTQuMTg0IDI0LjMwNS01NC4xODQgNTQuMTg0IDAgNS4zNDguODA4IDEwLjUwNSAyLjI1OCAxNS4zODlsLTI1LjQyMyAxNy44NDRjLTEwLjYyLTEzLjE3NS0yNS45MTEtMjIuMzc0LTQzLjMzMy0yNS4xODJ2LTMwLjY0YzI0LjU0NC01LjE1NSA0My4wMzctMjYuOTYyIDQzLjAzNy01My4wMTlDMTI0LjE3MSAyNC4zMDUgOTkuODYyIDAgNjkuOTg3IDAgNDAuMTEyIDAgMTUuODAzIDI0LjMwNSAxNS44MDMgNTQuMTg0YzAgMjUuNzA4IDE4LjAxNCA0Ny4yNDYgNDIuMDY3IDUyLjc2OXYzMS4wMzhDMjUuMDQ0IDE0My43NTMgMCAxNzIuNDAxIDAgMjA2Ljg1NGMwIDM0LjYyMSAyNS4yOTIgNjMuMzc0IDU4LjM1NSA2OC45NHYzMi43NzRjLTI0LjI5OSA1LjM0MS00Mi41NTIgMjcuMDExLTQyLjU1MiA1Mi44OTQgMCAyOS44NzkgMjQuMzA5IDU0LjE4NCA1NC4xODQgNTQuMTg0IDI5Ljg3NSAwIDU0LjE4NC0yNC4zMDUgNTQuMTg0LTU0LjE4NCAwLTI1Ljg4My0xOC4yNTMtNDcuNTUzLTQyLjU1Mi01Mi44OTR2LTMyLjc3NWE2OS45NjUgNjkuOTY1IDAgMCAwIDQyLjYtMjQuNzc2bDI1LjYzMyAxOC4xNDNjLTEuNDIzIDQuODQtMi4yMiA5Ljk0Ni0yLjIyIDE1LjI0IDAgMjkuODc5IDI0LjMwOSA1NC4xODQgNTQuMTg0IDU0LjE4NCAyOS44NzUgMCA1NC4xODQtMjQuMzA1IDU0LjE4NC01NC4xODQgMC0yOS44NzktMjQuMzA5LTU0LjE4NC01NC4xODQtNTQuMTg0em0wLTEyNi42OTVjMTQuNDg3IDAgMjYuMjcgMTEuNzg4IDI2LjI3IDI2LjI3MXMtMTEuNzgzIDI2LjI3LTI2LjI3IDI2LjI3LTI2LjI3LTExLjc4Ny0yNi4yNy0yNi4yN2MwLTE0LjQ4MyAxMS43ODMtMjYuMjcxIDI2LjI3LTI2LjI3MXptLTE1OC4xLTQ5LjMzN2MwLTE0LjQ4MyAxMS43ODQtMjYuMjcgMjYuMjcxLTI2LjI3czI2LjI3IDExLjc4NyAyNi4yNyAyNi4yN2MwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3em01Mi41NDEgMzA3LjI3OGMwIDE0LjQ4My0xMS43ODMgMjYuMjctMjYuMjcgMjYuMjdzLTI2LjI3MS0xMS43ODctMjYuMjcxLTI2LjI3YzAtMTQuNDgzIDExLjc4NC0yNi4yNyAyNi4yNzEtMjYuMjdzMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3em0tMjYuMjcyLTExNy45N2MtMjAuMjA1IDAtMzYuNjQyLTE2LjQzNC0zNi42NDItMzYuNjM4IDAtMjAuMjA1IDE2LjQzNy0zNi42NDIgMzYuNjQyLTM2LjY0MiAyMC4yMDQgMCAzNi42NDEgMTYuNDM3IDM2LjY0MSAzNi42NDIgMCAyMC4yMDQtMTYuNDM3IDM2LjYzOC0zNi42NDEgMzYuNjM4em0xMzEuODMxIDY3LjE3OWMtMTQuNDg3IDAtMjYuMjctMTEuNzg4LTI2LjI3LTI2LjI3MXMxMS43ODMtMjYuMjcgMjYuMjctMjYuMjcgMjYuMjcgMTEuNzg3IDI2LjI3IDI2LjI3YzAgMTQuNDgzLTExLjc4MyAyNi4yNzEtMjYuMjcgMjYuMjcxeiIvPjwvc3ZnPg=="
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbMsgToEmailNode.java

@ -42,7 +42,6 @@ import java.util.Map;
nodeDescription = "Transforms message to email message", nodeDescription = "Transforms message to email message",
nodeDetails = "Transforms message to email message. If transformation completed successfully output message type will be set to <code>SEND_EMAIL</code>.<br><br>" + nodeDetails = "Transforms message to email message. If transformation completed successfully output message type will be set to <code>SEND_EMAIL</code>.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbTransformationNodeToEmailConfig", configDirective = "tbTransformationNodeToEmailConfig",
icon = "email" icon = "email"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java

@ -44,7 +44,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
nodeDetails = "Expects messages with <b>SEND_EMAIL</b> type. Node works only with messages that " + nodeDetails = "Expects messages with <b>SEND_EMAIL</b> type. Node works only with messages that " +
" where created using <code>to Email</code> transformation Node, please connect this Node " + " where created using <code>to Email</code> transformation Node, please connect this Node " +
"with <code>to Email</code> Node using <code>Successful</code> chain.", "with <code>to Email</code> Node using <code>Successful</code> chain.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeSendEmailConfig", configDirective = "tbExternalNodeSendEmailConfig",
icon = "send" icon = "send"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/math/TbMathNode.java

@ -76,7 +76,6 @@ import static org.thingsboard.rule.engine.math.TbMathArgumentType.CONSTANT;
"<br/><br/>" + "<br/><br/>" +
"The execution is synchronized in scope of message originator (e.g. device) and server node. " + "The execution is synchronized in scope of message originator (e.g. device) and server node. " +
"If you have rule nodes in different rule chains, they will process messages from the same originator synchronously in the scope of the server node.", "If you have rule nodes in different rule chains, they will process messages from the same originator synchronously in the scope of the server node.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeMathFunctionConfig", configDirective = "tbActionNodeMathFunctionConfig",
icon = "calculate" icon = "calculate"

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/CalculateDeltaNode.java

@ -53,7 +53,6 @@ import java.util.Map;
"and current value for this key from the incoming message", "and current value for this key from the incoming message",
nodeDetails = "Useful for metering use cases, when you need to calculate consumption based on pulse counter reading.<br><br>" + nodeDetails = "Useful for metering use cases, when you need to calculate consumption based on pulse counter reading.<br><br>" +
"Output connections: <code>Success</code>, <code>Other</code> or <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Other</code> or <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeCalculateDeltaConfig") configDirective = "tbEnrichmentNodeCalculateDeltaConfig")
public class CalculateDeltaNode implements TbNode { public class CalculateDeltaNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbFetchDeviceCredentialsNode.java

@ -45,7 +45,6 @@ import java.util.concurrent.ExecutionException;
"Useful when you need to fetch device credentials and use them for further message processing. " + "Useful when you need to fetch device credentials and use them for further message processing. " +
"For example, use device credentials to interact with external systems.<br><br>" + "For example, use device credentials to interact with external systems.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeFetchDeviceCredentialsConfig") configDirective = "tbEnrichmentNodeFetchDeviceCredentialsConfig")
public class TbFetchDeviceCredentialsNode extends TbAbstractNodeWithFetchTo<TbFetchDeviceCredentialsNodeConfiguration> { public class TbFetchDeviceCredentialsNode extends TbAbstractNodeWithFetchTo<TbFetchDeviceCredentialsNodeConfiguration> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetAttributesNode.java

@ -43,7 +43,6 @@ import org.thingsboard.server.common.msg.TbMsg;
"that are not included in the incoming message to use them for further message processing. " + "that are not included in the incoming message to use them for further message processing. " +
"For example to filter messages based on the threshold value stored in the attributes.<br><br>" + "For example to filter messages based on the threshold value stored in the attributes.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeOriginatorAttributesConfig") configDirective = "tbEnrichmentNodeOriginatorAttributesConfig")
public class TbGetAttributesNode extends TbAbstractGetAttributesNode<TbGetAttributesNodeConfiguration, EntityId> { public class TbGetAttributesNode extends TbAbstractGetAttributesNode<TbGetAttributesNodeConfiguration, EntityId> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerAttributeNode.java

@ -41,7 +41,6 @@ import org.thingsboard.server.common.data.util.TbPair;
"that is stored as customer attributes or telemetry data and used for dynamic message filtering, transformation, " + "that is stored as customer attributes or telemetry data and used for dynamic message filtering, transformation, " +
"or actions such as alarm creation if the threshold is exceeded.<br><br>" + "or actions such as alarm creation if the threshold is exceeded.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeCustomerAttributesConfig") configDirective = "tbEnrichmentNodeCustomerAttributesConfig")
public class TbGetCustomerAttributeNode extends TbAbstractGetEntityDataNode<CustomerId> { public class TbGetCustomerAttributeNode extends TbAbstractGetEntityDataNode<CustomerId> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetCustomerDetailsNode.java

@ -50,7 +50,6 @@ import java.util.NoSuchElementException;
nodeDetails = "Useful in multi-customer solutions where we need dynamically use customer contact information " + nodeDetails = "Useful in multi-customer solutions where we need dynamically use customer contact information " +
"such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.<br><br>" + "such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeEntityDetailsConfig") configDirective = "tbEnrichmentNodeEntityDetailsConfig")
public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode<TbGetCustomerDetailsNodeConfiguration, CustomerId> { public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode<TbGetCustomerDetailsNodeConfiguration, CustomerId> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetDeviceAttrNode.java

@ -42,7 +42,6 @@ import org.thingsboard.server.common.msg.TbMsg;
"Useful when you need to retrieve attributes and/or latest telemetry values from device that has a relation " + "Useful when you need to retrieve attributes and/or latest telemetry values from device that has a relation " +
"to the message originator and use them for further message processing.<br><br>" + "to the message originator and use them for further message processing.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeDeviceAttributesConfig") configDirective = "tbEnrichmentNodeDeviceAttributesConfig")
public class TbGetDeviceAttrNode extends TbAbstractGetAttributesNode<TbGetDeviceAttrNodeConfiguration, DeviceId> { public class TbGetDeviceAttrNode extends TbAbstractGetAttributesNode<TbGetDeviceAttrNodeConfiguration, DeviceId> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetOriginatorFieldsNode.java

@ -43,7 +43,6 @@ import java.util.concurrent.ExecutionException;
nodeDetails = "Fetches fields values specified in the mapping. If specified field is not part of originator fields it will be ignored. " + nodeDetails = "Fetches fields values specified in the mapping. If specified field is not part of originator fields it will be ignored. " +
"Useful when you need to retrieve originator fields and use them for further message processing.<br><br>" + "Useful when you need to retrieve originator fields and use them for further message processing.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeOriginatorFieldsConfig") configDirective = "tbEnrichmentNodeOriginatorFieldsConfig")
public class TbGetOriginatorFieldsNode extends TbAbstractGetMappedDataNode<EntityId, TbGetOriginatorFieldsConfiguration> { public class TbGetOriginatorFieldsNode extends TbAbstractGetMappedDataNode<EntityId, TbGetOriginatorFieldsConfiguration> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetRelatedAttributeNode.java

@ -42,7 +42,6 @@ import java.util.Arrays;
"If multiple related entities are found, only first entity is used for message enrichment, other entities are discarded. " + "If multiple related entities are found, only first entity is used for message enrichment, other entities are discarded. " +
"Useful when you need to retrieve data from an entity that has a relation to the message originator and use them for further message processing.<br><br>" + "Useful when you need to retrieve data from an entity that has a relation to the message originator and use them for further message processing.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeRelatedAttributesConfig") configDirective = "tbEnrichmentNodeRelatedAttributesConfig")
public class TbGetRelatedAttributeNode extends TbAbstractGetEntityDataNode<EntityId> { public class TbGetRelatedAttributeNode extends TbAbstractGetEntityDataNode<EntityId> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTelemetryNode.java

@ -58,7 +58,6 @@ import java.util.stream.Collectors;
"instead of fetching just the latest telemetry or if you need to get the closest telemetry to the fetch interval start or end. " + "instead of fetching just the latest telemetry or if you need to get the closest telemetry to the fetch interval start or end. " +
"Also, this node can be used for telemetry aggregation within configured fetch interval.<br><br>" + "Also, this node can be used for telemetry aggregation within configured fetch interval.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase") configDirective = "tbEnrichmentNodeGetTelemetryFromDatabase")
public class TbGetTelemetryNode implements TbNode { public class TbGetTelemetryNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantAttributeNode.java

@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.util.TbPair;
nodeDetails = "Useful when you need to retrieve some common configuration or threshold set " + nodeDetails = "Useful when you need to retrieve some common configuration or threshold set " +
"that is stored as tenant attributes or telemetry data and use it for further message processing.<br><br>" + "that is stored as tenant attributes or telemetry data and use it for further message processing.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeTenantAttributesConfig") configDirective = "tbEnrichmentNodeTenantAttributesConfig")
public class TbGetTenantAttributeNode extends TbAbstractGetEntityDataNode<TenantId> { public class TbGetTenantAttributeNode extends TbAbstractGetEntityDataNode<TenantId> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbGetTenantDetailsNode.java

@ -39,7 +39,6 @@ import org.thingsboard.server.common.msg.TbMsg;
nodeDetails = "Useful when we need to retrieve contact information from your tenant " + nodeDetails = "Useful when we need to retrieve contact information from your tenant " +
"such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.<br><br>" + "such as email, phone, address, etc., for notifications via email, SMS, and other notification providers.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbEnrichmentNodeEntityDetailsConfig") configDirective = "tbEnrichmentNodeEntityDetailsConfig")
public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode<TbGetTenantDetailsNodeConfiguration, TenantId> { public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode<TbGetTenantDetailsNodeConfiguration, TenantId> {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java

@ -57,7 +57,6 @@ import java.util.concurrent.TimeoutException;
clusteringMode = ComponentClusteringMode.USER_PREFERENCE, clusteringMode = ComponentClusteringMode.USER_PREFERENCE,
nodeDescription = "Publish messages to the MQTT broker", nodeDescription = "Publish messages to the MQTT broker",
nodeDetails = "Will publish message payload to the MQTT broker with QoS <b>AT_LEAST_ONCE</b>.", nodeDetails = "Will publish message payload to the MQTT broker with QoS <b>AT_LEAST_ONCE</b>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeMqttConfig", configDirective = "tbExternalNodeMqttConfig",
icon = "call_split" icon = "call_split"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/azure/TbAzureIotHubNode.java

@ -41,7 +41,6 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
clusteringMode = ComponentClusteringMode.SINGLETON, clusteringMode = ComponentClusteringMode.SINGLETON,
nodeDescription = "Publish messages to the Azure IoT Hub", nodeDescription = "Publish messages to the Azure IoT Hub",
nodeDetails = "Will publish message payload to the Azure IoT Hub with QoS <b>AT_LEAST_ONCE</b>.", nodeDetails = "Will publish message payload to the Azure IoT Hub with QoS <b>AT_LEAST_ONCE</b>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeAzureIotHubConfig" configDirective = "tbExternalNodeAzureIotHubConfig"
) )
public class TbAzureIotHubNode extends TbMqttNode { public class TbAzureIotHubNode extends TbMqttNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbNotificationNode.java

@ -41,7 +41,6 @@ import java.util.concurrent.ExecutionException;
configClazz = TbNotificationNodeConfiguration.class, configClazz = TbNotificationNodeConfiguration.class,
nodeDescription = "Sends notification to targets using the template", nodeDescription = "Sends notification to targets using the template",
nodeDetails = "Will send notification to the specified targets using the template", nodeDetails = "Will send notification to the specified targets using the template",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeNotificationConfig", configDirective = "tbExternalNodeNotificationConfig",
icon = "notifications" icon = "notifications"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/notification/TbSlackNode.java

@ -33,7 +33,6 @@ import java.util.concurrent.ExecutionException;
configClazz = TbSlackNodeConfiguration.class, configClazz = TbSlackNodeConfiguration.class,
nodeDescription = "Send message via Slack", nodeDescription = "Send message via Slack",
nodeDetails = "Sends message to a Slack channel or user", nodeDetails = "Sends message to a Slack channel or user",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeSlackConfig", configDirective = "tbExternalNodeSlackConfig",
iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTYsMTVBMiwyIDAgMCwxIDQsMTdBMiwyIDAgMCwxIDIsMTVBMiwyIDAgMCwxIDQsMTNINlYxNU03LDE1QTIsMiAwIDAsMSA5LDEzQTIsMiAwIDAsMSAxMSwxNVYyMEEyLDIgMCAwLDEgOSwyMkEyLDIgMCAwLDEgNywyMFYxNU05LDdBMiwyIDAgMCwxIDcsNUEyLDIgMCAwLDEgOSwzQTIsMiAwIDAsMSAxMSw1VjdIOU05LDhBMiwyIDAgMCwxIDExLDEwQTIsMiAwIDAsMSA5LDEySDRBMiwyIDAgMCwxIDIsMTBBMiwyIDAgMCwxIDQsOEg5TTE3LDEwQTIsMiAwIDAsMSAxOSw4QTIsMiAwIDAsMSAyMSwxMEEyLDIgMCAwLDEgMTksMTJIMTdWMTBNMTYsMTBBMiwyIDAgMCwxIDE0LDEyQTIsMiAwIDAsMSAxMiwxMFY1QTIsMiAwIDAsMSAxNCwzQTIsMiAwIDAsMSAxNiw1VjEwTTE0LDE4QTIsMiAwIDAsMSAxNiwyMEEyLDIgMCAwLDEgMTQsMjJBMiwyIDAgMCwxIDEyLDIwVjE4SDE0TTE0LDE3QTIsMiAwIDAsMSAxMiwxNUEyLDIgMCAwLDEgMTQsMTNIMTlBMiwyIDAgMCwxIDIxLDE1QTIsMiAwIDAsMSAxOSwxN0gxNFoiIC8+PC9zdmc+" iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTYsMTVBMiwyIDAgMCwxIDQsMTdBMiwyIDAgMCwxIDIsMTVBMiwyIDAgMCwxIDQsMTNINlYxNU03LDE1QTIsMiAwIDAsMSA5LDEzQTIsMiAwIDAsMSAxMSwxNVYyMEEyLDIgMCAwLDEgOSwyMkEyLDIgMCAwLDEgNywyMFYxNU05LDdBMiwyIDAgMCwxIDcsNUEyLDIgMCAwLDEgOSwzQTIsMiAwIDAsMSAxMSw1VjdIOU05LDhBMiwyIDAgMCwxIDExLDEwQTIsMiAwIDAsMSA5LDEySDRBMiwyIDAgMCwxIDIsMTBBMiwyIDAgMCwxIDQsOEg5TTE3LDEwQTIsMiAwIDAsMSAxOSw4QTIsMiAwIDAsMSAyMSwxMEEyLDIgMCAwLDEgMTksMTJIMTdWMTBNMTYsMTBBMiwyIDAgMCwxIDE0LDEyQTIsMiAwIDAsMSAxMiwxMFY1QTIsMiAwIDAsMSAxNCwzQTIsMiAwIDAsMSAxNiw1VjEwTTE0LDE4QTIsMiAwIDAsMSAxNiwyMEEyLDIgMCAwLDEgMTQsMjJBMiwyIDAgMCwxIDEyLDIwVjE4SDE0TTE0LDE3QTIsMiAwIDAsMSAxMiwxNUEyLDIgMCAwLDEgMTQsMTNIMTlBMiwyIDAgMCwxIDIxLDE1QTIsMiAwIDAsMSAxOSwxN0gxNFoiIC8+PC9zdmc+"
) )

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNode.java

@ -59,8 +59,7 @@ import java.util.concurrent.TimeUnit;
nodeDescription = "Process device messages based on device profile settings", nodeDescription = "Process device messages based on device profile settings",
nodeDetails = "Create and clear alarms based on alarm rules defined in device profile. The output relation type is either " + nodeDetails = "Create and clear alarms based on alarm rules defined in device profile. The output relation type is either " +
"'Alarm Created', 'Alarm Updated', 'Alarm Severity Updated' and 'Alarm Cleared' or simply 'Success' if no alarms were affected.", "'Alarm Created', 'Alarm Updated', 'Alarm Severity Updated' and 'Alarm Cleared' or simply 'Success' if no alarms were affected.",
uiResources = {"static/rulenode/rulenode-core-config.js"}, configDirective = "tbActionNodeDeviceProfileConfig"
configDirective = "tbDeviceProfileConfig"
) )
public class TbDeviceProfileNode implements TbNode { public class TbDeviceProfileNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNode.java

@ -45,7 +45,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
configClazz = TbRabbitMqNodeConfiguration.class, configClazz = TbRabbitMqNodeConfiguration.class,
nodeDescription = "Publish messages to the RabbitMQ", nodeDescription = "Publish messages to the RabbitMQ",
nodeDetails = "Will publish message payload to RabbitMQ queue.", nodeDetails = "Will publish message payload to RabbitMQ queue.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeRabbitMqConfig", configDirective = "tbExternalNodeRabbitMqConfig",
iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZlcnNpb249IjEuMSIgeT0iMHB4IiB4PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIHN0cm9rZS13aWR0aD0iLjg0OTU2IiBkPSJtODYwLjQ3IDQxNi4zMmgtMjYyLjAxYy0xMi45MTMgMC0yMy42MTgtMTAuNzA0LTIzLjYxOC0yMy42MTh2LTI3Mi43MWMwLTIwLjMwNS0xNi4yMjctMzYuMjc2LTM2LjI3Ni0zNi4yNzZoLTkzLjc5MmMtMjAuMzA1IDAtMzYuMjc2IDE2LjIyNy0zNi4yNzYgMzYuMjc2djI3MC44NGMtMC4yNTQ4NyAxNC4xMDMtMTEuNDY5IDI1LjU3Mi0yNS43NDIgMjUuNTcybC04NS42MzYgMC42Nzk2NWMtMTQuMTAzIDAtMjUuNTcyLTExLjQ2OS0yNS41NzItMjUuNTcybDAuNjc5NjUtMjcxLjUyYzAtMjAuMzA1LTE2LjIyNy0zNi4yNzYtMzYuMjc2LTM2LjI3NmgtOTMuNTM3Yy0yMC4zMDUgMC0zNi4yNzYgMTYuMjI3LTM2LjI3NiAzNi4yNzZ2NzYzLjg0YzAgMTguMDk2IDE0Ljc4MiAzMi40NTMgMzIuNDUzIDMyLjQ1M2g3MjIuODFjMTguMDk2IDAgMzIuNDUzLTE0Ljc4MiAzMi40NTMtMzIuNDUzdi00MzUuMzFjLTEuMTg5NC0xOC4xODEtMTUuMjkyLTMyLjE5OC0zMy4zODgtMzIuMTk4em0tMTIyLjY4IDI4Ny4wN2MwIDIzLjYxOC0xOC44NiA0Mi40NzgtNDIuNDc4IDQyLjQ3OGgtNzMuOTk3Yy0yMy42MTggMC00Mi40NzgtMTguODYtNDIuNDc4LTQyLjQ3OHYtNzQuMjUyYzAtMjMuNjE4IDE4Ljg2LTQyLjQ3OCA0Mi40NzgtNDIuNDc4aDczLjk5N2MyMy42MTggMCA0Mi40NzggMTguODYgNDIuNDc4IDQyLjQ3OHoiLz48L3N2Zz4=" iconUrl = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZlcnNpb249IjEuMSIgeT0iMHB4IiB4PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPjxwYXRoIHN0cm9rZS13aWR0aD0iLjg0OTU2IiBkPSJtODYwLjQ3IDQxNi4zMmgtMjYyLjAxYy0xMi45MTMgMC0yMy42MTgtMTAuNzA0LTIzLjYxOC0yMy42MTh2LTI3Mi43MWMwLTIwLjMwNS0xNi4yMjctMzYuMjc2LTM2LjI3Ni0zNi4yNzZoLTkzLjc5MmMtMjAuMzA1IDAtMzYuMjc2IDE2LjIyNy0zNi4yNzYgMzYuMjc2djI3MC44NGMtMC4yNTQ4NyAxNC4xMDMtMTEuNDY5IDI1LjU3Mi0yNS43NDIgMjUuNTcybC04NS42MzYgMC42Nzk2NWMtMTQuMTAzIDAtMjUuNTcyLTExLjQ2OS0yNS41NzItMjUuNTcybDAuNjc5NjUtMjcxLjUyYzAtMjAuMzA1LTE2LjIyNy0zNi4yNzYtMzYuMjc2LTM2LjI3NmgtOTMuNTM3Yy0yMC4zMDUgMC0zNi4yNzYgMTYuMjI3LTM2LjI3NiAzNi4yNzZ2NzYzLjg0YzAgMTguMDk2IDE0Ljc4MiAzMi40NTMgMzIuNDUzIDMyLjQ1M2g3MjIuODFjMTguMDk2IDAgMzIuNDUzLTE0Ljc4MiAzMi40NTMtMzIuNDUzdi00MzUuMzFjLTEuMTg5NC0xOC4xODEtMTUuMjkyLTMyLjE5OC0zMy4zODgtMzIuMTk4em0tMTIyLjY4IDI4Ny4wN2MwIDIzLjYxOC0xOC44NiA0Mi40NzgtNDIuNDc4IDQyLjQ3OGgtNzMuOTk3Yy0yMy42MTggMC00Mi40NzgtMTguODYtNDIuNDc4LTQyLjQ3OHYtNzQuMjUyYzAtMjMuNjE4IDE4Ljg2LTQyLjQ3OCA0Mi40NzgtNDIuNDc4aDczLjk5N2MyMy42MTggMCA0Mi40NzggMTguODYgNDIuNDc4IDQyLjQ3OHoiLz48L3N2Zz4="
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeConfiguration.java

@ -44,7 +44,6 @@ public class TbRabbitMqNodeConfiguration implements NodeConfiguration<TbRabbitMq
configuration.setExchangeNamePattern(""); configuration.setExchangeNamePattern("");
configuration.setRoutingKeyPattern(""); configuration.setRoutingKeyPattern("");
configuration.setMessageProperties(null); configuration.setMessageProperties(null);
configuration.setHost(ConnectionFactory.DEFAULT_HOST);
configuration.setPort(ConnectionFactory.DEFAULT_AMQP_PORT); configuration.setPort(ConnectionFactory.DEFAULT_AMQP_PORT);
configuration.setVirtualHost(ConnectionFactory.DEFAULT_VHOST); configuration.setVirtualHost(ConnectionFactory.DEFAULT_VHOST);
configuration.setUsername(ConnectionFactory.DEFAULT_USER); configuration.setUsername(ConnectionFactory.DEFAULT_USER);

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbRestApiCallNode.java

@ -45,7 +45,6 @@ import java.util.List;
"For example <b>statusCode</b> field can be accessed with <code>metadata.statusCode</code>." + "For example <b>statusCode</b> field can be accessed with <code>metadata.statusCode</code>." +
"<br/><b>Note-</b> if you use system proxy properties, the next system proxy properties should be added: \"http.proxyHost\" and \"http.proxyPort\" or \"https.proxyHost\" and \"https.proxyPort\" or \"socksProxyHost\" and \"socksProxyPort\"," + "<br/><b>Note-</b> if you use system proxy properties, the next system proxy properties should be added: \"http.proxyHost\" and \"http.proxyPort\" or \"https.proxyHost\" and \"https.proxyPort\" or \"socksProxyHost\" and \"socksProxyPort\"," +
"and if your proxy with auth, the next ones should be added: \"tb.proxy.user\" and \"tb.proxy.password\" to the thingsboard.conf file.", "and if your proxy with auth, the next ones should be added: \"tb.proxy.user\" and \"tb.proxy.password\" to the thingsboard.conf file.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeRestApiCallConfig", configDirective = "tbExternalNodeRestApiCallConfig",
iconUrl = "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB2ZXJzaW9uPSIxLjEiIHk9IjBweCIgeD0iMHB4Ij48ZyB0cmFuc2Zvcm09Im1hdHJpeCguOTQ5NzUgMCAwIC45NDk3NSAxNy4xMiAyNi40OTIpIj48cGF0aCBkPSJtMTY5LjExIDEwOC41NGMtOS45MDY2IDAuMDczNC0xOS4wMTQgNi41NzI0LTIyLjAxNCAxNi40NjlsLTY5Ljk5MyAyMzEuMDhjLTMuNjkwNCAxMi4xODEgMy4yODkyIDI1LjIyIDE1LjQ2OSAyOC45MSAyLjIyNTkgMC42NzQ4MSA0LjQ5NjkgMSA2LjcyODUgMSA5Ljk3MjEgMCAxOS4xNjUtNi41MTUzIDIyLjE4Mi0xNi40NjdhNi41MjI0IDYuNTIyNCAwIDAgMCAwLjAwMiAtMC4wMDJsNjkuOTktMjMxLjA3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMCAtMC4wMDJjMy42ODU1LTEyLjE4MS0zLjI4Ny0yNS4yMjUtMTUuNDcxLTI4LjkxMi0yLjI4MjUtMC42OTE0NS00LjYxMTYtMS4wMTY5LTYuODk4NC0xem04NC45ODggMGMtOS45MDQ4IDAuMDczNC0xOS4wMTggNi41Njc1LTIyLjAxOCAxNi40NjlsLTY5Ljk4NiAyMzEuMDhjLTMuNjg5OCAxMi4xNzkgMy4yODUzIDI1LjIxNyAxNS40NjUgMjguOTA4IDIuMjI5NyAwLjY3NjQ3IDQuNTAwOCAxLjAwMiA2LjczMjQgMS4wMDIgOS45NzIxIDAgMTkuMTY1LTYuNTE1MyAyMi4xODItMTYuNDY3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMC4wMDIgLTAuMDAybDY5Ljk4OC0yMzEuMDdjMy42OTA4LTEyLjE4MS0zLjI4NTItMjUuMjIzLTE1LjQ2Ny0yOC45MTItMi4yODE0LTAuNjkyMzEtNC42MTA4LTEuMDE4OS02Ljg5ODQtMS4wMDJ6bS0yMTcuMjkgNDIuMjNjLTEyLjcyOS0wLjAwMDg3LTIzLjE4OCAxMC40NTYtMjMuMTg4IDIzLjE4NiAwLjAwMSAxMi43MjggMTAuNDU5IDIzLjE4NiAyMy4xODggMjMuMTg2IDEyLjcyNy0wLjAwMSAyMy4xODMtMTAuNDU5IDIzLjE4NC0yMy4xODYgMC4wMDA4NzYtMTIuNzI4LTEwLjQ1Ni0yMy4xODUtMjMuMTg0LTIzLjE4NnptMCAxNDYuNjRjLTEyLjcyNy0wLjAwMDg3LTIzLjE4NiAxMC40NTUtMjMuMTg4IDIzLjE4NC0wLjAwMDg3MyAxMi43MjkgMTAuNDU4IDIzLjE4OCAyMy4xODggMjMuMTg4IDEyLjcyOC0wLjAwMSAyMy4xODQtMTAuNDYgMjMuMTg0LTIzLjE4OC0wLjAwMS0xMi43MjYtMTAuNDU3LTIzLjE4My0yMy4xODQtMjMuMTg0em0yNzAuNzkgNDIuMjExYy0xMi43MjcgMC0yMy4xODQgMTAuNDU3LTIzLjE4NCAyMy4xODRzMTAuNDU1IDIzLjE4OCAyMy4xODQgMjMuMTg4aDE1NC45OGMxMi43MjkgMCAyMy4xODYtMTAuNDYgMjMuMTg2LTIzLjE4OCAwLjAwMS0xMi43MjgtMTAuNDU4LTIzLjE4NC0yMy4xODYtMjMuMTg0eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzc2IDAgMCAxLjAzNzYgLTcuNTY3NiAtMTQuOTI1KSIgc3Ryb2tlLXdpZHRoPSIxLjI2OTMiLz48L2c+PC9zdmc+" iconUrl = "data:image/svg+xml;base64,PHN2ZyBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB2ZXJzaW9uPSIxLjEiIHk9IjBweCIgeD0iMHB4Ij48ZyB0cmFuc2Zvcm09Im1hdHJpeCguOTQ5NzUgMCAwIC45NDk3NSAxNy4xMiAyNi40OTIpIj48cGF0aCBkPSJtMTY5LjExIDEwOC41NGMtOS45MDY2IDAuMDczNC0xOS4wMTQgNi41NzI0LTIyLjAxNCAxNi40NjlsLTY5Ljk5MyAyMzEuMDhjLTMuNjkwNCAxMi4xODEgMy4yODkyIDI1LjIyIDE1LjQ2OSAyOC45MSAyLjIyNTkgMC42NzQ4MSA0LjQ5NjkgMSA2LjcyODUgMSA5Ljk3MjEgMCAxOS4xNjUtNi41MTUzIDIyLjE4Mi0xNi40NjdhNi41MjI0IDYuNTIyNCAwIDAgMCAwLjAwMiAtMC4wMDJsNjkuOTktMjMxLjA3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMCAtMC4wMDJjMy42ODU1LTEyLjE4MS0zLjI4Ny0yNS4yMjUtMTUuNDcxLTI4LjkxMi0yLjI4MjUtMC42OTE0NS00LjYxMTYtMS4wMTY5LTYuODk4NC0xem04NC45ODggMGMtOS45MDQ4IDAuMDczNC0xOS4wMTggNi41Njc1LTIyLjAxOCAxNi40NjlsLTY5Ljk4NiAyMzEuMDhjLTMuNjg5OCAxMi4xNzkgMy4yODUzIDI1LjIxNyAxNS40NjUgMjguOTA4IDIuMjI5NyAwLjY3NjQ3IDQuNTAwOCAxLjAwMiA2LjczMjQgMS4wMDIgOS45NzIxIDAgMTkuMTY1LTYuNTE1MyAyMi4xODItMTYuNDY3YTYuNTIyNCA2LjUyMjQgMCAwIDAgMC4wMDIgLTAuMDAybDY5Ljk4OC0yMzEuMDdjMy42OTA4LTEyLjE4MS0zLjI4NTItMjUuMjIzLTE1LjQ2Ny0yOC45MTItMi4yODE0LTAuNjkyMzEtNC42MTA4LTEuMDE4OS02Ljg5ODQtMS4wMDJ6bS0yMTcuMjkgNDIuMjNjLTEyLjcyOS0wLjAwMDg3LTIzLjE4OCAxMC40NTYtMjMuMTg4IDIzLjE4NiAwLjAwMSAxMi43MjggMTAuNDU5IDIzLjE4NiAyMy4xODggMjMuMTg2IDEyLjcyNy0wLjAwMSAyMy4xODMtMTAuNDU5IDIzLjE4NC0yMy4xODYgMC4wMDA4NzYtMTIuNzI4LTEwLjQ1Ni0yMy4xODUtMjMuMTg0LTIzLjE4NnptMCAxNDYuNjRjLTEyLjcyNy0wLjAwMDg3LTIzLjE4NiAxMC40NTUtMjMuMTg4IDIzLjE4NC0wLjAwMDg3MyAxMi43MjkgMTAuNDU4IDIzLjE4OCAyMy4xODggMjMuMTg4IDEyLjcyOC0wLjAwMSAyMy4xODQtMTAuNDYgMjMuMTg0LTIzLjE4OC0wLjAwMS0xMi43MjYtMTAuNDU3LTIzLjE4My0yMy4xODQtMjMuMTg0em0yNzAuNzkgNDIuMjExYy0xMi43MjcgMC0yMy4xODQgMTAuNDU3LTIzLjE4NCAyMy4xODRzMTAuNDU1IDIzLjE4OCAyMy4xODQgMjMuMTg4aDE1NC45OGMxMi43MjkgMCAyMy4xODYtMTAuNDYgMjMuMTg2LTIzLjE4OCAwLjAwMS0xMi43MjgtMTAuNDU4LTIzLjE4NC0yMy4xODYtMjMuMTg0eiIgdHJhbnNmb3JtPSJtYXRyaXgoMS4wMzc2IDAgMCAxLjAzNzYgLTcuNTY3NiAtMTQuOTI1KSIgc3Ryb2tlLXdpZHRoPSIxLjI2OTMiLz48L2c+PC9zdmc+"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbSendRestApiCallReplyNode.java

@ -35,7 +35,6 @@ import java.util.UUID;
configClazz = TbSendRestApiCallReplyNodeConfiguration.class, configClazz = TbSendRestApiCallReplyNodeConfiguration.class,
nodeDescription = "Sends reply to REST API call to rule engine", nodeDescription = "Sends reply to REST API call to rule engine",
nodeDetails = "Expects messages with any message type. Forwards incoming message as a reply to REST API call sent to rule engine.", nodeDetails = "Expects messages with any message type. Forwards incoming message as a reply to REST API call sent to rule engine.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeSendRestApiCallReplyConfig", configDirective = "tbActionNodeSendRestApiCallReplyConfig",
icon = "call_merge" icon = "call_merge"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCReplyNode.java

@ -48,7 +48,6 @@ import java.util.UUID;
configClazz = TbSendRpcReplyNodeConfiguration.class, configClazz = TbSendRpcReplyNodeConfiguration.class,
nodeDescription = "Sends reply to RPC call from device", nodeDescription = "Sends reply to RPC call from device",
nodeDetails = "Expects messages with any message type. Will forward message body to the device.", nodeDetails = "Expects messages with any message type. Will forward message body to the device.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeRpcReplyConfig", configDirective = "tbActionNodeRpcReplyConfig",
icon = "call_merge" icon = "call_merge"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rpc/TbSendRPCRequestNode.java

@ -49,7 +49,6 @@ import java.util.concurrent.TimeUnit;
nodeDescription = "Sends RPC call to device", nodeDescription = "Sends RPC call to device",
nodeDetails = "Expects messages with \"method\" and \"params\". Will forward response from device to next nodes." + nodeDetails = "Expects messages with \"method\" and \"params\". Will forward response from device to next nodes." +
"If the RPC call request is originated by REST API call from user, will forward the response to user immediately.", "If the RPC call request is originated by REST API call from user, will forward the response to user immediately.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeRpcRequestConfig", configDirective = "tbActionNodeRpcRequestConfig",
icon = "call_made" icon = "call_made"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/sms/TbSendSmsNode.java

@ -35,7 +35,6 @@ import static org.thingsboard.common.util.DonAsynchron.withCallback;
configClazz = TbSendSmsNodeConfiguration.class, configClazz = TbSendSmsNodeConfiguration.class,
nodeDescription = "Sends SMS message via SMS provider.", nodeDescription = "Sends SMS message via SMS provider.",
nodeDetails = "Will send SMS message by populating target phone numbers and sms message fields using values derived from message metadata.", nodeDetails = "Will send SMS message by populating target phone numbers and sms message fields using values derived from message metadata.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbExternalNodeSendSmsConfig", configDirective = "tbExternalNodeSendSmsConfig",
icon = "sms" icon = "sms"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgAttributesNode.java

@ -62,7 +62,6 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_ATTRIBUTES_R
"Additionally if checkbox <b>Send attributes updated notification</b> is set to true, rule node will put the \"Attributes Updated\" " + "Additionally if checkbox <b>Send attributes updated notification</b> is set to true, rule node will put the \"Attributes Updated\" " +
"event for <b>SHARED_SCOPE</b> and <b>SERVER_SCOPE</b> attributes updates to the corresponding rule engine queue." + "event for <b>SHARED_SCOPE</b> and <b>SERVER_SCOPE</b> attributes updates to the corresponding rule engine queue." +
"Performance checkbox 'Save attributes only if the value changes' will skip attributes overwrites for values with no changes (avoid concurrent writes because this check is not transactional; will not update 'Last updated time' for skipped attributes).", "Performance checkbox 'Save attributes only if the value changes' will skip attributes overwrites for values with no changes (avoid concurrent writes because this check is not transactional; will not update 'Last updated time' for skipped attributes).",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeAttributesConfig", configDirective = "tbActionNodeAttributesConfig",
icon = "file_upload" icon = "file_upload"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgDeleteAttributesNode.java

@ -45,7 +45,6 @@ import static org.thingsboard.server.common.data.DataConstants.SCOPE;
" a key selected in the configuration, it will be ignored. If delete operation is completed successfully, " + " a key selected in the configuration, it will be ignored. If delete operation is completed successfully, " +
" rule node will send the \"Attributes Deleted\" event to the root chain of the message originator and " + " rule node will send the \"Attributes Deleted\" event to the root chain of the message originator and " +
" send the incoming message via <b>Success</b> chain, otherwise, <b>Failure</b> chain is used.", " send the incoming message via <b>Success</b> chain, otherwise, <b>Failure</b> chain is used.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeDeleteAttributesConfig", configDirective = "tbActionNodeDeleteAttributesConfig",
icon = "remove_circle" icon = "remove_circle"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java

@ -58,7 +58,6 @@ import static org.thingsboard.server.common.data.msg.TbMsgType.POST_TELEMETRY_RE
"However, the timestamp of the messages originated by multiple devices/servers may be unsynchronized long before they are pushed to the queue. " + "However, the timestamp of the messages originated by multiple devices/servers may be unsynchronized long before they are pushed to the queue. " +
"The DB layer has certain optimizations to ignore the updates of the \"attributes\" and \"latest values\" tables if the new record has a timestamp that is older than the previous record. " + "The DB layer has certain optimizations to ignore the updates of the \"attributes\" and \"latest values\" tables if the new record has a timestamp that is older than the previous record. " +
"So, to make sure that all the messages will be processed correctly, one should enable this parameter for sequential message processing scenarios.", "So, to make sure that all the messages will be processed correctly, one should enable this parameter for sequential message processing scenarios.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeTimeseriesConfig", configDirective = "tbActionNodeTimeseriesConfig",
icon = "file_upload" icon = "file_upload"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationBeginNode.java

@ -34,7 +34,6 @@ import org.thingsboard.server.common.msg.TbMsg;
nodeDetails = "This node should be used together with \"synchronization end\" node. \n This node will put messages into queue based on message originator id. \n" + nodeDetails = "This node should be used together with \"synchronization end\" node. \n This node will put messages into queue based on message originator id. \n" +
"Subsequent messages will not be processed until the previous message processing is completed or timeout event occurs.\n" + "Subsequent messages will not be processed until the previous message processing is completed or timeout event occurs.\n" +
"Size of the queue per originator and timeout values are configurable on a system level", "Size of the queue per originator and timeout values are configurable on a system level",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbNodeEmptyConfig") configDirective = "tbNodeEmptyConfig")
@Deprecated @Deprecated
public class TbSynchronizationBeginNode implements TbNode { public class TbSynchronizationBeginNode implements TbNode {

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transaction/TbSynchronizationEndNode.java

@ -32,7 +32,6 @@ import org.thingsboard.server.common.msg.TbMsg;
configClazz = EmptyNodeConfiguration.class, configClazz = EmptyNodeConfiguration.class,
nodeDescription = "This Node is now deprecated. Use \"Checkpoint\" instead.", nodeDescription = "This Node is now deprecated. Use \"Checkpoint\" instead.",
nodeDetails = "", nodeDetails = "",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = ("tbNodeEmptyConfig") configDirective = ("tbNodeEmptyConfig")
) )
@Deprecated @Deprecated

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbChangeOriginatorNode.java

@ -54,7 +54,6 @@ import static org.thingsboard.rule.engine.transform.OriginatorSource.RELATED;
"<li><strong>Entity by name pattern</strong> - specify entity type and name pattern of new originator. Following entity types are supported: " + "<li><strong>Entity by name pattern</strong> - specify entity type and name pattern of new originator. Following entity types are supported: " +
"'Device', 'Asset', 'Entity View', 'Edge' or 'User'.</li></ul>" + "'Device', 'Asset', 'Entity View', 'Edge' or 'User'.</li></ul>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbTransformationNodeChangeOriginatorConfig", configDirective = "tbTransformationNodeChangeOriginatorConfig",
icon = "find_replace" icon = "find_replace"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbCopyKeysNode.java

@ -46,7 +46,6 @@ import java.util.stream.Collectors;
nodeDetails = "Copies key-value pairs from the message to message metadata, or vice-versa, according to the configured direction and keys. " + nodeDetails = "Copies key-value pairs from the message to message metadata, or vice-versa, according to the configured direction and keys. " +
"Regular expressions can be used to define which keys-value pairs to copy. Any configured key not found in the source will be ignored.<br><br>" + "Regular expressions can be used to define which keys-value pairs to copy. Any configured key not found in the source will be ignored.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbTransformationNodeCopyKeysConfig", configDirective = "tbTransformationNodeCopyKeysConfig",
icon = "content_copy" icon = "content_copy"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbDeleteKeysNode.java

@ -46,7 +46,6 @@ import java.util.stream.Collectors;
nodeDetails = "Deletes key-value pairs from the message or message metadata according to the configured " + nodeDetails = "Deletes key-value pairs from the message or message metadata according to the configured " +
"keys and/or regular expressions.<br><br>" + "keys and/or regular expressions.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbTransformationNodeDeleteKeysConfig", configDirective = "tbTransformationNodeDeleteKeysConfig",
icon = "remove_circle" icon = "remove_circle"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbJsonPathNode.java

@ -40,7 +40,6 @@ import java.util.concurrent.ExecutionException;
nodeDescription = "Transforms incoming message body using JSONPath expression.", nodeDescription = "Transforms incoming message body using JSONPath expression.",
nodeDetails = "JSONPath expression specifies a path to an element or a set of elements in a JSON structure.<br><br>" + nodeDetails = "JSONPath expression specifies a path to an element or a set of elements in a JSON structure.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
icon = "functions", icon = "functions",
configDirective = "tbTransformationNodeJsonPathConfig" configDirective = "tbTransformationNodeJsonPathConfig"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbRenameKeysNode.java

@ -43,7 +43,6 @@ import java.util.concurrent.ExecutionException;
nodeDetails = "Renames keys in the message or message metadata according to the provided mapping. " + nodeDetails = "Renames keys in the message or message metadata according to the provided mapping. " +
"If key to rename doesn't exist in the specified source (message or message metadata) it will be ignored.<br><br>" + "If key to rename doesn't exist in the specified source (message or message metadata) it will be ignored.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbTransformationNodeRenameKeysConfig", configDirective = "tbTransformationNodeRenameKeysConfig",
icon = "find_replace" icon = "find_replace"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbSplitArrayMsgNode.java

@ -43,7 +43,6 @@ import java.util.concurrent.ExecutionException;
nodeDetails = "Splits an array message into individual elements, with each element sent as a separate message. " + nodeDetails = "Splits an array message into individual elements, with each element sent as a separate message. " +
"All outbound messages will have the same type and metadata as the original array message.<br><br>" + "All outbound messages will have the same type and metadata as the original array message.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
icon = "content_copy", icon = "content_copy",
configDirective = "tbNodeEmptyConfig" configDirective = "tbNodeEmptyConfig"
) )

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/transform/TbTransformMsgNode.java

@ -41,7 +41,6 @@ import java.util.List;
"<code>{ msg: <i style=\"color: #666;\">new payload</i>,<br/>&nbsp&nbsp&nbspmetadata: <i style=\"color: #666;\">new metadata</i>,<br/>&nbsp&nbsp&nbspmsgType: <i style=\"color: #666;\">new msgType</i> }</code><br/>" + "<code>{ msg: <i style=\"color: #666;\">new payload</i>,<br/>&nbsp&nbsp&nbspmetadata: <i style=\"color: #666;\">new metadata</i>,<br/>&nbsp&nbsp&nbspmsgType: <i style=\"color: #666;\">new msgType</i> }</code><br/>" +
"All fields in resulting object are optional and will be taken from original message if not specified.<br><br>" + "All fields in resulting object are optional and will be taken from original message if not specified.<br><br>" +
"Output connections: <code>Success</code>, <code>Failure</code>.", "Output connections: <code>Success</code>, <code>Failure</code>.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbTransformationNodeScriptConfig" configDirective = "tbTransformationNodeScriptConfig"
) )
public class TbTransformMsgNode extends TbAbstractTransformNode<TbTransformMsgNodeConfiguration> { public class TbTransformMsgNode extends TbAbstractTransformNode<TbTransformMsgNodeConfiguration> {

1
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js

File diff suppressed because one or more lines are too long

2
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/rabbitmq/TbRabbitMqNodeTest.java

@ -103,7 +103,7 @@ public class TbRabbitMqNodeTest {
assertThat(config.getExchangeNamePattern()).isEqualTo(""); assertThat(config.getExchangeNamePattern()).isEqualTo("");
assertThat(config.getRoutingKeyPattern()).isEqualTo(""); assertThat(config.getRoutingKeyPattern()).isEqualTo("");
assertThat(config.getMessageProperties()).isNull(); assertThat(config.getMessageProperties()).isNull();
assertThat(config.getHost()).isEqualTo(ConnectionFactory.DEFAULT_HOST); assertThat(config.getHost()).isNull();
assertThat(config.getPort()).isEqualTo(ConnectionFactory.DEFAULT_AMQP_PORT); assertThat(config.getPort()).isEqualTo(ConnectionFactory.DEFAULT_AMQP_PORT);
assertThat(config.getVirtualHost()).isEqualTo(ConnectionFactory.DEFAULT_VHOST); assertThat(config.getVirtualHost()).isEqualTo(ConnectionFactory.DEFAULT_VHOST);
assertThat(config.getUsername()).isEqualTo(ConnectionFactory.DEFAULT_USER); assertThat(config.getUsername()).isEqualTo(ConnectionFactory.DEFAULT_USER);

9
ui-ngx/src/app/core/http/domain.service.ts

@ -32,9 +32,12 @@ export class DomainService {
) { ) {
} }
public saveDomain(domain: Domain, oauth2ClientIds: Array<string>, config?: RequestConfig): Observable<Domain> { public saveDomain(domain: Domain, oauth2ClientIds?: Array<string>, config?: RequestConfig): Observable<Domain> {
return this.http.post<Domain>(`/api/domain?oauth2ClientIds=${oauth2ClientIds.join(',')}`, let url = '/api/domain';
domain, defaultHttpOptionsFromConfig(config)); if (oauth2ClientIds?.length) {
url += `?oauth2ClientIds=${oauth2ClientIds.join(',')}`;
}
return this.http.post<Domain>(url, domain, defaultHttpOptionsFromConfig(config));
} }
public updateOauth2Clients(id: string, oauth2ClientIds: Array<string>, config?: RequestConfig): Observable<void> { public updateOauth2Clients(id: string, oauth2ClientIds: Array<string>, config?: RequestConfig): Observable<void> {

6
ui-ngx/src/app/core/http/rule-chain.service.ts

@ -180,6 +180,10 @@ export class RuleChainService {
return this.http.post<TestScriptResult>(url, inputParams, defaultHttpOptionsFromConfig(config)); return this.http.post<TestScriptResult>(url, inputParams, defaultHttpOptionsFromConfig(config));
} }
public registerSystemRuleNodeConfigModule(module: any) {
Object.assign(this.ruleNodeConfigComponents, this.resourcesService.extractComponentsFromModule<IRuleNodeConfigurationComponent>(module, true));
}
private loadRuleNodeComponents(ruleChainType: RuleChainType, config?: RequestConfig): Observable<Array<RuleNodeComponentDescriptor>> { private loadRuleNodeComponents(ruleChainType: RuleChainType, config?: RequestConfig): Observable<Array<RuleNodeComponentDescriptor>> {
return this.componentDescriptorService.getComponentDescriptorsByTypes(ruleNodeTypeComponentTypes, ruleChainType, config).pipe( return this.componentDescriptorService.getComponentDescriptorsByTypes(ruleNodeTypeComponentTypes, ruleChainType, config).pipe(
map((components) => { map((components) => {
@ -211,7 +215,7 @@ export class RuleChainService {
Observable<RuleNodeComponentDescriptor> { Observable<RuleNodeComponentDescriptor> {
const nodeDefinition = component.configurationDescriptor.nodeDefinition; const nodeDefinition = component.configurationDescriptor.nodeDefinition;
const uiResources = nodeDefinition.uiResources; const uiResources = nodeDefinition.uiResources;
if (uiResources && uiResources.length) { if (!this.ruleNodeConfigComponents[nodeDefinition.configDirective] && uiResources && uiResources.length) {
const commonResources = uiResources.filter((resource) => !resource.endsWith('.js')); const commonResources = uiResources.filter((resource) => !resource.endsWith('.js'));
const moduleResource = uiResources.find((resource) => resource.endsWith('.js')); const moduleResource = uiResources.find((resource) => resource.endsWith('.js'));
const tasks: Observable<any>[] = []; const tasks: Observable<any>[] = [];

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save