Browse Source

Merge branch 'master' into related-agg-cf/dx-improvements

pull/14827/head
Viacheslav Klimov 8 months ago
committed by GitHub
parent
commit
f693308cce
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      README.md
  2. 31
      TEST_FAST.md
  3. 2
      application/pom.xml
  4. 66
      application/src/main/data/upgrade/basic/schema_update.sql
  5. 6
      application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java
  6. 1
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  7. 7
      application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java
  8. 2
      application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java
  9. 2
      application/src/main/resources/thingsboard.yml
  10. 12
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  11. 97
      application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
  12. 14
      application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java
  13. 14
      application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
  14. 10
      application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java
  15. 19
      application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
  16. 28
      application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
  17. 5
      application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java
  18. 14
      application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java
  19. 3
      application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java
  20. 37
      application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java
  21. 14
      application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java
  22. 70
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  23. 2
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java
  24. 41
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
  25. 1
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java
  26. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java
  27. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java
  28. 13
      application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java
  29. 10
      application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java
  30. 6
      application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java
  31. 6
      application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java
  32. 42
      application/src/test/java/org/thingsboard/server/utils/PortFinder.java
  33. 1
      application/src/test/resources/application-test.properties
  34. 6
      application/src/test/resources/logback-test.xml
  35. 2
      common/actor/pom.xml
  36. 2
      common/cache/pom.xml
  37. 3
      common/cache/src/test/resources/logback-test.xml
  38. 2
      common/cluster-api/pom.xml
  39. 2
      common/coap-server/pom.xml
  40. 2
      common/dao-api/pom.xml
  41. 2
      common/data/pom.xml
  42. 2
      common/discovery-api/pom.xml
  43. 2
      common/edge-api/pom.xml
  44. 2
      common/edqs/pom.xml
  45. 3
      common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java
  46. 2
      common/message/pom.xml
  47. 2
      common/pom.xml
  48. 2
      common/proto/pom.xml
  49. 2
      common/queue/pom.xml
  50. 2
      common/script/pom.xml
  51. 2
      common/script/remote-js-client/pom.xml
  52. 2
      common/script/script-api/pom.xml
  53. 2
      common/stats/pom.xml
  54. 2
      common/transport/coap/pom.xml
  55. 2
      common/transport/http/pom.xml
  56. 2
      common/transport/lwm2m/pom.xml
  57. 2
      common/transport/mqtt/pom.xml
  58. 2
      common/transport/pom.xml
  59. 2
      common/transport/snmp/pom.xml
  60. 2
      common/transport/transport-api/pom.xml
  61. 2
      common/util/pom.xml
  62. 2
      common/version-control/pom.xml
  63. 2
      dao/pom.xml
  64. 7
      dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java
  65. 16
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java
  66. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeAssetRepository.java
  67. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeDeviceRepository.java
  68. 1
      dao/src/test/resources/application-test.properties
  69. 3
      dao/src/test/resources/logback-test.xml
  70. 2
      edqs/pom.xml
  71. 2
      monitoring/pom.xml
  72. 2
      msa/black-box-tests/pom.xml
  73. 4
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java
  74. 20
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
  75. 42
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java
  76. 176
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java
  77. 8
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java
  78. 21
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java
  79. 3
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java
  80. 2
      msa/edqs/pom.xml
  81. 2
      msa/js-executor/package.json
  82. 2
      msa/js-executor/pom.xml
  83. 2
      msa/monitoring/pom.xml
  84. 4
      msa/pom.xml
  85. 2
      msa/tb-node/pom.xml
  86. 2
      msa/tb/pom.xml
  87. 2
      msa/transport/coap/pom.xml
  88. 2
      msa/transport/http/pom.xml
  89. 2
      msa/transport/lwm2m/pom.xml
  90. 2
      msa/transport/mqtt/pom.xml
  91. 2
      msa/transport/pom.xml
  92. 2
      msa/transport/snmp/pom.xml
  93. 2
      msa/vc-executor-docker/pom.xml
  94. 2
      msa/vc-executor/pom.xml
  95. 2
      msa/web-ui/package.json
  96. 2
      msa/web-ui/pom.xml
  97. 4
      netty-mqtt/pom.xml
  98. 12
      pom.xml
  99. 6
      rest-client/pom.xml
  100. 63
      rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java

4
README.md

@ -123,9 +123,9 @@ ThingsBoard is a scalable, user-friendly, and device-agnostic IoT platform that
[![SCADA Swimming pool](https://github.com/user-attachments/assets/68fd9e29-99f1-4c16-8c4c-476f4ccb20c0 "SCADA Swimming pool")](https://thingsboard.io/use-cases/scada/) [![SCADA Swimming pool](https://github.com/user-attachments/assets/68fd9e29-99f1-4c16-8c4c-476f4ccb20c0 "SCADA Swimming pool")](https://thingsboard.io/use-cases/scada/)
[**Fleet tracking**](https://thingsboard.io/use-cases/fleet-tracking/) [**Site fleet tracking**](https://thingsboard.io/use-cases/site-fleet-tracking/)
[![Fleet tracking](https://github.com/user-attachments/assets/9e8938ba-ee0c-4599-9494-d74b7de8a63d "Fleet tracking")](https://thingsboard.io/use-cases/fleet-tracking/) [![Site fleet tracking](https://github.com/user-attachments/assets/d6ce0766-b138-4a42-86aa-7112a543026c "Site fleet tracking")](https://thingsboard.io/use-cases/site-fleet-tracking/)
[**Smart farming**](https://thingsboard.io/use-cases/smart-farming/) [**Smart farming**](https://thingsboard.io/use-cases/smart-farming/)

31
TEST_FAST.md

@ -0,0 +1,31 @@
## Running tests in parallel with a reasonable memory usage
```bash
export MAVEN_OPTS="-Xmx1024m"
export NODE_OPTIONS="--max_old_space_size=4096"
export SUREFIRE_JAVA_OPTS="-Xmx1200m -Xss256k -XX:+ExitOnOutOfMemoryError"
mvn clean install -T6 -DskipTests
mvn test -pl='!application,!dao,!ui-ngx,!msa/js-executor,!msa/web-ui' -T4
mvn test -pl dao -Dparallel=packages -DforkCount=4
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.controller.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.edge.**' -DforkCount=4 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.service.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.mqtt.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.coap.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.lwm2m.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='**/*TestSuite.java' -DforkCount=4 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
#the rest of application tests
mvn test -pl application -Dtest='
!**/nosql/*Test.java,
!org.thingsboard.server.controller.**,
!org.thingsboard.server.edge.**,
!org.thingsboard.server.service.**,
!org.thingsboard.server.transport.mqtt.**,
!org.thingsboard.server.transport.coap.**,
!org.thingsboard.server.transport.lwm2m.**
' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
```

2
application/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>application</artifactId> <artifactId>application</artifactId>

66
application/src/main/data/upgrade/basic/schema_update.sql

@ -14,69 +14,3 @@
-- limitations under the License. -- limitations under the License.
-- --
-- UPDATE TENANT PROFILE CONFIGURATION START
UPDATE tenant_profile
SET profile_data = jsonb_set(
profile_data,
'{configuration}',
jsonb_build_object(
'minAllowedScheduledUpdateIntervalInSecForCF', 10,
'maxRelationLevelPerCfArgument', 2,
'maxRelatedEntitiesToReturnPerCfArgument', 100,
'minAllowedDeduplicationIntervalInSecForCF', 10,
'minAllowedAggregationIntervalInSecForCF', 60,
'intermediateAggregationIntervalInSecForCF', 300,
'cfReevaluationCheckInterval', 60,
'alarmsReevaluationInterval', 60
)
||
jsonb_strip_nulls(profile_data -> 'configuration')
)
WHERE NOT (
jsonb_strip_nulls(profile_data -> 'configuration') ?& ARRAY[
'minAllowedScheduledUpdateIntervalInSecForCF',
'maxRelationLevelPerCfArgument',
'maxRelatedEntitiesToReturnPerCfArgument',
'minAllowedDeduplicationIntervalInSecForCF',
'minAllowedAggregationIntervalInSecForCF',
'intermediateAggregationIntervalInSecForCF',
'cfReevaluationCheckInterval',
'alarmsReevaluationInterval'
]
);
-- UPDATE TENANT PROFILE CONFIGURATION END
-- CALCULATED FIELD UNIQUE CONSTRAINT UPDATE START
ALTER TABLE calculated_field DROP CONSTRAINT IF EXISTS calculated_field_unq_key;
ALTER TABLE calculated_field ADD CONSTRAINT calculated_field_unq_key UNIQUE (entity_id, type, name);
-- CALCULATED FIELD UNIQUE CONSTRAINT UPDATE END
-- CALCULATED FIELD OUTPUT STRATEGY UPDATE START
UPDATE calculated_field
SET configuration = jsonb_set(
configuration::jsonb,
'{output}',
(configuration::jsonb -> 'output')
|| jsonb_build_object(
'strategy',
jsonb_build_object(
'type', 'RULE_CHAIN'
)
),
false
)
WHERE (configuration::jsonb -> 'output' -> 'strategy') IS NULL;
-- CALCULATED FIELD OUTPUT STRATEGY UPDATE END
-- REMOVAL OF CALCULATED FIELD LINKS PERSISTENCE START
DROP TABLE IF EXISTS calculated_field_link;
ANALYZE calculated_field;
-- REMOVAL OF CALCULATED FIELD LINKS PERSISTENCE END

6
application/src/main/java/org/thingsboard/server/actors/calculatedField/CalculatedFieldManagerMessageProcessor.java

@ -425,7 +425,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
try { try {
cfCtx.init(); cfCtx.init();
} catch (Exception e) { } catch (Exception e) {
throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build(); throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage(e.getMessage()).build();
} }
calculatedFields.put(cf.getId(), cfCtx); calculatedFields.put(cf.getId(), cfCtx);
// We use copy on write lists to safely pass the reference to another actor for the iteration. // We use copy on write lists to safely pass the reference to another actor for the iteration.
@ -456,7 +456,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
try { try {
newCfCtx.init(); newCfCtx.init();
} catch (Exception e) { } catch (Exception e) {
throw CalculatedFieldException.builder().ctx(newCfCtx).eventEntity(newCfCtx.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build(); throw CalculatedFieldException.builder().ctx(newCfCtx).eventEntity(newCfCtx.getEntityId()).cause(e).errorMessage(e.getMessage()).build();
} finally { } finally {
calculatedFields.put(newCf.getId(), newCfCtx); calculatedFields.put(newCf.getId(), newCfCtx);
List<CalculatedFieldCtx> oldCfList = entityIdCalculatedFields.get(newCf.getEntityId()); List<CalculatedFieldCtx> oldCfList = entityIdCalculatedFields.get(newCf.getEntityId());
@ -819,7 +819,7 @@ public class CalculatedFieldManagerMessageProcessor extends AbstractContextAware
try { try {
cfCtx.init(); cfCtx.init();
} catch (Exception e) { } catch (Exception e) {
throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage("Failed to initialize CF context").build(); throw CalculatedFieldException.builder().ctx(cfCtx).eventEntity(cf.getEntityId()).cause(e).errorMessage(e.getMessage()).build();
} finally { } finally {
calculatedFields.put(cf.getId(), cfCtx); calculatedFields.put(cf.getId(), cfCtx);
// We use copy on write lists to safely pass the reference to another actor for the iteration. // We use copy on write lists to safely pass the reference to another actor for the iteration.

1
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -116,7 +116,6 @@ public class ThingsboardInstallService {
entityDatabaseSchemaService.createDatabaseIndexes(); entityDatabaseSchemaService.createDatabaseIndexes();
// TODO: cleanup update code after each release // TODO: cleanup update code after each release
systemDataLoaderService.updateDefaultNotificationConfigs(false);
// Runs upgrade scripts that are not possible in plain SQL. // Runs upgrade scripts that are not possible in plain SQL.
dataUpdateService.updateData(); dataUpdateService.updateData();

7
application/src/main/java/org/thingsboard/server/service/cf/ctx/state/CalculatedFieldCtx.java

@ -387,7 +387,7 @@ public class CalculatedFieldCtx implements Closeable {
tbelExpressions.put(expression, engine); tbelExpressions.put(expression, engine);
} catch (Exception e) { } catch (Exception e) {
initialized = false; initialized = false;
throw new RuntimeException("Failed to init calculated field ctx. Invalid expression syntax.", e); throw new RuntimeException("Failed to initialize CF context. The script expression is invalid. Please check for syntax errors or unsupported functions.", e);
} }
} }
@ -404,7 +404,7 @@ public class CalculatedFieldCtx implements Closeable {
simpleExpressions.put(expression, compiledExpression); simpleExpressions.put(expression, compiledExpression);
} else { } else {
initialized = false; initialized = false;
throw new RuntimeException("Failed to init calculated field ctx. Invalid expression syntax."); throw new RuntimeException("Failed to initialize CF context. The expression has invalid syntax or unknown variables. Ensure all mathematical operators are correct.");
} }
} }
@ -726,6 +726,9 @@ public class CalculatedFieldCtx implements Closeable {
return true; return true;
} }
} }
if (cfType == CalculatedFieldType.PROPAGATION && !propagationArgument.equals(other.propagationArgument)) {
return true;
}
if (hasGeofencingZoneGroupConfigurationChanges(other)) { if (hasGeofencingZoneGroupConfigurationChanges(other)) {
return true; return true;
} }

2
application/src/main/java/org/thingsboard/server/service/install/DefaultDatabaseSchemaSettingsService.java

@ -31,7 +31,7 @@ public class DefaultDatabaseSchemaSettingsService implements DatabaseSchemaSetti
// This list should include all versions that are compatible for the upgrade in 4 digits format (like 4.2.0.0, etc.). // This list should include all versions that are compatible for the upgrade in 4 digits format (like 4.2.0.0, etc.).
// The compatibility cycle usually breaks when we have some scripts written in Java that may not work after a new release. // The compatibility cycle usually breaks when we have some scripts written in Java that may not work after a new release.
// TODO: don't check the "patch" number, since upgrade is not required for patch releases // TODO: don't check the "patch" number, since upgrade is not required for patch releases
private static final List<String> SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.2.1.0", "4.2.1.1", "4.2.1.2"); private static final List<String> SUPPORTED_VERSIONS_FOR_UPGRADE = List.of("4.3.0.0", "4.3.0.1");
private final ProjectInfo projectInfo; private final ProjectInfo projectInfo;
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;

2
application/src/main/resources/thingsboard.yml

@ -217,7 +217,7 @@ ui:
# Help parameters # Help parameters
help: help:
# Base URL for UI help assets # Base URL for UI help assets
base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.3}" base-url: "${UI_HELP_BASE_URL:https://raw.githubusercontent.com/thingsboard/thingsboard-ui-help/release-4.4}"
# Database telemetry parameters # Database telemetry parameters
database: database:

12
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -60,6 +60,7 @@ import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
@ -349,7 +350,14 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
if (this.mockMvc == null) { if (this.mockMvc == null) {
this.mockMvc = webAppContextSetup(webApplicationContext) this.mockMvc = webAppContextSetup(webApplicationContext)
.apply(springSecurity()).build(); .apply(springSecurity())
// conditional printing of non 2xx responses
.alwaysDo(result -> {
if (result.getResponse().getStatus() >= 400) {
MockMvcResultHandlers.log().handle(result);
}
})
.build();
} }
loginSysAdmin(); loginSysAdmin();
@ -423,7 +431,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
jdbcTemplate.execute("TRUNCATE TABLE notification"); jdbcTemplate.execute("TRUNCATE TABLE notification");
log.info("Executed web test teardown"); log.debug("Executed web test teardown");
} }
private void verifyNoTenantsLeft() throws Exception { private void verifyNoTenantsLeft() throws Exception {

97
application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java

@ -36,7 +36,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
@ -99,6 +102,7 @@ import org.thingsboard.server.service.edge.instructions.EdgeUpgradeInstructionsS
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -121,7 +125,12 @@ import static org.thingsboard.server.edge.AbstractEdgeTest.CONNECT_MESSAGE_COUNT
public class EdgeControllerTest extends AbstractControllerTest { public class EdgeControllerTest extends AbstractControllerTest {
public static final String EDGE_HOST = "localhost"; public static final String EDGE_HOST = "localhost";
public static final int EDGE_PORT = 7070; public static final int EDGE_PORT = TestSocketUtils.findAvailableTcpPort();
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.debug("edges.rpc.port = {}", EDGE_PORT);
registry.add("edges.rpc.port", () -> EDGE_PORT);
}
private IdComparator<Edge> idComparator = new IdComparator<>(); private IdComparator<Edge> idComparator = new IdComparator<>();
@ -916,11 +925,11 @@ public class EdgeControllerTest extends AbstractControllerTest {
verifyFetchersMsgs(edgeImitator, savedDevice); verifyFetchersMsgs(edgeImitator, savedDevice);
// verify queue msgs // verify queue msgs
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1")); Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId())); Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), savedDevice.getId()));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
printQueueMsgsIfNotEmpty(edgeImitator); printQueueMsgsIfNotEmpty(edgeImitator);
// 17 connect messages // 17 connect messages
@ -1000,32 +1009,32 @@ public class EdgeControllerTest extends AbstractControllerTest {
} }
private void verifyFetchersMsgs(EdgeImitator edgeImitator, Device savedDevice) { private void verifyFetchersMsgs(EdgeImitator edgeImitator, Device savedDevice) {
Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main")); Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main"));
Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain")); Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain"));
Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator))); Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator)));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "general")); Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "general"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mail")); Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "mail"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "connectivity")); Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "connectivity"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "jwt")); Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "jwt"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgs(), currentUserId)); Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), currentUserId));
Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN)); Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN));
Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public")); Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1")); Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId())); Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), savedDevice.getId()));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgs(), tenantId)); Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgsDeque(), tenantId));
Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgs(), tenantProfileId)); Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgsDeque(), tenantProfileId));
Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgs())); Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgsDeque()));
} }
private boolean popQueueMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popQueueMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof QueueUpdateMsg queueUpdateMsg) { if (message instanceof QueueUpdateMsg queueUpdateMsg) {
Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true); Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true);
@ -1039,7 +1048,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popRuleChainMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popRuleChainMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof RuleChainUpdateMsg ruleChainUpdateMsg) { if (message instanceof RuleChainUpdateMsg ruleChainUpdateMsg) {
RuleChain ruleChain = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true); RuleChain ruleChain = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true);
@ -1055,7 +1064,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popRuleChainMetadataMsg(List<AbstractMessage> messages, UpdateMsgType msgType, RuleChainId ruleChainId) { private boolean popRuleChainMetadataMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, RuleChainId ruleChainId) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg) { if (message instanceof RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg) {
RuleChainMetaData ruleChainMetaData = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true); RuleChainMetaData ruleChainMetaData = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true);
@ -1070,7 +1079,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popAdminSettingsMsg(List<AbstractMessage> messages, String key) { private boolean popAdminSettingsMsg(Deque<AbstractMessage> messages, String key) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof AdminSettingsUpdateMsg adminSettingsUpdateMsg) { if (message instanceof AdminSettingsUpdateMsg adminSettingsUpdateMsg) {
AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true); AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true);
@ -1084,7 +1093,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popDeviceProfileMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popDeviceProfileMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof DeviceProfileUpdateMsg deviceProfileUpdateMsg) { if (message instanceof DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
DeviceProfile deviceProfile = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); DeviceProfile deviceProfile = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true);
@ -1099,7 +1108,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popDeviceMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popDeviceMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof DeviceUpdateMsg deviceUpdateMsg) { if (message instanceof DeviceUpdateMsg deviceUpdateMsg) {
Device device = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true); Device device = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true);
@ -1114,7 +1123,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popDeviceCredentialsMsg(List<AbstractMessage> messages, DeviceId deviceId) { private boolean popDeviceCredentialsMsg(Deque<AbstractMessage> messages, DeviceId deviceId) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { if (message instanceof DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
DeviceCredentials deviceCredentials = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true); DeviceCredentials deviceCredentials = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true);
@ -1128,7 +1137,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popAssetProfileMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popAssetProfileMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof AssetProfileUpdateMsg assetProfileUpdateMsg) { if (message instanceof AssetProfileUpdateMsg assetProfileUpdateMsg) {
AssetProfile assetProfile = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true); AssetProfile assetProfile = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true);
@ -1143,7 +1152,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popAssetMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popAssetMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof AssetUpdateMsg assetUpdateMsg) { if (message instanceof AssetUpdateMsg assetUpdateMsg) {
Asset asset = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); Asset asset = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true);
@ -1158,7 +1167,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popUserCredentialsMsg(List<AbstractMessage> messages, UserId userId) { private boolean popUserCredentialsMsg(Deque<AbstractMessage> messages, UserId userId) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof UserCredentialsUpdateMsg userCredentialsUpdateMsg) { if (message instanceof UserCredentialsUpdateMsg userCredentialsUpdateMsg) {
UserCredentials userCredentials = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true); UserCredentials userCredentials = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
@ -1172,7 +1181,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popUserMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String email, Authority authority) { private boolean popUserMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String email, Authority authority) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof UserUpdateMsg userUpdateMsg) { if (message instanceof UserUpdateMsg userUpdateMsg) {
User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
@ -1188,7 +1197,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popCustomerMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String title) { private boolean popCustomerMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String title) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof CustomerUpdateMsg customerUpdateMsg) { if (message instanceof CustomerUpdateMsg customerUpdateMsg) {
Customer customer = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true); Customer customer = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true);
@ -1203,7 +1212,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popTenantMsg(List<AbstractMessage> messages, TenantId tenantId1) { private boolean popTenantMsg(Deque<AbstractMessage> messages, TenantId tenantId1) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof TenantUpdateMsg tenantUpdateMsg) { if (message instanceof TenantUpdateMsg tenantUpdateMsg) {
Tenant tenant = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true); Tenant tenant = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true);
@ -1218,7 +1227,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popTenantProfileMsg(List<AbstractMessage> messages, TenantProfileId tenantProfileId) { private boolean popTenantProfileMsg(Deque<AbstractMessage> messages, TenantProfileId tenantProfileId) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof TenantProfileUpdateMsg tenantProfileUpdateMsg) { if (message instanceof TenantProfileUpdateMsg tenantProfileUpdateMsg) {
TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true);
@ -1233,7 +1242,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popSyncCompletedMsg(List<AbstractMessage> messages) { private boolean popSyncCompletedMsg(Deque<AbstractMessage> messages) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof SyncCompletedMsg) { if (message instanceof SyncCompletedMsg) {
messages.remove(message); messages.remove(message);

14
application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java

@ -38,6 +38,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
@ -88,6 +90,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_URL;
@TestPropertySource(properties = { @TestPropertySource(properties = {
"transport.mqtt.enabled=true", "transport.mqtt.enabled=true",
@ -97,6 +101,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@ContextConfiguration(classes = {EntityViewControllerTest.Config.class}) @ContextConfiguration(classes = {EntityViewControllerTest.Config.class})
@DaoSqlTest @DaoSqlTest
public class EntityViewControllerTest extends AbstractControllerTest { public class EntityViewControllerTest extends AbstractControllerTest {
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
}
static final TypeReference<PageData<EntityView>> PAGE_DATA_ENTITY_VIEW_TYPE_REF = new TypeReference<>() { static final TypeReference<PageData<EntityView>> PAGE_DATA_ENTITY_VIEW_TYPE_REF = new TypeReference<>() {
}; };
static final TypeReference<PageData<EntityViewInfo>> PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF = new TypeReference<>() { static final TypeReference<PageData<EntityViewInfo>> PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF = new TypeReference<>() {
@ -683,7 +693,7 @@ public class EntityViewControllerTest extends AbstractControllerTest {
String viewDeviceId = testDevice.getId().getId().toString(); String viewDeviceId = testDevice.getId().getId().toString();
String clientId = MqttAsyncClient.generateClientId(); String clientId = MqttAsyncClient.generateClientId();
MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions(); MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(accessToken); options.setUserName(accessToken);
@ -734,7 +744,7 @@ public class EntityViewControllerTest extends AbstractControllerTest {
assertNotNull(accessToken); assertNotNull(accessToken);
String clientId = MqttAsyncClient.generateClientId(); String clientId = MqttAsyncClient.generateClientId();
MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions(); MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(accessToken); options.setUserName(accessToken);

14
application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java

@ -27,7 +27,10 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
@ -118,6 +121,15 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
}) })
@Slf4j @Slf4j
abstract public class AbstractEdgeTest extends AbstractControllerTest { abstract public class AbstractEdgeTest extends AbstractControllerTest {
public static final String EDGE_HOST = "localhost";
public static final int EDGE_PORT = TestSocketUtils.findAvailableTcpPort();
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.debug("edges.rpc.port = {}", EDGE_PORT);
registry.add("edges.rpc.port", () -> EDGE_PORT);
}
public static final Integer CONNECT_MESSAGE_COUNT = 17; public static final Integer CONNECT_MESSAGE_COUNT = 17;
public static final Integer INSTALLATION_MESSAGE_COUNT = 8; public static final Integer INSTALLATION_MESSAGE_COUNT = 8;
public static final Integer SYNC_MESSAGE_COUNT = CONNECT_MESSAGE_COUNT + INSTALLATION_MESSAGE_COUNT; public static final Integer SYNC_MESSAGE_COUNT = CONNECT_MESSAGE_COUNT + INSTALLATION_MESSAGE_COUNT;
@ -144,7 +156,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
//8 installation messages //8 installation messages
installation(); installation();
edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret());
// 17 connect messages + 8 installation messages // 17 connect messages + 8 installation messages
edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT); edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT);
edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class); edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class);

10
application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java

@ -22,10 +22,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.protobuf.AbstractMessage; import com.google.protobuf.AbstractMessage;
import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.adaptor.JsonConverter;
@ -84,12 +87,19 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.gen.edge.v1.UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; import static org.thingsboard.server.gen.edge.v1.UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
@TestPropertySource(properties = { @TestPropertySource(properties = {
"transport.mqtt.enabled=true" "transport.mqtt.enabled=true"
}) })
@Slf4j
@DaoSqlTest @DaoSqlTest
public class DeviceEdgeTest extends AbstractEdgeTest { public class DeviceEdgeTest extends AbstractEdgeTest {
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
}
private static final String DEFAULT_DEVICE_TYPE = "default"; private static final String DEFAULT_DEVICE_TYPE = "default";

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

@ -69,10 +69,12 @@ 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.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -103,8 +105,17 @@ public class EdgeImitator {
@Getter @Getter
private EdgeConfiguration configuration; private EdgeConfiguration configuration;
@Getter private final ConcurrentLinkedDeque<AbstractMessage> downlinkMsgs;
private final List<AbstractMessage> downlinkMsgs;
//Returns collection copy as Unmodifiable list
//This addressing the issue: DeviceEdgeTest>AbstractEdgeTest.setupEdgeTest:212->AbstractEdgeTest.verifyEdgeConnectionAndInitialData:306->AbstractEdgeTest.validateMsgsCnt:387 » ConcurrentModification
public List<AbstractMessage> getDownlinkMsgs() {
return downlinkMsgs.stream().toList();
}
public Deque<AbstractMessage> getDownlinkMsgsDeque() {
return downlinkMsgs;
}
@Getter @Getter
private UplinkResponseMsg latestResponseMsg; private UplinkResponseMsg latestResponseMsg;
@ -113,7 +124,7 @@ public class EdgeImitator {
edgeRpcClient = new EdgeGrpcClient(); edgeRpcClient = new EdgeGrpcClient();
messagesLatch = new CountDownLatch(0); messagesLatch = new CountDownLatch(0);
responsesLatch = new CountDownLatch(0); responsesLatch = new CountDownLatch(0);
downlinkMsgs = new ArrayList<>(); downlinkMsgs = new ConcurrentLinkedDeque<>();
ignoredTypes = new ArrayList<>(); ignoredTypes = new ArrayList<>();
this.routingKey = routingKey; this.routingKey = routingKey;
this.routingSecret = routingSecret; this.routingSecret = routingSecret;
@ -458,7 +469,7 @@ public class EdgeImitator {
} }
public AbstractMessage getLatestMessage() { public AbstractMessage getLatestMessage() {
return downlinkMsgs.get(downlinkMsgs.size() - 1); return downlinkMsgs.peekLast();
} }
public void ignoreType(Class<? extends AbstractMessage> type) { public void ignoreType(Class<? extends AbstractMessage> type) {

28
application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java

@ -238,12 +238,12 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.set("createAlarm", BooleanNode.TRUE); .set("createAlarm", BooleanNode.TRUE);
postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString()); postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString());
await().atMost(10, TimeUnit.SECONDS) await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null); .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null);
Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType); Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType);
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
await().atMost(15, TimeUnit.SECONDS) await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> clients.values().stream().allMatch(client -> client.getLastDataUpdate() != null)); .until(() -> clients.values().stream().allMatch(client -> client.getLastDataUpdate() != null));
clients.forEach((expectedDelay, wsClient) -> { clients.forEach((expectedDelay, wsClient) -> {
Notification notification = wsClient.getLastDataUpdate().getUpdate(); Notification notification = wsClient.getLastDataUpdate().getUpdate();
@ -291,8 +291,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
alarm.setOriginator(device.getId()); alarm.setOriginator(device.getId());
alarm = doPost("/api/alarm", alarm, Alarm.class); alarm = doPost("/api/alarm", alarm, Alarm.class);
await().atMost(15, TimeUnit.SECONDS) await().atMost(TIMEOUT, TimeUnit.SECONDS)
.pollDelay(2, TimeUnit.SECONDS) .pollDelay(1, TimeUnit.SECONDS)
.untilAsserted(() -> { .untilAsserted(() -> {
List<Notification> notifications = getMyNotifications(false, 10); List<Notification> notifications = getMyNotifications(false, 10);
assertThat(notifications).singleElement().matches(notification -> { assertThat(notifications).singleElement().matches(notification -> {
@ -345,7 +345,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.set("createAlarm", BooleanNode.TRUE); .set("createAlarm", BooleanNode.TRUE);
postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString()); postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString());
await().atMost(10, TimeUnit.SECONDS) await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null); .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null);
Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType); Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType);
getWsClient().waitForUpdate(true); getWsClient().waitForUpdate(true);
@ -355,14 +355,14 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
assertThat(notification.getInfo()).asInstanceOf(type(AlarmNotificationInfo.class)) assertThat(notification.getInfo()).asInstanceOf(type(AlarmNotificationInfo.class))
.extracting(AlarmNotificationInfo::getAlarmId).isEqualTo(alarm.getUuidId()); .extracting(AlarmNotificationInfo::getAlarmId).isEqualTo(alarm.getUuidId());
await().atMost(10, TimeUnit.SECONDS).until(() -> findNotificationRequests(EntityType.ALARM).getTotalElements() == escalationTable.size()); await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> findNotificationRequests(EntityType.ALARM).getTotalElements() == escalationTable.size());
NotificationRequestInfo scheduledNotificationRequest = findNotificationRequests(EntityType.ALARM).getData().stream() NotificationRequestInfo scheduledNotificationRequest = findNotificationRequests(EntityType.ALARM).getData().stream()
.filter(NotificationRequest::isScheduled) .filter(NotificationRequest::isScheduled)
.findFirst().orElse(null); .findFirst().orElse(null);
assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo()); assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo());
alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null); alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null);
await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> {
assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty(); assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty();
}); });
} }
@ -480,7 +480,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
} }
loginTenantAdmin(); loginTenantAdmin();
List<Notification> notifications = await().atMost(15, TimeUnit.SECONDS) List<Notification> notifications = await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> getMyNotifications(true, 10).stream() .until(() -> getMyNotifications(true, 10).stream()
.filter(notification -> notification.getType() == NotificationType.RATE_LIMITS) .filter(notification -> notification.getType() == NotificationType.RATE_LIMITS)
.collect(Collectors.toList()), list -> list.size() == 3); .collect(Collectors.toList()), list -> list.size() == 3);
@ -500,7 +500,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
}); });
loginSysAdmin(); loginSysAdmin();
notifications = await().atMost(15, TimeUnit.SECONDS) notifications = await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> getMyNotifications(true, 10).stream() .until(() -> getMyNotifications(true, 10).stream()
.filter(notification -> notification.getType() == NotificationType.RATE_LIMITS) .filter(notification -> notification.getType() == NotificationType.RATE_LIMITS)
.collect(Collectors.toList()), list -> list.size() == 1); .collect(Collectors.toList()), list -> list.size() == 1);
@ -721,7 +721,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.updateInfo(new UpdateMessage(true, "CHANGED", "test", .updateInfo(new UpdateMessage(true, "CHANGED", "test",
"test", "test", "test")) "test", "test", "test"))
.build()); .build());
await().atMost(5, TimeUnit.SECONDS) await().atMost(TIMEOUT, TimeUnit.SECONDS)
.untilAsserted(() -> { .untilAsserted(() -> {
assertThat(getMyNotifications(false, 100)).size().isEqualTo(2); assertThat(getMyNotifications(false, 100)).size().isEqualTo(2);
}); });
@ -809,7 +809,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
method.invoke(systemInfoService); method.invoke(systemInfoService);
} }
await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
Notification notification = getMyNotifications(false, 100).get(0); Notification notification = getMyNotifications(false, 100).get(0);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage"); assertThat(notification.getText()).isEqualTo("RAM shortage");
@ -836,7 +836,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.build()); .build());
TimeUnit.MILLISECONDS.sleep(300); TimeUnit.MILLISECONDS.sleep(300);
} }
await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
Notification notification = getMyNotifications(false, 100).get(0); Notification notification = getMyNotifications(false, 100).get(0);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage"); assertThat(notification.getText()).isEqualTo("RAM shortage");
@ -848,7 +848,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.serviceType("serviceType") .serviceType("serviceType")
.serviceId("serviceId") .serviceId("serviceId")
.build()); .build());
await("").atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne()); await("").atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne());
} }
@Test @Test
@ -887,7 +887,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
method.invoke(systemInfoService); method.invoke(systemInfoService);
await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
Notification notification = getMyNotifications(false, 100).get(0); Notification notification = getMyNotifications(false, 100).get(0);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage"); assertThat(notification.getText()).isEqualTo("RAM shortage");

5
application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java

@ -28,10 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
@Slf4j @Slf4j
public abstract class AbstractTransportIntegrationTest extends AbstractControllerTest { public abstract class AbstractTransportIntegrationTest extends AbstractControllerTest {
protected static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30; public static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30;
protected static final String MQTT_URL = "tcp://localhost:1883";
protected static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/";
protected static final AtomicInteger atomicInteger = new AtomicInteger(2); protected static final AtomicInteger atomicInteger = new AtomicInteger(2);

14
application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java

@ -16,7 +16,10 @@
package org.thingsboard.server.transport.coap; package org.thingsboard.server.transport.coap;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.CoapDeviceType;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
@ -40,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.transport.AbstractTransportIntegrationTest; import org.thingsboard.server.transport.AbstractTransportIntegrationTest;
import org.thingsboard.server.utils.PortFinder;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -52,6 +56,16 @@ import static org.junit.Assert.assertNotNull;
@Slf4j @Slf4j
public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest { public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest {
public static final String COAP_HOST = "localhost";
public static final int COAP_PORT = PortFinder.findAvailableUdpPort();
public static final String COAP_BASE_URL = "coap://" + COAP_HOST + ":" + COAP_PORT + "/api/v1/";
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("coap.bind_port = {}", COAP_PORT);
registry.add("coap.bind_port", () -> COAP_PORT);
}
protected final byte[] EMPTY_PAYLOAD = new byte[0]; protected final byte[] EMPTY_PAYLOAD = new byte[0];
protected CoapTestClient client; protected CoapTestClient client;
protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," +

3
application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java

@ -28,9 +28,10 @@ import org.thingsboard.server.common.msg.session.FeatureType;
import java.io.IOException; import java.io.IOException;
import static org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest.COAP_BASE_URL;
public class CoapTestClient { public class CoapTestClient {
private static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/";
private static final long CLIENT_REQUEST_TIMEOUT = 60000L; private static final long CLIENT_REQUEST_TIMEOUT = 60000L;
private final CoapClient client; private final CoapClient client;

37
application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java

@ -21,6 +21,8 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResponse; import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.CoAP;
import org.junit.Assert; import org.junit.Assert;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.CoapDeviceType;
@ -37,10 +39,10 @@ import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest;
import org.thingsboard.server.transport.coap.x509.CertPrivateKey; import org.thingsboard.server.transport.coap.x509.CertPrivateKey;
import org.thingsboard.server.transport.coap.x509.CoapClientX509Test; import org.thingsboard.server.transport.coap.x509.CoapClientX509Test;
import org.thingsboard.server.transport.coap.CoapTestConfigProperties; import org.thingsboard.server.transport.coap.CoapTestConfigProperties;
import org.thingsboard.server.utils.PortFinder;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.ServerSocket;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.PrivateKey; import java.security.PrivateKey;
@ -51,9 +53,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -70,7 +70,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
"transport.coap.enabled=true", "transport.coap.enabled=true",
}) })
public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIntegrationTest { public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIntegrationTest {
private static final String COAPS_BASE_URL = "coaps://localhost:5684/api/v1/";
public static final String COAPS_HOST = "localhost";
public static final int COAPS_PORT = PortFinder.findAvailableUdpPort();
public static final String COAPS_BASE_URL = "coaps://" + COAPS_HOST + ":" + COAPS_PORT + "/api/v1/";
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("coap.dtls.bind_port = {}", COAPS_PORT);
registry.add("coap.dtls.bind_port", () -> COAPS_PORT);
}
protected final String CREDENTIALS_PATH = "coap/credentials/"; protected final String CREDENTIALS_PATH = "coap/credentials/";
protected final String CREDENTIALS_PATH_CLIENT = CREDENTIALS_PATH + "client/"; protected final String CREDENTIALS_PATH_CLIENT = CREDENTIALS_PATH + "client/";
protected final String CREDENTIALS_PATH_CLIENT_CERT_PEM = CREDENTIALS_PATH_CLIENT + "cert.pem"; protected final String CREDENTIALS_PATH_CLIENT_CERT_PEM = CREDENTIALS_PATH_CLIENT + "cert.pem";
@ -160,13 +170,10 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn
CertPrivateKey certPrivateKey = new CertPrivateKey(CREDENTIALS_PATH_CLIENT_CERT_PEM, CREDENTIALS_PATH_CLIENT_KEY_PEM); CertPrivateKey certPrivateKey = new CertPrivateKey(CREDENTIALS_PATH_CLIENT_CERT_PEM, CREDENTIALS_PATH_CLIENT_KEY_PEM);
CertPrivateKey certPrivateKey_01 = new CertPrivateKey(CREDENTIALS_PATH_CLIENT + "cert_01.pem", CertPrivateKey certPrivateKey_01 = new CertPrivateKey(CREDENTIALS_PATH_CLIENT + "cert_01.pem",
CREDENTIALS_PATH_CLIENT + "key_01.pem"); CREDENTIALS_PATH_CLIENT + "key_01.pem");
Integer fixedPort = getFreePort(); int fixedPort = PortFinder.findAvailableUdpPort();
CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey, CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey,
"CoapX509TrustNo_" + FeatureType.TELEMETRY.name(), deviceProfile.getId(), fixedPort); "CoapX509TrustNo_" + FeatureType.TELEMETRY.name(), deviceProfile.getId(), fixedPort);
clientX509.disconnect(); clientX509.disconnect();
await("Need to make port " + fixedPort + " free")
.atMost(40, TimeUnit.SECONDS)
.until(() -> isPortAvailable(fixedPort));
CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01, CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01,
"CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(), "CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(),
fixedPort, PAYLOAD_VALUES_STR_01); fixedPort, PAYLOAD_VALUES_STR_01);
@ -274,19 +281,5 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn
} }
} }
private static int getFreePort() throws IOException {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
private static boolean isPortAvailable(int port) {
try (ServerSocket serverSocket = new ServerSocket(port)) {
serverSocket.setReuseAddress(true);
return true;
} catch (IOException e) {
return false;
}
}
} }

14
application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java

@ -51,6 +51,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.awaitility.Awaitility.await;
import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS; import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CIPHER_SUITES; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CIPHER_SUITES;
@ -71,6 +72,8 @@ import static org.eclipse.californium.scandium.config.DtlsConfig.MODULE;
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_ECDSA; import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_ECDSA;
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_RSA; import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_RSA;
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA384_WITH_ECDSA; import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA384_WITH_ECDSA;
import static org.thingsboard.server.transport.AbstractTransportIntegrationTest.DEFAULT_WAIT_TIMEOUT_SECONDS;
import static org.thingsboard.server.utils.PortFinder.isUDPPortAvailable;
@Slf4j @Slf4j
public class CoapClientX509Test { public class CoapClientX509Test {
@ -82,6 +85,7 @@ public class CoapClientX509Test {
private final Configuration config; private final Configuration config;
private final CertPrivateKey certPrivateKey; private final CertPrivateKey certPrivateKey;
private final String coapsBaseUrl; private final String coapsBaseUrl;
private final Integer fixedPort;
@Getter @Getter
private CoAP.Type type = CoAP.Type.CON; private CoAP.Type type = CoAP.Type.CON;
@ -90,6 +94,7 @@ public class CoapClientX509Test {
this.certPrivateKey = certPrivateKey; this.certPrivateKey = certPrivateKey;
this.coapsBaseUrl = coapsBaseUrl; this.coapsBaseUrl = coapsBaseUrl;
this.config = createConfiguration(); this.config = createConfiguration();
this.fixedPort = fixedPort;
this.dtlsConnector = createDTLSConnector(fixedPort); this.dtlsConnector = createDTLSConnector(fixedPort);
this.clientX509 = createClient(getFeatureTokenUrl(featureType)); this.clientX509 = createClient(getFeatureTokenUrl(featureType));
} }
@ -97,6 +102,15 @@ public class CoapClientX509Test {
if (clientX509 != null) { if (clientX509 != null) {
clientX509.shutdown(); clientX509.shutdown();
} }
if (dtlsConnector != null) {
dtlsConnector.destroy();
if (fixedPort != null) {
log.debug("Awaiting releasing UDP fixedPort {}", fixedPort);
await("Await client UDP port " + fixedPort + " to disconnect")
.atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.until(() -> isUDPPortAvailable(fixedPort));
}
}
} }
public CoapResponse postMethod(String requestBody) throws ConnectorException, IOException { public CoapResponse postMethod(String requestBody) throws ConnectorException, IOException {

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

@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import jnr.ffi.annotations.In;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.awaitility.core.ConditionTimeoutException; import org.awaitility.core.ConditionTimeoutException;
@ -38,6 +39,8 @@ import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
@ -84,9 +87,8 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.ResourceUpdateResult; import org.thingsboard.server.transport.lwm2m.server.client.ResourceUpdateResult;
import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2mUplinkMsgHandler;
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
import org.thingsboard.server.utils.PortFinder;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -141,21 +143,35 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
private LwM2mClientContext clientContextTest; private LwM2mClientContext clientContextTest;
// Lwm2m Server // Lwm2m Server
public static final int port = 5685; public static final String LWM2M_HOST = "localhost";
public static final int securityPort = 5686; public static final int LWM2M_PORT = PortFinder.findAvailableUdpPort(); // 5685
public static final int portBs = 5687; public static final int LWM2MS_PORT = PortFinder.findAvailableUdpPort(); //5686
public static final int securityPortBs = 5688;
public static final String LWM2M_BOOTSTRAP_HOST = "localhost";
public static final int LWM2M_BOOTSTRAP_PORT = PortFinder.findAvailableUdpPort(); // 5687
public static final int LWM2MS_BOOTSTRAP_PORT = PortFinder.findAvailableUdpPort(); // 5688
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("transport.lwm2m.server.bind_port = {}", LWM2M_PORT);
registry.add("transport.lwm2m.server.bind_port", () -> LWM2M_PORT);
log.warn("transport.lwm2m.server.security.bind_port = {}", LWM2MS_PORT);
registry.add("transport.lwm2m.server.security.bind_port", () -> LWM2MS_PORT);
log.warn("transport.lwm2m.bootstrap.bind_port = {}", LWM2M_BOOTSTRAP_PORT);
registry.add("transport.lwm2m.bootstrap.bind_port", () -> LWM2M_BOOTSTRAP_PORT);
log.warn("transport.lwm2m.bootstrap.security.bind_port = {}", LWM2MS_BOOTSTRAP_PORT);
registry.add("transport.lwm2m.bootstrap.security.bind_port", () -> LWM2MS_BOOTSTRAP_PORT);
}
public static final String host = "localhost";
public static final String hostBs = "localhost";
public static final Integer shortServerId = 123; public static final Integer shortServerId = 123;
public static final String COAP = "coap://"; public static final String COAP = "coap://";
public static final String COAPS = "coaps://"; public static final String COAPS = "coaps://";
public static final String URI = COAP + host + ":" + port; public static final String URI = COAP + LWM2M_HOST + ":" + LWM2M_PORT;
public static final String SECURE_URI = COAPS + host + ":" + securityPort; public static final String SECURE_URI = COAPS + LWM2M_HOST + ":" + LWM2MS_PORT;
public static final String URI_BS = COAP + hostBs + ":" + portBs; public static final String URI_BS = COAP + LWM2M_BOOTSTRAP_HOST + ":" + LWM2M_BOOTSTRAP_PORT;
public static final String SECURE_URI_BS = COAPS + hostBs + ":" + securityPortBs; public static final String SECURE_URI_BS = COAPS + LWM2M_BOOTSTRAP_HOST + ":" + LWM2MS_BOOTSTRAP_PORT;
public static final Security SECURITY_NO_SEC = noSec(URI, shortServerId); public static final Security SECURITY_NO_SEC = noSec(URI, shortServerId);
protected final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS = protected final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS =
@ -570,12 +586,11 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
this.clientDestroy(); this.clientDestroy();
lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint, resources); lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint, resources);
try (ServerSocket socket = new ServerSocket(0)) { int clientPort = PortFinder.findAvailableUdpPort();
int clientPort = socket.getLocalPort(); //automatic client port assignment
lwM2MTestClient.init(security, securityBs, clientPort, isRpc, lwM2MTestClient.init(security, securityBs, 0 /* clientPort */, isRpc,
this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest, this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest,
clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR, value3_0_9); clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR, value3_0_9);
}
lwM2MTestClient.setDeviceIdStr(deviceIdStr); lwM2MTestClient.setDeviceIdStr(deviceIdStr);
} }
@ -658,10 +673,9 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
try { try {
if (lwM2MTestClient != null && lwM2MTestClient.getLeshanClient() != null) { if (lwM2MTestClient != null && lwM2MTestClient.getLeshanClient() != null) {
boolean serverAlive = false; boolean serverAlive = false;
for (int port = AbstractLwM2MIntegrationTest.port; port <= securityPortBs; port++) { List<Integer> ports = List.of(LWM2M_PORT, LWM2MS_PORT, LWM2MS_BOOTSTRAP_PORT, LWM2MS_BOOTSTRAP_PORT);
try (ServerSocket socket = new ServerSocket(port)) { for (Integer port : ports) {
log.info("Port {} is free.", port); if (!PortFinder.isUDPPortAvailable(port)) {
} catch (IOException e) {
log.debug("Port {} is busy — CoAP server still active.", port); log.debug("Port {} is busy — CoAP server still active.", port);
serverAlive = true; serverAlive = true;
break; break;
@ -730,8 +744,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
bootstrapServerCredential.setServerPublicKey(""); bootstrapServerCredential.setServerPublicKey("");
bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId); bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap); bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host); bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST);
bootstrapServerCredential.setPort(isBootstrap ? portBs : port); bootstrapServerCredential.setPort(isBootstrap ? LWM2M_BOOTSTRAP_PORT : LWM2M_PORT);
return bootstrapServerCredential; return bootstrapServerCredential;
} }
@ -746,11 +760,15 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
return credentials; return credentials;
} }
protected void awaitObserveReadAll(int cntObserve, String deviceIdStr) throws Exception { protected void awaitObserveReadAll(int cntObserve, String deviceIdStr) throws Exception {
awaitObserveReadAll(cntObserve, deviceIdStr, "");
}
protected void awaitObserveReadAll(int cntObserve, String deviceIdStr, String assertionAlias) throws Exception {
try { try {
await("ObserveReadAll: countObserve " + cntObserve) await("ObserveReadAll: countObserve " + cntObserve)
.atMost(40, TimeUnit.SECONDS) .alias(assertionAlias)
.atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> cntObserve == getCntObserveAll(deviceIdStr)); .until(() -> cntObserve == getCntObserveAll(deviceIdStr));
} catch (ConditionTimeoutException e) { } catch (ConditionTimeoutException e) {
int current = getCntObserveAll(deviceIdStr); int current = getCntObserveAll(deviceIdStr);

2
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java

@ -33,7 +33,6 @@ import java.util.List;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicInteger;
import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TARGET_FW_VERSION; import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TARGET_FW_VERSION;
import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TITLE; import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TITLE;
@ -137,6 +136,7 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
@Override @Override
public void destroy() { public void destroy() {
scheduler.shutdownNow(); scheduler.shutdownNow();
this.leshanClient = null;
} }
private void startDownloading() { private void startDownloading() {

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

@ -33,7 +33,6 @@ import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.client.object.Server; import org.eclipse.leshan.client.object.Server;
import org.eclipse.leshan.client.observer.LwM2mClientObserver; import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.resource.DummyInstanceEnabler; import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler; import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.resource.ObjectsInitializer; import org.eclipse.leshan.client.resource.ObjectsInitializer;
import org.eclipse.leshan.client.resource.listener.ObjectsListenerAdapter; import org.eclipse.leshan.client.resource.listener.ObjectsListenerAdapter;
@ -82,6 +81,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.awaitility.Awaitility.await;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY;
import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL;
@ -92,6 +92,7 @@ import static org.eclipse.leshan.core.LwM2mId.SECURITY;
import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SERVER;
import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT;
import static org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder.getDefaultPathEncoder; import static org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder.getDefaultPathEncoder;
import static org.thingsboard.server.transport.AbstractTransportIntegrationTest.DEFAULT_WAIT_TIMEOUT_SECONDS;
import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.shortServerId; import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.shortServerId;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState;
@ -119,6 +120,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INST
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.lwm2mClientResources; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.lwm2mClientResources;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.setDtlsConnectorConfigCidLength; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.setDtlsConnectorConfigCidLength;
import static org.thingsboard.server.utils.PortFinder.isUDPPortAvailable;
@Slf4j @Slf4j
@ -139,14 +141,16 @@ public class LwM2MTestClient {
private Map<LwM2MClientState, Integer> clientDtlsCid; private Map<LwM2MClientState, Integer> clientDtlsCid;
private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest; private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest;
private LwM2mClientContext clientContext; private LwM2mClientContext clientContext;
private LwM2mTemperatureSensor lwM2mTemperatureSensor12; private LwM2mTemperatureSensor lwM2MTemperatureSensor12;
private String deviceIdStr; private String deviceIdStr;
private int clientPort;
public void init(Security securityLwm2m, Security securityBs, int port, boolean isRpc, public void init(Security securityLwm2m, Security securityBs, int clientPort, boolean isRpc,
LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler, LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler,
LwM2mClientContext clientContext, Integer cIdLength, boolean queueMode, LwM2mClientContext clientContext, Integer cIdLength, boolean queueMode,
boolean supportFormatOnly_SenMLJSON_SenMLCBOR, Integer value3_0_9) throws InvalidDDFFileException, IOException { boolean supportFormatOnly_SenMLJSON_SenMLCBOR, Integer value3_0_9) throws InvalidDDFFileException, IOException {
Assert.assertNull("client already initialized", leshanClient); Assert.assertNull("client already initialized", leshanClient);
this.clientPort = clientPort;
this.defaultLwM2mUplinkMsgHandlerTest = defaultLwM2mUplinkMsgHandler; this.defaultLwM2mUplinkMsgHandlerTest = defaultLwM2mUplinkMsgHandler;
this.clientContext = clientContext; this.clientContext = clientContext;
@ -156,7 +160,7 @@ public class LwM2MTestClient {
if (securityLwm2m != null && securityLwm2m.getId() != null) { if (securityLwm2m != null && securityLwm2m.getId() != null) {
forceNullSecurityId(securityLwm2m); forceNullSecurityId(securityLwm2m);
} }
if (securityBs!= null && securityBs.getId() != null) { if (securityBs != null && securityBs.getId() != null) {
forceNullSecurityId(securityBs); forceNullSecurityId(securityBs);
} }
if (securityBs != null && securityLwm2m != null) { if (securityBs != null && securityLwm2m != null) {
@ -165,7 +169,7 @@ public class LwM2MTestClient {
} else if (securityBs != null) { } else if (securityBs != null) {
log.warn("Security BS only: securityBs: [{}] ", securityBs.getId()); log.warn("Security BS only: securityBs: [{}] ", securityBs.getId());
initializer.setInstancesForObject(SECURITY, securityBs); initializer.setInstancesForObject(SECURITY, securityBs);
} else if (securityLwm2m != null){ } else if (securityLwm2m != null) {
// SECURITY // SECURITY
log.warn("Security Lwm2m only: security Lwm2m [{}]", securityLwm2m.getId()); log.warn("Security Lwm2m only: security Lwm2m [{}]", securityLwm2m.getId());
initializer.setInstancesForObject(SECURITY, securityLwm2m); initializer.setInstancesForObject(SECURITY, securityLwm2m);
@ -186,8 +190,8 @@ public class LwM2MTestClient {
locationParams.getPos(); locationParams.getPos();
initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0)); initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0));
LwM2mTemperatureSensor lwM2mTemperatureSensor0 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0); LwM2mTemperatureSensor lwM2mTemperatureSensor0 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0);
lwM2mTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12); lwM2MTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12);
initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2mTemperatureSensor12); initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2MTemperatureSensor12);
List<LwM2mObjectEnabler> enablers = initializer.createAll(); List<LwM2mObjectEnabler> enablers = initializer.createAll();
@ -208,7 +212,9 @@ public class LwM2MTestClient {
builder.setSessionListener(new DtlsSessionLogger(clientStates, clientDtlsCid)); builder.setSessionListener(new DtlsSessionLogger(clientStates, clientDtlsCid));
return builder; return builder;
}; }
;
}; };
} }
}; };
@ -245,7 +251,7 @@ public class LwM2MTestClient {
// Set Californium Configuration // Set Californium Configuration
endpointsBuilder.setConfiguration(clientCoapConfig); endpointsBuilder.setConfiguration(clientCoapConfig);
endpointsBuilder.setClientAddress(new InetSocketAddress(port).getAddress()); endpointsBuilder.setClientAddress(new InetSocketAddress(clientPort).getAddress());
// creates EndpointsProvider // creates EndpointsProvider
@ -438,12 +444,14 @@ public class LwM2MTestClient {
destroySafe(swLwM2MDevice); destroySafe(swLwM2MDevice);
destroySafe(lwM2MBinaryAppDataContainer); destroySafe(lwM2MBinaryAppDataContainer);
destroySafe(lwM2MTemperatureSensor); destroySafe(lwM2MTemperatureSensor);
destroySafe(lwM2MTemperatureSensor12);
lwM2MDevice = null; lwM2MDevice = null;
fwLwM2MDevice = null; fwLwM2MDevice = null;
swLwM2MDevice = null; swLwM2MDevice = null;
lwM2MBinaryAppDataContainer = null; lwM2MBinaryAppDataContainer = null;
lwM2MTemperatureSensor = null; lwM2MTemperatureSensor = null;
lwM2MTemperatureSensor12 = null;
} }
@ -462,11 +470,17 @@ public class LwM2MTestClient {
public void start(boolean isStartLw) { public void start(boolean isStartLw) {
if (leshanClient != null) { if (leshanClient != null) {
if (clientPort > 0) {
log.error("Await UDP clientPort {} to be available before leshanClient.start()", clientPort);
await("Await UDP clientPort " + clientPort + " to be available before leshanClient.start()")
.atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.until(() -> isUDPPortAvailable(clientPort));
}
leshanClient.start(); leshanClient.start();
if (isStartLw) { if (isStartLw) {
this.awaitClientAfterStartConnectLw(); this.awaitClientAfterStartConnectLw();
} }
lwM2mTemperatureSensor12.setLeshanClient(leshanClient); lwM2MTemperatureSensor12.setLeshanClient(leshanClient);
fwLwM2MDevice.setLeshanClient(leshanClient); fwLwM2MDevice.setLeshanClient(leshanClient);
} }
} }
@ -474,6 +488,12 @@ public class LwM2MTestClient {
public void stop(boolean deregister) { public void stop(boolean deregister) {
if (leshanClient != null) { if (leshanClient != null) {
leshanClient.stop(deregister); leshanClient.stop(deregister);
if (clientPort > 0) {
log.error("Await UDP clientPort {} to disconnect after leshanClient.stop(deregister)", clientPort);
await("Await client UDP port " + clientPort + " to disconnect after leshanClient.stop(deregister)")
.atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.until(() -> isUDPPortAvailable(clientPort));
}
} }
} }
@ -535,5 +555,6 @@ public class LwM2MTestClient {
log.error("[forceNullSecurityId] Failed to set id=null for {}", security.getClass(), e); log.error("[forceNullSecurityId] Failed to set id=null for {}", security.getClass(), e);
} }
} }
} }

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

@ -169,6 +169,7 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr
@Override @Override
public void destroy() { public void destroy() {
this.leshanClient = null;
} }
private void sendCollected() { private void sendCollected() {

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

@ -366,8 +366,8 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
} }
bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId); bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap); bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host); bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST);
bootstrapServerCredential.setPort(isBootstrap ? securityPortBs : securityPort); bootstrapServerCredential.setPort(isBootstrap ? LWM2MS_BOOTSTRAP_PORT : LWM2MS_PORT);
return bootstrapServerCredential; return bootstrapServerCredential;
} }

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

@ -133,7 +133,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); true);
awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString()); awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString(), "before client stops for the first time");
lwM2MTestClient.stop(true); lwM2MTestClient.stop(true);
DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class); DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class);
@ -143,7 +143,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
Assert.assertNotNull(lwm2mDeviceProfileManyParams); Assert.assertNotNull(lwm2mDeviceProfileManyParams);
lwM2MTestClient.start(true); lwM2MTestClient.start(true);
awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString()); awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString(), "second after client restart");
awaitUpdateReg(3); awaitUpdateReg(3);
} }

13
application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java

@ -19,7 +19,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
@ -58,6 +61,16 @@ import static org.junit.Assert.assertNotNull;
@Slf4j @Slf4j
public abstract class AbstractMqttIntegrationTest extends AbstractTransportIntegrationTest { public abstract class AbstractMqttIntegrationTest extends AbstractTransportIntegrationTest {
public static final String MQTT_HOST = "localhost";
public static final int MQTT_PORT = TestSocketUtils.findAvailableTcpPort();
public static final String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
}
protected Device savedGateway; protected Device savedGateway;
protected String gatewayAccessToken; protected String gatewayAccessToken;

10
application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java

@ -16,12 +16,15 @@
package org.thingsboard.server.transport.mqtt; package org.thingsboard.server.transport.mqtt;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
@ -43,13 +46,20 @@ import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY; import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY;
import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY_DEVICE; import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY_DEVICE;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
@DaoSqlTest @DaoSqlTest
@TestPropertySource(properties = { @TestPropertySource(properties = {
"service.integrations.supported=ALL", "service.integrations.supported=ALL",
"transport.mqtt.enabled=true", "transport.mqtt.enabled=true",
}) })
@Slf4j
public class MqttGatewayRateLimitsTest extends AbstractControllerTest { public class MqttGatewayRateLimitsTest extends AbstractControllerTest {
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
}
private static final String GATEWAY_TOPIC = "v1/gateway/telemetry"; private static final String GATEWAY_TOPIC = "v1/gateway/telemetry";
private static final String DEVICE_TOPIC = "v1/devices/me/telemetry"; private static final String DEVICE_TOPIC = "v1/devices/me/telemetry";

6
application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java

@ -27,12 +27,16 @@ import org.thingsboard.server.common.data.StringUtils;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_HOST;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
public class MqttTestClient { public class MqttTestClient {
private static final String MQTT_URL = "tcp://localhost:1883";
private static final int TIMEOUT = 30; // seconds private static final int TIMEOUT = 30; // seconds
public static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT); public static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT);
private String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
private final MqttAsyncClient client; private final MqttAsyncClient client;
public void setCallback(MqttTestCallback callback) { public void setCallback(MqttTestCallback callback) {

6
application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java

@ -27,12 +27,16 @@ import org.thingsboard.server.common.data.StringUtils;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_HOST;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
public class MqttV5TestClient { // We should copy part of MqttV3TestClient, due to different package names in import public class MqttV5TestClient { // We should copy part of MqttV3TestClient, due to different package names in import
private static final String MQTT_URL = "tcp://localhost:1883";
private static final int TIMEOUT = 30; // seconds private static final int TIMEOUT = 30; // seconds
private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT); private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT);
private String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
private final MqttAsyncClient client; private final MqttAsyncClient client;
public void setCallback(MqttCallback callback) { public void setCallback(MqttCallback callback) {

42
application/src/test/java/org/thingsboard/server/utils/PortFinder.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.utils;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.SocketException;
@Slf4j
public class PortFinder {
public static int findAvailableUdpPort() {
try (DatagramSocket socket = new DatagramSocket(0)) {
return socket.getLocalPort();
} catch (SocketException e) {
throw new IllegalStateException("No available UDP ports found", e);
}
}
public static boolean isUDPPortAvailable(int port) {
try (DatagramSocket socket = new DatagramSocket(port)) {
return true;
} catch (IOException e) {
log.debug("Failed to open UDP port {}", port, e);
return false;
}
}
}

1
application/src/test/resources/application-test.properties

@ -1,3 +1,4 @@
spring.main.banner-mode=off
js.evaluator=mock js.evaluator=mock
transport.lwm2m.server.security.credentials.enabled=true transport.lwm2m.server.security.credentials.enabled=true
transport.lwm2m.server.security.credentials.type=KEYSTORE transport.lwm2m.server.security.credentials.type=KEYSTORE

6
application/src/test/resources/logback-test.xml

@ -42,6 +42,12 @@
<!-- <logger name="org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mRedisRegistrationStore" level="TRACE"/>--> <!-- <logger name="org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mRedisRegistrationStore" level="TRACE"/>-->
<!-- <logger name="org.thingsboard.server.transport.lwm2m.security.diffPort" level="TRACE"/>--> <!-- <logger name="org.thingsboard.server.transport.lwm2m.security.diffPort" level="TRACE"/>-->
<!-- Avoiding multiple logs like: The cache 'relations' is not recording statistics. No meters except 'cache.size' will be registered. Call 'Caffeine#recordStats()' prior to building the cache for metrics to be recorded.-->
<logger name="io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics" level="ERROR"/>
<!-- Log MvcMock result with status >=400 -->
<logger name="org.springframework.test.web.servlet.result" level="DEBUG"/>
<root level="WARN"> <root level="WARN">
<appender-ref ref="console"/> <appender-ref ref="console"/>
</root> </root>

2
common/actor/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/cache/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

3
common/cache/src/test/resources/logback-test.xml

@ -9,6 +9,9 @@
<logger name="org.thingsboard.server.cache" level="TRACE"/> <logger name="org.thingsboard.server.cache" level="TRACE"/>
<!-- Avoiding multiple logs like: The cache 'relations' is not recording statistics. No meters except 'cache.size' will be registered. Call 'Caffeine#recordStats()' prior to building the cache for metrics to be recorded.-->
<logger name="io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics" level="ERROR"/>
<root level="INFO"> <root level="INFO">
<appender-ref ref="console"/> <appender-ref ref="console"/>
</root> </root>

2
common/cluster-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/coap-server/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/dao-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/data/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/discovery-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/edge-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/edqs/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

3
common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java

@ -16,6 +16,7 @@
package org.thingsboard.server.edqs.util; package org.thingsboard.server.edqs.util;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.rocksdb.Options; import org.rocksdb.Options;
import org.rocksdb.RocksDB; import org.rocksdb.RocksDB;
import org.rocksdb.RocksIterator; import org.rocksdb.RocksIterator;
@ -26,6 +27,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@Slf4j
public class TbRocksDb { public class TbRocksDb {
protected final String path; protected final String path;
@ -45,6 +47,7 @@ public class TbRocksDb {
@SneakyThrows @SneakyThrows
public void init() { public void init() {
log.debug("RocksDB init in {}", path);
Files.createDirectories(Path.of(path).getParent()); Files.createDirectories(Path.of(path).getParent());
db = RocksDB.open(dbOptions, path); db = RocksDB.open(dbOptions, path);
} }

2
common/message/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>common</artifactId> <artifactId>common</artifactId>

2
common/proto/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/queue/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/script/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/script/remote-js-client/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>script</artifactId> <artifactId>script</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.script</groupId> <groupId>org.thingsboard.common.script</groupId>

2
common/script/script-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>script</artifactId> <artifactId>script</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.script</groupId> <groupId>org.thingsboard.common.script</groupId>

2
common/stats/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/lwm2m/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/transport/snmp/pom.xml

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>

2
common/transport/transport-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common.transport</groupId> <groupId>org.thingsboard.common.transport</groupId>

2
common/util/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
common/version-control/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>common</artifactId> <artifactId>common</artifactId>
</parent> </parent>
<groupId>org.thingsboard.common</groupId> <groupId>org.thingsboard.common</groupId>

2
dao/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>dao</artifactId> <artifactId>dao</artifactId>

7
dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java

@ -19,6 +19,7 @@ import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.FluentFuture;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -55,6 +56,12 @@ public class DefaultTbResourceDataCache implements TbResourceDataCache {
.buildAsync((key, executor) -> CompletableFuture.supplyAsync(() -> resourceService.getResourceDataInfo(key.tenantId(), key.resourceId()), executor)); .buildAsync((key, executor) -> CompletableFuture.supplyAsync(() -> resourceService.getResourceDataInfo(key.tenantId(), key.resourceId()), executor));
} }
@PreDestroy
private void destroy() {
cache.synchronous().invalidateAll();
cache = null;
}
@Override @Override
public FluentFuture<TbResourceDataInfo> getResourceDataInfoAsync(TenantId tenantId, TbResourceId resourceId) { public FluentFuture<TbResourceDataInfo> getResourceDataInfoAsync(TenantId tenantId, TbResourceId resourceId) {
log.trace("Retrieving resource data info by id [{}], tenant id [{}] from cache", resourceId, tenantId); log.trace("Retrieving resource data info by id [{}], tenant id [{}] from cache", resourceId, tenantId);

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

@ -100,6 +100,18 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
@Autowired @Autowired
private DashboardService dashboardService; private DashboardService dashboardService;
@Value("${transport.lwm2m.server.bind_port:5685}")
private Integer lwm2mPort;
@Value("${transport.lwm2m.server.security.bind_port:5686}")
private Integer lwm2mSecurePort;
@Value("${transport.lwm2m.bootstrap.bind_port:5687}")
private Integer lwm2mBootstrapPort;
@Value("${transport.lwm2m.bootstrap.security.bind_port:5688}")
private Integer lwm2mBootstrapSecurePort;
@Value("${security.java_cacerts.path:}") @Value("${security.java_cacerts.path:}")
private String javaCacertsPath; private String javaCacertsPath;
@ -371,9 +383,9 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
} }
int port; int port;
if (LwM2MSecurityMode.NO_SEC.equals(serverConfig.getSecurityMode())) { if (LwM2MSecurityMode.NO_SEC.equals(serverConfig.getSecurityMode())) {
port = serverConfig.isBootstrapServerIs() ? 5687 : 5685; port = serverConfig.isBootstrapServerIs() ? lwm2mBootstrapPort : lwm2mPort;
} else { } else {
port = serverConfig.isBootstrapServerIs() ? 5688 : 5686; port = serverConfig.isBootstrapServerIs() ? lwm2mBootstrapSecurePort : lwm2mSecurePort;
} }
if (serverConfig.getPort() == null || serverConfig.getPort() != port) { if (serverConfig.getPort() == null || serverConfig.getPort() != port) {
throw new DeviceCredentialsValidationException(server + " \"Port\" value = " + serverConfig.getPort() + ". This value for security " + serverConfig.getSecurityMode().name() + " must be " + port + "!"); throw new DeviceCredentialsValidationException(server + " \"Port\" value = " + serverConfig.getPort() + ". This value for security " + serverConfig.getSecurityMode().name() + " must be " + port + "!");

3
dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeAssetRepository.java

@ -50,7 +50,8 @@ public class DefaultNativeAssetRepository extends AbstractNativeRepository imple
@Override @Override
public PageData<ProfileEntityIdInfo> findProfileEntityIdInfosByTenantId(UUID tenantId, Pageable pageable) { public PageData<ProfileEntityIdInfo> findProfileEntityIdInfosByTenantId(UUID tenantId, Pageable pageable) {
String PROFILE_ASSET_ID_INFO_QUERY = String.format("SELECT tenant_id as tenantId, customer_id as customerId, asset_profile_id as profileId, id as id FROM asset WHERE tenant_id = '%s' ORDER BY created_time ASC LIMIT %%s OFFSET %%s", tenantId); String PROFILE_ASSET_ID_INFO_QUERY = String.format("SELECT tenant_id as tenantId, customer_id as customerId, asset_profile_id as profileId, id as id FROM asset WHERE tenant_id = '%s' ORDER BY created_time ASC LIMIT %%s OFFSET %%s", tenantId);
return find(COUNT_QUERY, PROFILE_ASSET_ID_INFO_QUERY, pageable, DefaultNativeAssetRepository::toInfo); String COUNT_QUERY_BY_TENANT = String.format("SELECT count(id) FROM asset WHERE tenant_id = '%s';", tenantId);
return find(COUNT_QUERY_BY_TENANT, PROFILE_ASSET_ID_INFO_QUERY, pageable, DefaultNativeAssetRepository::toInfo);
} }
private static ProfileEntityIdInfo toInfo(Map<String, Object> row) { private static ProfileEntityIdInfo toInfo(Map<String, Object> row) {

3
dao/src/main/java/org/thingsboard/server/dao/sql/device/DefaultNativeDeviceRepository.java

@ -62,7 +62,8 @@ public class DefaultNativeDeviceRepository extends AbstractNativeRepository impl
@Override @Override
public PageData<ProfileEntityIdInfo> findProfileEntityIdInfosByTenantId(UUID tenantId, Pageable pageable) { public PageData<ProfileEntityIdInfo> findProfileEntityIdInfosByTenantId(UUID tenantId, Pageable pageable) {
String PROFILE_DEVICE_ID_INFO_QUERY = String.format("SELECT tenant_id as tenantId, customer_id as customerId, device_profile_id as profileId, id as id FROM device WHERE tenant_id = '%s' ORDER BY created_time ASC LIMIT %%s OFFSET %%s", tenantId); String PROFILE_DEVICE_ID_INFO_QUERY = String.format("SELECT tenant_id as tenantId, customer_id as customerId, device_profile_id as profileId, id as id FROM device WHERE tenant_id = '%s' ORDER BY created_time ASC LIMIT %%s OFFSET %%s", tenantId);
return find(COUNT_QUERY, PROFILE_DEVICE_ID_INFO_QUERY, pageable, DefaultNativeDeviceRepository::toInfo); String COUNT_QUERY_BY_TENANT = String.format("SELECT count(id) FROM device WHERE tenant_id = '%s';", tenantId);
return find(COUNT_QUERY_BY_TENANT, PROFILE_DEVICE_ID_INFO_QUERY, pageable, DefaultNativeDeviceRepository::toInfo);
} }
private static ProfileEntityIdInfo toInfo(Map<String, Object> row) { private static ProfileEntityIdInfo toInfo(Map<String, Object> row) {

1
dao/src/test/resources/application-test.properties

@ -1,3 +1,4 @@
spring.main.banner-mode=off
zk.enabled=false zk.enabled=false
zk.url=localhost:2181 zk.url=localhost:2181
zk.zk_dir=/thingsboard zk.zk_dir=/thingsboard

3
dao/src/test/resources/logback-test.xml

@ -19,6 +19,9 @@
<!-- <logger name="org.hibernate.SQL" level="DEBUG"/> --> <!-- <logger name="org.hibernate.SQL" level="DEBUG"/> -->
<!-- <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG" /> --> <!-- <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG" /> -->
<!-- Avoiding multiple logs like: The cache 'relations' is not recording statistics. No meters except 'cache.size' will be registered. Call 'Caffeine#recordStats()' prior to building the cache for metrics to be recorded.-->
<logger name="io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics" level="ERROR"/>
<root level="WARN"> <root level="WARN">
<appender-ref ref="console"/> <appender-ref ref="console"/>
</root> </root>

2
edqs/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>edqs</artifactId> <artifactId>edqs</artifactId>

2
monitoring/pom.xml

@ -21,7 +21,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>

2
msa/black-box-tests/pom.xml

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

4
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java

@ -28,7 +28,7 @@ import org.thingsboard.server.common.msg.session.FeatureType;
public abstract class AbstractCoapClientTest extends AbstractContainerTest{ public abstract class AbstractCoapClientTest extends AbstractContainerTest{
private static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/"; private static final String CONTAINER_COAP_BASE_URL = "coap://localhost:5683/api/v1/";
private static final long CLIENT_REQUEST_TIMEOUT = 60000L; private static final long CLIENT_REQUEST_TIMEOUT = 60000L;
@ -54,7 +54,7 @@ public abstract class AbstractCoapClientTest extends AbstractContainerTest{
protected byte[] createCoapClientAndPublish(String deviceName) throws Exception { protected byte[] createCoapClientAndPublish(String deviceName) throws Exception {
String provisionRequestMsg = createTestProvisionMessage(deviceName); String provisionRequestMsg = createTestProvisionMessage(deviceName);
Configuration.addDefaultModule(MODULE_DEFINITIONS_PROVIDER); Configuration.addDefaultModule(MODULE_DEFINITIONS_PROVIDER);
String featureTokenUrl = COAP_BASE_URL + FeatureType.PROVISION.name().toLowerCase(); String featureTokenUrl = CONTAINER_COAP_BASE_URL + FeatureType.PROVISION.name().toLowerCase();
client = new CoapClient(featureTokenUrl); client = new CoapClient(featureTokenUrl);
return client.setTimeout(CLIENT_REQUEST_TIMEOUT) return client.setTimeout(CLIENT_REQUEST_TIMEOUT)
.post(provisionRequestMsg.getBytes(), MediaTypeRegistry.APPLICATION_JSON) .post(provisionRequestMsg.getBytes(), MediaTypeRegistry.APPLICATION_JSON)

20
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java

@ -44,7 +44,6 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.function.Consumer; import java.util.function.Consumer;
@Slf4j @Slf4j
@Listeners(TestListener.class) @Listeners(TestListener.class)
public abstract class AbstractContainerTest { public abstract class AbstractContainerTest {
@ -170,19 +169,14 @@ public abstract class AbstractContainerTest {
DeviceProfileProvisionConfiguration provisionConfiguration; DeviceProfileProvisionConfiguration provisionConfiguration;
String testProvisionDeviceKey = TEST_PROVISION_DEVICE_KEY; String testProvisionDeviceKey = TEST_PROVISION_DEVICE_KEY;
deviceProfile.setProvisionType(provisionType); deviceProfile.setProvisionType(provisionType);
switch(provisionType) { provisionConfiguration = switch (provisionType) {
case ALLOW_CREATE_NEW_DEVICES: case ALLOW_CREATE_NEW_DEVICES -> new AllowCreateNewDevicesDeviceProfileProvisionConfiguration(TEST_PROVISION_DEVICE_SECRET);
provisionConfiguration = new AllowCreateNewDevicesDeviceProfileProvisionConfiguration(TEST_PROVISION_DEVICE_SECRET); case CHECK_PRE_PROVISIONED_DEVICES -> new CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration(TEST_PROVISION_DEVICE_SECRET);
break; default -> {
case CHECK_PRE_PROVISIONED_DEVICES:
provisionConfiguration = new CheckPreProvisionedDevicesDeviceProfileProvisionConfiguration(TEST_PROVISION_DEVICE_SECRET);
break;
default:
case DISABLED:
testProvisionDeviceKey = null; testProvisionDeviceKey = null;
provisionConfiguration = new DisabledDeviceProfileProvisionConfiguration(null); yield new DisabledDeviceProfileProvisionConfiguration(null);
break; }
} };
DeviceProfileData deviceProfileData = deviceProfile.getProfileData(); DeviceProfileData deviceProfileData = deviceProfile.getProfileData();
deviceProfileData.setProvisionConfiguration(provisionConfiguration); deviceProfileData.setProvisionConfiguration(provisionConfiguration);
deviceProfile.setProfileData(deviceProfileData); deviceProfile.setProfileData(deviceProfileData);

42
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.msa;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.SocketException;
@Slf4j
public class PortFinder {
public static int findAvailableUdpPort() {
try (DatagramSocket socket = new DatagramSocket(0)) {
return socket.getLocalPort();
} catch (SocketException e) {
throw new IllegalStateException("No available UDP ports found", e);
}
}
public static boolean isUDPPortAvailable(int port) {
try (DatagramSocket socket = new DatagramSocket(port)) {
return true;
} catch (IOException e) {
log.debug("Failed to open UDP port {}", port, e);
return false;
}
}
}

176
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/JavaRestClientTest.java

@ -16,6 +16,7 @@
package org.thingsboard.server.msa.connectivity; package org.thingsboard.server.msa.connectivity;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
@ -26,7 +27,6 @@ import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.core5.ssl.SSLContexts; import org.apache.hc.core5.ssl.SSLContexts;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils;
import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
@ -34,6 +34,10 @@ import org.testng.annotations.Test;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rest.client.RestClient; import org.thingsboard.rest.client.RestClient;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
@ -41,6 +45,11 @@ import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.asset.AssetProfileInfo;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.domain.Domain; import org.thingsboard.server.common.data.domain.Domain;
import org.thingsboard.server.common.data.domain.DomainInfo; import org.thingsboard.server.common.data.domain.DomainInfo;
import org.thingsboard.server.common.data.id.NotificationTargetId; import org.thingsboard.server.common.data.id.NotificationTargetId;
@ -94,6 +103,7 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -109,8 +119,10 @@ public class JavaRestClientTest extends AbstractContainerTest {
public static final String DEFAULT_NOTIFICATION_SUBJECT = "Just a test"; public static final String DEFAULT_NOTIFICATION_SUBJECT = "Just a test";
public static final NotificationType DEFAULT_NOTIFICATION_TYPE = NotificationType.GENERAL; public static final NotificationType DEFAULT_NOTIFICATION_TYPE = NotificationType.GENERAL;
private RestClient restClient; private RestClient restClient;
private Tenant tenant; private Tenant tenant1;
private User user; private Tenant tenant2;
private User tenantAdmin1;
private User tenantAdmin2;
@BeforeClass @BeforeClass
public void beforeClass() throws Exception { public void beforeClass() throws Exception {
@ -140,31 +152,45 @@ public class JavaRestClientTest extends AbstractContainerTest {
public void setUp() throws Exception { public void setUp() throws Exception {
restClient.login("sysadmin@thingsboard.org", "sysadmin"); restClient.login("sysadmin@thingsboard.org", "sysadmin");
// create tenant and tenant admin // create tenant 1 and tenant admin 1
tenant = new Tenant(); tenant1 = new Tenant();
tenant.setTitle("Java Rest Client Test Tenant " + RandomStringUtils.randomAlphabetic(5)); tenant1.setTitle("Java Rest Client Test Tenant " + RandomStringUtils.insecure().randomAlphabetic(5));
tenant = restClient.saveTenant(tenant); tenant1 = restClient.saveTenant(tenant1);
String email = RandomStringUtils.randomAlphabetic(5) + "@gmail.com"; String email1 = RandomStringUtils.insecure().randomAlphabetic(5) + "@gmail.com";
user = restClient.saveUser(defaultTenantAdmin(tenant.getId(), email), false); tenantAdmin1 = restClient.saveUser(defaultTenantAdmin(tenant1.getId(), email1), false);
restClient.activateUser(user.getId(), "password123", false); restClient.activateUser(tenantAdmin1.getId(), "password123", false);
restClient.login(email, "password123");
// create tenant 2 and tenant admin 2
tenant2 = new Tenant();
tenant2.setTitle("Java Rest Client Test Tenant " + RandomStringUtils.insecure().randomAlphabetic(5));
tenant2 = restClient.saveTenant(tenant2);
String email2 = RandomStringUtils.insecure().randomAlphabetic(5) + "@gmail.com";
tenantAdmin2 = restClient.saveUser(defaultTenantAdmin(tenant2.getId(), email2), false);
restClient.activateUser(tenantAdmin2.getId(), "password123", false);
// tenant 1 tenant admin by default
restClient.login(tenantAdmin1.getEmail(), "password123");
} }
@AfterMethod @AfterMethod
public void tearDown() { public void tearDown() {
restClient.login("sysadmin@thingsboard.org", "sysadmin"); restClient.login("sysadmin@thingsboard.org", "sysadmin");
if (tenant != null) { if (tenant1 != null) {
restClient.deleteTenant(tenant.getId()); restClient.deleteTenant(tenant1.getId());
}
if (tenant2 != null) {
restClient.deleteTenant(tenant2.getId());
} }
} }
@Test @Test
public void testGetAlarmsV2() { public void testGetAlarmsV2() {
Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.randomAlphabetic(5))); Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.insecure().randomAlphabetic(5)));
assertThat(device).isNotNull(); assertThat(device).isNotNull();
String type = "High temp" + RandomStringUtils.randomAlphabetic(5); String type = "High temp" + RandomStringUtils.insecure().randomAlphabetic(5);
Alarm alarm = Alarm.builder() Alarm alarm = Alarm.builder()
.originator(device.getId()) .originator(device.getId())
.severity(AlarmSeverity.CRITICAL) .severity(AlarmSeverity.CRITICAL)
@ -202,7 +228,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
@Test @Test
public void testTimeSeriesByReadTsKvQueries() { public void testTimeSeriesByReadTsKvQueries() {
Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.randomAlphabetic(5))); Device device = restClient.saveDevice(defaultDevicePrototype(RandomStringUtils.insecure().randomAlphabetic(5)));
assertThat(device).isNotNull(); assertThat(device).isNotNull();
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get(); DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
@ -230,7 +256,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
@Test @Test
public void testFindNotifications() { public void testFindNotifications() {
NotificationTarget notificationTarget = createNotificationTarget(user.getId()); NotificationTarget notificationTarget = createNotificationTarget(tenantAdmin1.getId());
String notificationText1 = "Notification 1"; String notificationText1 = "Notification 1";
NotificationTemplate notificationTemplate = createNotificationTemplate(DEFAULT_NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_SUBJECT, notificationText1, new NotificationDeliveryMethod[]{WEB}); NotificationTemplate notificationTemplate = createNotificationTemplate(DEFAULT_NOTIFICATION_TYPE, DEFAULT_NOTIFICATION_SUBJECT, notificationText1, new NotificationDeliveryMethod[]{WEB});
NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), notificationTemplate.getId()); NotificationRequest notificationRequest = submitNotificationRequest(notificationTarget.getId(), notificationTemplate.getId());
@ -248,7 +274,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
NotificationRequestPreview requestPreview = restClient.getNotificationRequestPreview(notificationRequest, 10); NotificationRequestPreview requestPreview = restClient.getNotificationRequestPreview(notificationRequest, 10);
assertThat(requestPreview.getTotalRecipientsCount()).isEqualTo(1); assertThat(requestPreview.getTotalRecipientsCount()).isEqualTo(1);
assertThat(requestPreview.getRecipientsPreview()).isEqualTo(List.of(user.getEmail())); assertThat(requestPreview.getRecipientsPreview()).isEqualTo(List.of(tenantAdmin1.getEmail()));
PageData<Notification> notifications = restClient.getNotifications(false, WEB, new PageLink(30)); PageData<Notification> notifications = restClient.getNotifications(false, WEB, new PageLink(30));
assertThat(notifications.getTotalElements()).isEqualTo(2); assertThat(notifications.getTotalElements()).isEqualTo(2);
@ -316,7 +342,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
restClient.login("sysadmin@thingsboard.org", "sysadmin"); restClient.login("sysadmin@thingsboard.org", "sysadmin");
Domain domain = new Domain(); Domain domain = new Domain();
String prefix = RandomStringUtils.randomAlphabetic(5).toLowerCase(); String prefix = RandomStringUtils.insecure().randomAlphabetic(5).toLowerCase();
domain.setName(prefix + ".test.com"); domain.setName(prefix + ".test.com");
Domain savedDomain = restClient.saveDomain(domain); Domain savedDomain = restClient.saveDomain(domain);
assertThat(savedDomain.getName()).isEqualTo(domain.getName()); assertThat(savedDomain.getName()).isEqualTo(domain.getName());
@ -330,10 +356,10 @@ public class JavaRestClientTest extends AbstractContainerTest {
restClient.login("sysadmin@thingsboard.org", "sysadmin"); restClient.login("sysadmin@thingsboard.org", "sysadmin");
MobileApp mobileApp = new MobileApp(); MobileApp mobileApp = new MobileApp();
String prefix = RandomStringUtils.randomAlphabetic(5).toLowerCase(); String prefix = RandomStringUtils.insecure().randomAlphabetic(5).toLowerCase();
mobileApp.setPkgName(prefix + "test.app.apple"); mobileApp.setPkgName(prefix + "test.app.apple");
mobileApp.setPlatformType(PlatformType.ANDROID); mobileApp.setPlatformType(PlatformType.ANDROID);
mobileApp.setAppSecret(RandomStringUtils.randomAlphabetic(20)); mobileApp.setAppSecret(RandomStringUtils.insecure().randomAlphabetic(20));
mobileApp.setStatus(MobileAppStatus.DRAFT); mobileApp.setStatus(MobileAppStatus.DRAFT);
MobileApp savedMobileApp = restClient.saveMobileApp(mobileApp); MobileApp savedMobileApp = restClient.saveMobileApp(mobileApp);
@ -343,7 +369,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
assertThat(retrieved.getData()).hasSize(1); assertThat(retrieved.getData()).hasSize(1);
MobileAppBundle mobileAppBundle = new MobileAppBundle(); MobileAppBundle mobileAppBundle = new MobileAppBundle();
String bundlePrefix = RandomStringUtils.randomAlphabetic(5).toLowerCase(); String bundlePrefix = RandomStringUtils.insecure().randomAlphabetic(5).toLowerCase();
mobileAppBundle.setTitle(bundlePrefix + "Test Bundle"); mobileAppBundle.setTitle(bundlePrefix + "Test Bundle");
mobileAppBundle.setAndroidAppId(savedMobileApp.getId()); mobileAppBundle.setAndroidAppId(savedMobileApp.getId());
@ -357,7 +383,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
filter.setUsersIds(Arrays.stream(usersIds).map(UUIDBased::getId).toList()); filter.setUsersIds(Arrays.stream(usersIds).map(UUIDBased::getId).toList());
NotificationTarget notificationTarget = new NotificationTarget(); NotificationTarget notificationTarget = new NotificationTarget();
notificationTarget.setName(filter + RandomStringUtils.randomNumeric(5)); notificationTarget.setName(filter + RandomStringUtils.insecure().randomNumeric(5));
PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig(); PlatformUsersNotificationTargetConfig targetConfig = new PlatformUsersNotificationTargetConfig();
targetConfig.setUsersFilter(filter); targetConfig.setUsersFilter(filter);
notificationTarget.setConfiguration(targetConfig); notificationTarget.setConfiguration(targetConfig);
@ -367,7 +393,7 @@ public class JavaRestClientTest extends AbstractContainerTest {
private NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject, private NotificationTemplate createNotificationTemplate(NotificationType notificationType, String subject,
String text, NotificationDeliveryMethod... deliveryMethods) { String text, NotificationDeliveryMethod... deliveryMethods) {
NotificationTemplate notificationTemplate = new NotificationTemplate(); NotificationTemplate notificationTemplate = new NotificationTemplate();
notificationTemplate.setName("Notification template: " + RandomStringUtils.randomAlphabetic(5)); notificationTemplate.setName("Notification template: " + RandomStringUtils.insecure().randomAlphabetic(5));
notificationTemplate.setNotificationType(notificationType); notificationTemplate.setNotificationType(notificationType);
NotificationTemplateConfig config = new NotificationTemplateConfig(); NotificationTemplateConfig config = new NotificationTemplateConfig();
config.setDeliveryMethodsTemplates(new HashMap<>()); config.setDeliveryMethodsTemplates(new HashMap<>());
@ -418,9 +444,9 @@ public class JavaRestClientTest extends AbstractContainerTest {
public void testApiKeyOperations() { public void testApiKeyOperations() {
// Create an API key // Create an API key
ApiKeyInfo apiKeyInfo = new ApiKeyInfo(); ApiKeyInfo apiKeyInfo = new ApiKeyInfo();
apiKeyInfo.setDescription("Test API Key " + RandomStringUtils.randomAlphabetic(5)); apiKeyInfo.setDescription("Test API Key " + RandomStringUtils.insecure().randomAlphabetic(5));
apiKeyInfo.setEnabled(true); apiKeyInfo.setEnabled(true);
apiKeyInfo.setUserId(user.getId()); apiKeyInfo.setUserId(tenantAdmin1.getId());
apiKeyInfo.setExpirationTime(0); apiKeyInfo.setExpirationTime(0);
ApiKey savedApiKey = restClient.saveApiKey(apiKeyInfo); ApiKey savedApiKey = restClient.saveApiKey(apiKeyInfo);
@ -428,18 +454,18 @@ public class JavaRestClientTest extends AbstractContainerTest {
assertThat(savedApiKey.getId()).isNotNull(); assertThat(savedApiKey.getId()).isNotNull();
assertThat(savedApiKey.getDescription()).isEqualTo(apiKeyInfo.getDescription()); assertThat(savedApiKey.getDescription()).isEqualTo(apiKeyInfo.getDescription());
assertThat(savedApiKey.isEnabled()).isTrue(); assertThat(savedApiKey.isEnabled()).isTrue();
assertThat(savedApiKey.getUserId()).isEqualTo(user.getId()); assertThat(savedApiKey.getUserId()).isEqualTo(tenantAdmin1.getId());
assertThat(savedApiKey.getTenantId()).isEqualTo(tenant.getId()); assertThat(savedApiKey.getTenantId()).isEqualTo(tenant1.getId());
assertThat(savedApiKey.getValue()).isNotNull(); assertThat(savedApiKey.getValue()).isNotNull();
// Get user API keys // Get user API keys
PageData<ApiKeyInfo> apiKeys = restClient.getUserApiKeys(user.getId(), new PageLink(10)); PageData<ApiKeyInfo> apiKeys = restClient.getUserApiKeys(tenantAdmin1.getId(), new PageLink(10));
assertThat(apiKeys).isNotNull(); assertThat(apiKeys).isNotNull();
assertThat(apiKeys.getData()).hasSize(1); assertThat(apiKeys.getData()).hasSize(1);
assertThat(apiKeys.getData().get(0).getId()).isEqualTo(savedApiKey.getId()); assertThat(apiKeys.getData().get(0).getId()).isEqualTo(savedApiKey.getId());
// Update API key description // Update API key description
String updatedDescription = "Updated description " + RandomStringUtils.randomAlphabetic(5); String updatedDescription = "Updated description " + RandomStringUtils.insecure().randomAlphabetic(5);
ApiKeyInfo updatedApiKeyInfo = restClient.updateApiKeyDescription(savedApiKey.getId(), updatedDescription); ApiKeyInfo updatedApiKeyInfo = restClient.updateApiKeyDescription(savedApiKey.getId(), updatedDescription);
assertThat(updatedApiKeyInfo).isNotNull(); assertThat(updatedApiKeyInfo).isNotNull();
assertThat(updatedApiKeyInfo.getDescription()).isEqualTo(updatedDescription); assertThat(updatedApiKeyInfo.getDescription()).isEqualTo(updatedDescription);
@ -458,8 +484,96 @@ public class JavaRestClientTest extends AbstractContainerTest {
restClient.deleteApiKey(savedApiKey.getId()); restClient.deleteApiKey(savedApiKey.getId());
// Verify the API key is deleted // Verify the API key is deleted
PageData<ApiKeyInfo> apiKeysAfterDelete = restClient.getUserApiKeys(user.getId(), new PageLink(10)); PageData<ApiKeyInfo> apiKeysAfterDelete = restClient.getUserApiKeys(tenantAdmin1.getId(), new PageLink(10));
assertThat(apiKeysAfterDelete.getData()).isEmpty(); assertThat(apiKeysAfterDelete.getData()).isEmpty();
} }
@Test
public void testGetDeviceProfileInfosByIds() {
var profileData = new DeviceProfileData();
profileData.setConfiguration(new DefaultDeviceProfileConfiguration());
profileData.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration());
// Create a device profile in tenant1 (current tenant)
var deviceProfile1 = new DeviceProfile();
deviceProfile1.setTenantId(tenant1.getId());
deviceProfile1.setName("Device Profile 1");
deviceProfile1.setType(DeviceProfileType.DEFAULT);
deviceProfile1.setTransportType(DeviceTransportType.DEFAULT);
deviceProfile1.setProfileData(profileData);
deviceProfile1 = restClient.saveDeviceProfile(deviceProfile1);
var deviceProfile2 = new DeviceProfile();
deviceProfile2.setTenantId(tenant1.getId());
deviceProfile2.setName("Device Profile 2");
deviceProfile2.setType(DeviceProfileType.DEFAULT);
deviceProfile2.setTransportType(DeviceTransportType.DEFAULT);
deviceProfile2.setProfileData(profileData);
deviceProfile2 = restClient.saveDeviceProfile(deviceProfile2);
// Create two more device profiles in tenant2 (different tenant)
restClient.login(tenantAdmin2.getEmail(), "password123");
var deviceProfile3 = new DeviceProfile();
deviceProfile3.setTenantId(tenant2.getId());
deviceProfile3.setName("Device Profile 3");
deviceProfile3.setType(DeviceProfileType.DEFAULT);
deviceProfile3.setTransportType(DeviceTransportType.DEFAULT);
deviceProfile3.setProfileData(profileData);
deviceProfile3 = restClient.saveDeviceProfile(deviceProfile3);
var deviceProfile4 = new DeviceProfile();
deviceProfile4.setTenantId(tenant2.getId());
deviceProfile4.setName("Device Profile 4");
deviceProfile4.setType(DeviceProfileType.DEFAULT);
deviceProfile4.setTransportType(DeviceTransportType.DEFAULT);
deviceProfile4.setProfileData(profileData);
deviceProfile4 = restClient.saveDeviceProfile(deviceProfile4);
// Attempt to fetch profiles 1 and 3 while acting as Tenant 2.
// - Profile 1: Filtered out because it belongs to a different tenant.
// - Profile 2: Filtered out because it belongs to a different tenant and was not requested.
// - Profile 3: Should be returned.
// - Profile 4: Filtered out; it belongs to the correct tenant, but was not requested by ID.
List<DeviceProfileInfo> profiles = restClient.getDeviceProfileInfosByIds(Set.of(deviceProfile1.getUuidId(), deviceProfile3.getUuidId()));
assertThat(profiles).hasSize(1);
assertThat(profiles.get(0).getId()).isEqualTo(deviceProfile3.getId());
}
@Test
public void testGetAssetProfilesByIds() {
// Create two asset profiles in tenant1 (current tenant)
var assetProfile1 = new AssetProfile();
assetProfile1.setTenantId(tenant1.getId());
assetProfile1.setName("Asset Profile 1");
assetProfile1 = restClient.saveAssetProfile(assetProfile1);
var assetProfile2 = new AssetProfile();
assetProfile2.setTenantId(tenant1.getId());
assetProfile2.setName("Asset Profile 2");
assetProfile2 = restClient.saveAssetProfile(assetProfile2);
// Create two more asset profiles in tenant2 (different tenant)
restClient.login(tenantAdmin2.getEmail(), "password123");
var assetProfile3 = new AssetProfile();
assetProfile3.setTenantId(tenant2.getId());
assetProfile3.setName("Asset Profile 3");
assetProfile3 = restClient.saveAssetProfile(assetProfile3);
var assetProfile4 = new AssetProfile();
assetProfile4.setTenantId(tenant2.getId());
assetProfile4.setName("Asset Profile 4");
assetProfile4 = restClient.saveAssetProfile(assetProfile4);
// Attempt to fetch profiles 1 and 3 while acting as Tenant 2.
// - Profile 1: Filtered out because it belongs to a different tenant.
// - Profile 2: Filtered out because it belongs to a different tenant and was not requested.
// - Profile 3: Should be returned.
// - Profile 4: Filtered out; it belongs to the correct tenant, but was not requested by ID.
List<AssetProfileInfo> profiles = restClient.getAssetProfilesByIds(Set.of(assetProfile1.getUuidId(), assetProfile3.getUuidId()));
assertThat(profiles).hasSize(1);
assertThat(profiles.get(0).getId()).isEqualTo(assetProfile3.getId());
}
} }

8
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java

@ -57,12 +57,12 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.msa.AbstractContainerTest; import org.thingsboard.server.msa.AbstractContainerTest;
import org.thingsboard.server.msa.PortFinder;
import org.thingsboard.server.msa.WsClient; import org.thingsboard.server.msa.WsClient;
import org.thingsboard.server.msa.connectivity.lwm2m.client.LwM2MTestClient; import org.thingsboard.server.msa.connectivity.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState; import org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState;
import org.thingsboard.server.msa.mapper.WsTelemetryResponse; import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
import java.net.ServerSocket;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
@ -229,10 +229,8 @@ public class AbstractLwm2mClientTest extends AbstractContainerTest {
String endpoint, ScheduledExecutorService executor) throws Exception { String endpoint, ScheduledExecutorService executor) throws Exception {
this.executor = executor; this.executor = executor;
LwM2MTestClient lwM2MTestClient = new LwM2MTestClient(executor, endpoint); LwM2MTestClient lwM2MTestClient = new LwM2MTestClient(executor, endpoint);
try (ServerSocket socket = new ServerSocket(0)) { int clientPort = PortFinder.findAvailableUdpPort();
int clientPort = socket.getLocalPort(); lwM2MTestClient.init(security, clientPort);
lwM2MTestClient.init(security, clientPort);
}
return lwM2MTestClient; return lwM2MTestClient;
} }

21
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java

@ -65,6 +65,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY;
import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL;
@ -72,6 +73,7 @@ import static org.eclipse.leshan.core.LwM2mId.DEVICE;
import static org.eclipse.leshan.core.LwM2mId.FIRMWARE; import static org.eclipse.leshan.core.LwM2mId.FIRMWARE;
import static org.eclipse.leshan.core.LwM2mId.SECURITY; import static org.eclipse.leshan.core.LwM2mId.SECURITY;
import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SERVER;
import static org.thingsboard.server.msa.PortFinder.isUDPPortAvailable;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_FAILURE; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_FAILURE;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_STARTED; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_STARTED;
@ -95,7 +97,6 @@ import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelp
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.serverId; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.serverId;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.shortServerId; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.shortServerId;
@Slf4j @Slf4j
@Data @Data
public class LwM2MTestClient { public class LwM2MTestClient {
@ -115,8 +116,11 @@ public class LwM2MTestClient {
private int countUpdateRegistrationSuccess; private int countUpdateRegistrationSuccess;
private int countReadObserveAfterUpdateRegistrationSuccess; private int countReadObserveAfterUpdateRegistrationSuccess;
private int clientPort;
public void init(Security security, int clientPort) throws InvalidDDFFileException, IOException { public void init(Security security, int clientPort) throws InvalidDDFFileException, IOException {
assertThat(leshanClient).as("client already initialized").isNull(); assertThat(leshanClient).as("client already initialized").isNull();
this.clientPort = clientPort;
List<ObjectModel> models = new ArrayList<>(); List<ObjectModel> models = new ArrayList<>();
for (String resourceName : resources) { for (String resourceName : resources) {
@ -342,12 +346,27 @@ public class LwM2MTestClient {
} }
}); });
}
public void start() {
leshanClient.start(); leshanClient.start();
if (clientPort > 0) {
log.error("Await UDP clientPort {} to be available before leshanClient.start()", clientPort);
await("Await UDP clientPort " + clientPort + " to be available before leshanClient.start()")
.atMost(30, TimeUnit.SECONDS)
.until(() -> isUDPPortAvailable(clientPort));
}
} }
public void destroy() { public void destroy() {
if (leshanClient != null) { if (leshanClient != null) {
leshanClient.destroy(true); leshanClient.destroy(true);
if (clientPort > 0) {
log.error("Await UDP clientPort {} to disconnect after leshanClient.stop(deregister)", clientPort);
await("Await client UDP port " + clientPort + " to disconnect after leshanClient.stop(deregister)")
.atMost(30, TimeUnit.SECONDS)
.until(() -> isUDPPortAvailable(clientPort));
}
} }
if (lwM2MDevice != null) { if (lwM2MDevice != null) {
lwM2MDevice.destroy(); lwM2MDevice.destroy();

3
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java

@ -65,6 +65,7 @@ import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevi
public class MqttNodeTest extends AbstractContainerTest { public class MqttNodeTest extends AbstractContainerTest {
private static final String TOPIC = "tb/mqtt/device"; private static final String TOPIC = "tb/mqtt/device";
private static final String CONTAINER_MQTT_URL = "tcp://localhost:1883";
private Device device; private Device device;
@ -94,7 +95,7 @@ public class MqttNodeTest extends AbstractContainerTest {
responseClient.connect(); responseClient.connect();
responseClient.subscribe(TOPIC, messageListener); responseClient.subscribe(TOPIC, messageListener);
MqttClient mqttClient = new MqttClient("tcp://localhost:1883", StringUtils.randomAlphanumeric(10), new MemoryPersistence()); MqttClient mqttClient = new MqttClient(CONTAINER_MQTT_URL, StringUtils.randomAlphanumeric(10), new MemoryPersistence());
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setUserName(deviceCredentials.getCredentialsId()); mqttConnectOptions.setUserName(deviceCredentials.getCredentialsId());
mqttClient.connect(mqttConnectOptions); mqttClient.connect(mqttConnectOptions);

2
msa/edqs/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/js-executor/package.json

@ -1,7 +1,7 @@
{ {
"name": "thingsboard-js-executor", "name": "thingsboard-js-executor",
"private": true, "private": true,
"version": "4.3.0", "version": "4.4.0",
"description": "ThingsBoard JavaScript Executor Microservice", "description": "ThingsBoard JavaScript Executor Microservice",
"main": "server.ts", "main": "server.ts",
"bin": "server.js", "bin": "server.js",

2
msa/js-executor/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/monitoring/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>

4
msa/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
@ -133,7 +133,7 @@
</property> </property>
</activation> </activation>
<properties> <properties>
<docker.lts.tag>4.3.0-latest</docker.lts.tag> <docker.lts.tag>4.4.0-latest</docker.lts.tag>
<docker.skip.latest.tag>false</docker.skip.latest.tag> <docker.skip.latest.tag>false</docker.skip.latest.tag>
</properties> </properties>
<build> <build>

2
msa/tb-node/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/tb/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/lwm2m/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/transport/snmp/pom.xml

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>
<artifactId>transport</artifactId> <artifactId>transport</artifactId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
</parent> </parent>
<groupId>org.thingsboard.msa.transport</groupId> <groupId>org.thingsboard.msa.transport</groupId>

2
msa/vc-executor-docker/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/vc-executor/pom.xml

@ -21,7 +21,7 @@
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

2
msa/web-ui/package.json

@ -1,7 +1,7 @@
{ {
"name": "thingsboard-web-ui", "name": "thingsboard-web-ui",
"private": true, "private": true,
"version": "4.3.0", "version": "4.4.0",
"description": "ThingsBoard Web UI Microservice", "description": "ThingsBoard Web UI Microservice",
"main": "server.ts", "main": "server.ts",
"bin": "server.js", "bin": "server.js",

2
msa/web-ui/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>msa</artifactId> <artifactId>msa</artifactId>
</parent> </parent>
<groupId>org.thingsboard.msa</groupId> <groupId>org.thingsboard.msa</groupId>

4
netty-mqtt/pom.xml

@ -19,11 +19,11 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>netty-mqtt</artifactId> <artifactId>netty-mqtt</artifactId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Netty MQTT Client</name> <name>Netty MQTT Client</name>

12
pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Thingsboard</name> <name>Thingsboard</name>
@ -69,7 +69,7 @@
<paho.client.version>1.2.5</paho.client.version> <paho.client.version>1.2.5</paho.client.version>
<paho.mqttv5.client.version>1.2.5</paho.mqttv5.client.version> <paho.mqttv5.client.version>1.2.5</paho.mqttv5.client.version>
<os-maven-plugin.version>1.7.1</os-maven-plugin.version> <os-maven-plugin.version>1.7.1</os-maven-plugin.version>
<surefire.version>3.2.5</surefire.version> <surefire.version>3.5.4</surefire.version>
<jar-plugin.version>3.4.0</jar-plugin.version> <jar-plugin.version>3.4.0</jar-plugin.version>
<springdoc-swagger.version>2.8.8TB</springdoc-swagger.version> <springdoc-swagger.version>2.8.8TB</springdoc-swagger.version>
<swagger-annotations.version>2.2.30</swagger-annotations.version> <swagger-annotations.version>2.2.30</swagger-annotations.version>
@ -127,7 +127,7 @@
<jeasy.version>5.0.0</jeasy.version> <jeasy.version>5.0.0</jeasy.version>
<!-- BLACKBOX TEST SCOPE --> <!-- BLACKBOX TEST SCOPE -->
<testng.version>7.10.1</testng.version> <testng.version>7.10.1</testng.version>
<testcontainers.version>1.20.6</testcontainers.version> <testcontainers.version>1.21.4</testcontainers.version>
<testcontainers-junit4-mock.version>1.0.2</testcontainers-junit4-mock.version> <testcontainers-junit4-mock.version>1.0.2</testcontainers-junit4-mock.version>
<zeroturnaround.version>1.12</zeroturnaround.version> <zeroturnaround.version>1.12</zeroturnaround.version>
<webdrivermanager.version>6.1.0</webdrivermanager.version> <webdrivermanager.version>6.1.0</webdrivermanager.version>
@ -662,8 +662,10 @@
<version>${surefire.version}</version> <version>${surefire.version}</version>
<configuration> <configuration>
<argLine> <argLine>
-XX:+UseStringDeduplication -XX:MaxGCPauseMillis=20 -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=200
--add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
-Dqueue.edqs.local.rocksdb_path="target/rocksdb/fork_${surefire.forkNumber}/edqs"
-Dqueue.calculated_fields.rocks_db_path="target/rocksdb/fork_${surefire.forkNumber}/cf"
</argLine> </argLine>
</configuration> </configuration>
</plugin> </plugin>

6
rest-client/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<version>4.3.0-RC</version> <version>4.4.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId> <artifactId>thingsboard</artifactId>
</parent> </parent>
<artifactId>rest-client</artifactId> <artifactId>rest-client</artifactId>
@ -51,6 +51,10 @@
<groupId>com.auth0</groupId> <groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId> <artifactId>java-jwt</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents.core5</groupId>
<artifactId>httpcore5</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

63
rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java

@ -23,6 +23,7 @@ import lombok.Getter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.concurrent.LazyInitializer; import org.apache.commons.lang3.concurrent.LazyInitializer;
import org.apache.hc.core5.net.URIBuilder;
import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@ -157,11 +158,11 @@ import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.pat.ApiKey;
import org.thingsboard.server.common.data.pat.ApiKeyInfo;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.pat.ApiKey;
import org.thingsboard.server.common.data.pat.ApiKeyInfo;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.query.AlarmCountQuery; import org.thingsboard.server.common.data.query.AlarmCountQuery;
@ -210,17 +211,21 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static java.util.stream.Collectors.joining;
import static org.thingsboard.server.common.data.StringUtils.isEmpty; import static org.thingsboard.server.common.data.StringUtils.isEmpty;
public class RestClient implements Closeable { public class RestClient implements Closeable {
@ -1588,6 +1593,33 @@ public class RestClient implements Closeable {
}, activeOnly).getBody(); }, activeOnly).getBody();
} }
public List<DeviceProfileInfo> getDeviceProfileInfosByIds(Set<UUID> ids) {
URIBuilder builder;
try {
builder = new URIBuilder(baseURL);
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid base URL: " + baseURL, e);
}
builder.appendPath("/api/deviceProfileInfos");
String commaSeparatedIds = ids.stream()
.filter(Objects::nonNull)
.map(UUID::toString)
.collect(joining(","));
builder.addParameter("deviceProfileIds", commaSeparatedIds);
URI uri;
try {
uri = builder.build();
} catch (URISyntaxException e) {
throw new IllegalStateException("Failed to construct API URI from base URL and provided params", e);
}
return restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference<List<DeviceProfileInfo>>() {}).getBody();
}
public JsonNode claimDevice(String deviceName, ClaimRequest claimRequest) { public JsonNode claimDevice(String deviceName, ClaimRequest claimRequest) {
return restTemplate.exchange( return restTemplate.exchange(
baseURL + "/api/customer/device/{deviceName}/claim", baseURL + "/api/customer/device/{deviceName}/claim",
@ -1824,6 +1856,33 @@ public class RestClient implements Closeable {
}, params).getBody(); }, params).getBody();
} }
public List<AssetProfileInfo> getAssetProfilesByIds(Set<UUID> ids) {
URIBuilder builder;
try {
builder = new URIBuilder(baseURL);
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid base URL: " + baseURL, e);
}
builder.appendPath("/api/assetProfileInfos");
String commaSeparatedIds = ids.stream()
.filter(Objects::nonNull)
.map(UUID::toString)
.collect(joining(","));
builder.addParameter("assetProfileIds", commaSeparatedIds);
URI uri;
try {
uri = builder.build();
} catch (URISyntaxException e) {
throw new IllegalStateException("Failed to construct API URI from base URL and provided params", e);
}
return restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference<List<AssetProfileInfo>>() {}).getBody();
}
public Long countEntitiesByQuery(EntityCountQuery query) { public Long countEntitiesByQuery(EntityCountQuery query) {
return restTemplate.postForObject(baseURL + "/api/entitiesQuery/count", query, Long.class); return restTemplate.postForObject(baseURL + "/api/entitiesQuery/count", query, Long.class);
} }

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

Loading…
Cancel
Save