Browse Source

Merge branch 'feature/re-pack-timeout' into feature/mqtt-rate-limits

pull/5884/head
Andrii Shvaika 4 years ago
parent
commit
21e080cae8
  1. 18
      application/src/main/data/json/system/widget_bundles/control_widgets.json
  2. 51
      application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql
  3. 13
      application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java
  4. 4
      application/src/main/java/org/thingsboard/server/controller/UserController.java
  5. 4
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  6. 18
      application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java
  7. 2
      application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java
  8. 2
      application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java
  9. 14
      application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContext.java
  10. 2
      application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategy.java
  11. 18
      application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java
  12. 16
      application/src/main/resources/thingsboard.yml
  13. 1
      application/src/test/java/org/thingsboard/server/service/ServiceSqlTestSuite.java
  14. 2
      application/src/test/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContextTest.java
  15. 207
      application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java
  16. 16
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  17. 54
      application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java
  18. 25
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
  19. 11
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java
  20. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mLocation.java
  21. 7
      application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java
  22. 20
      application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java
  23. 63
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java
  24. 30
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java
  25. 14
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java
  26. 8
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java
  27. 30
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationExecuteTest.java
  28. 26
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java
  29. 68
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java
  30. 11
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java
  31. 77
      application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java
  32. 151
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java
  33. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/NoSecLwM2MIntegrationTest.java
  34. 12
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java
  35. 12
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java
  36. 11
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java
  37. 8
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java
  38. 20
      application/src/test/resources/application-test.properties
  39. BIN
      application/src/test/resources/lwm2m/credentials/clientKeyStore.jks
  40. BIN
      application/src/test/resources/lwm2m/credentials/lwm2mclient.jks
  41. BIN
      application/src/test/resources/lwm2m/credentials/lwm2mserver.jks
  42. BIN
      application/src/test/resources/lwm2m/credentials/lwm2mtruststorechain.jks
  43. BIN
      application/src/test/resources/lwm2m/credentials/serverKeyStore.jks
  44. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java
  45. 14
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java
  46. 2
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java
  47. 50
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java
  48. 2
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java
  49. 359
      common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh
  50. 57
      common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties
  51. BIN
      common/transport/lwm2m/src/main/resources/lwm2mserver.jks
  52. 26
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/AbstractSslCredentials.java
  53. 3
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelFilter.java
  54. 6
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  55. 10
      dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java
  56. 4
      dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java
  57. 7
      dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java
  58. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java
  59. 10
      dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java
  60. 34
      pom.xml
  61. 15
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbSaveToCustomCassandraTableNode.java
  62. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNode.java
  63. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mqtt/TbMqttNodeConfiguration.java
  64. 18
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
  65. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java
  66. 2
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  67. 423
      tools/src/main/shell/lwm2m/lwM2M_cfssl_chain_clients_for_test.sh
  68. 81
      tools/src/main/shell/lwm2m/lwm2m_cfssl_chain_all_for_test.sh
  69. 314
      tools/src/main/shell/lwm2m/lwm2m_cfssl_chain_server_for_test.sh
  70. 6
      transport/coap/src/main/resources/tb-coap-transport.yml
  71. 6
      transport/http/src/main/resources/tb-http-transport.yml
  72. 360
      transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh
  73. 57
      transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties
  74. BIN
      transport/lwm2m/src/main/data/lwm2mserver.jks
  75. 16
      transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml
  76. 6
      transport/mqtt/src/main/resources/tb-mqtt-transport.yml
  77. 6
      transport/snmp/src/main/resources/tb-snmp-transport.yml
  78. 5
      ui-ngx/angular.json
  79. 3
      ui-ngx/package.json
  80. 11
      ui-ngx/src/app/core/api/widget-api.models.ts
  81. 13
      ui-ngx/src/app/core/api/widget-subscription.ts
  82. 15
      ui-ngx/src/app/core/http/device.service.ts
  83. 2142
      ui-ngx/src/app/core/services/material-icons-codepoints.raw
  84. 2
      ui-ngx/src/app/core/services/utils.service.ts
  85. 5
      ui-ngx/src/app/core/utils.ts
  86. 2
      ui-ngx/src/app/modules/common/modules-map.ts
  87. 1
      ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts
  88. 1
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html
  89. 1
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss
  90. 25
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts
  91. 4
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts
  92. 8
      ui-ngx/src/app/modules/home/components/audit-log/audit-log-table.component.ts
  93. 1
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.html
  94. 1
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.scss
  95. 143
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts
  96. 57
      ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html
  97. 119
      ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.scss
  98. 182
      ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts
  99. 16
      ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts
  100. 2
      ui-ngx/src/app/modules/home/components/entity/entity.component.ts

18
application/src/main/data/json/system/widget_bundles/control_widgets.json

File diff suppressed because one or more lines are too long

51
application/src/main/data/upgrade/3.3.2/schema_update_lwm2m_bootstrap.sql

@ -14,6 +14,7 @@
-- limitations under the License.
--
CREATE OR REPLACE PROCEDURE update_profile_bootstrap()
LANGUAGE plpgsql AS
$$
@ -25,9 +26,11 @@ BEGIN
profile_data,
'{transportConfiguration}',
get_bootstrap(
profile_data::jsonb #> '{transportConfiguration}',
subquery.publickey_bs,
subquery.publickey_lw),
profile_data::jsonb #> '{transportConfiguration}',
subquery.publickey_bs,
subquery.publickey_lw,
profile_data::json #>> '{transportConfiguration, bootstrap, bootstrapServer, securityMode}',
profile_data::json #>> '{transportConfiguration, bootstrap, lwm2mServer, securityMode}'),
true)
FROM (
SELECT id,
@ -48,7 +51,8 @@ END;
$$;
CREATE OR REPLACE FUNCTION get_bootstrap(transport_configuration_in jsonb, publickey_bs text,
publickey_lw text) RETURNS jsonb AS
publickey_lw text, security_mode_bs text,
security_mode_lw text) RETURNS jsonb AS
$$
DECLARE
@ -56,10 +60,19 @@ DECLARE
bootstrap_in jsonb;
BEGIN
IF security_mode_lw IS NULL THEN
security_mode_lw := 'NO_SEC';
END IF;
IF security_mode_bs IS NULL THEN
security_mode_bs := 'NO_SEC';
END IF;
bootstrap_in := transport_configuration_in::jsonb #> '{bootstrap}';
bootstrap_new := json_build_array(
json_build_object('shortServerId', bootstrap_in::json #> '{bootstrapServer}' -> 'serverId',
'securityMode', bootstrap_in::json #> '{bootstrapServer}' ->> 'securityMode',
'securityMode', security_mode_bs,
'binding', bootstrap_in::json #> '{servers}' ->> 'binding',
'lifetime', bootstrap_in::json #> '{servers}' -> 'lifetime',
'notifIfDisabled', bootstrap_in::json #> '{servers}' -> 'notifIfDisabled',
@ -73,7 +86,7 @@ BEGIN
bootstrap_in::json #> '{bootstrapServer}' -> 'bootstrapServerAccountTimeout'
),
json_build_object('shortServerId', bootstrap_in::json #> '{lwm2mServer}' -> 'serverId',
'securityMode', bootstrap_in::json #> '{lwm2mServer}' ->> 'securityMode',
'securityMode', security_mode_lw,
'binding', bootstrap_in::json #> '{servers}' ->> 'binding',
'lifetime', bootstrap_in::json #> '{servers}' -> 'lifetime',
'notifIfDisabled', bootstrap_in::json #> '{servers}' -> 'notifIfDisabled',
@ -93,7 +106,7 @@ BEGIN
bootstrap_new,
true) || '{"bootstrapServerUpdateEnable": true}';
END ;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE PROCEDURE update_device_credentials_to_base64_and_bootstrap()
@ -102,9 +115,9 @@ $$
BEGIN
UPDATE device_credentials
SET credentials_value = get_device_and_bootstrap(credentials_value::text)
WHERE credentials_type = 'LWM2M_CREDENTIALS';
UPDATE device_credentials
SET credentials_value = get_device_and_bootstrap(credentials_value::text)
WHERE credentials_type = 'LWM2M_CREDENTIALS';
END;
$$;
@ -112,7 +125,7 @@ CREATE OR REPLACE FUNCTION get_device_and_bootstrap(IN credentials_value text, O
LANGUAGE plpgsql AS
$$
DECLARE
client_secret_key text;
client_secret_key text;
client_public_key_or_id text;
client_key_value_object jsonb;
client_bootstrap_server_value_object jsonb;
@ -130,7 +143,7 @@ BEGIN
'key', client_public_key_or_id);
credentials_value_new :=
credentials_value_new::jsonb || json_build_object('client', client_key_value_object)::jsonb;
END IF;
END IF;
IF credentials_value::jsonb #> '{client}' ->> 'securityConfigClientMode' = 'X509' AND
NULLIF((credentials_value::jsonb #> '{client}' ->> 'cert' ~ '^[0-9a-fA-F]+$')::text, 'false') = 'true' THEN
client_public_key_or_id :=
@ -141,8 +154,8 @@ END IF;
'cert', client_public_key_or_id);
credentials_value_new :=
credentials_value_new::jsonb || json_build_object('client', client_key_value_object)::jsonb;
END IF;
END IF;
IF credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'securityMode' = 'RPK' OR
credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'securityMode' = 'X509' THEN
IF NULLIF((credentials_value::jsonb #> '{bootstrap,lwm2mServer}' ->> 'clientSecretKey' ~ '^[0-9a-fA-F]+$')::text,
@ -165,9 +178,9 @@ END IF;
client_bootstrap_object := credentials_value_new::jsonb #> '{bootstrap}' || client_bootstrap_server_object::jsonb;
credentials_value_new :=
jsonb_set(credentials_value_new::jsonb, '{bootstrap}', client_bootstrap_object::jsonb, false)::jsonb;
END IF;
END IF;
END IF;
END IF;
IF credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'securityMode' = 'RPK' OR
credentials_value::jsonb #> '{bootstrap,bootstrapServer}' ->> 'securityMode' = 'X509' THEN
IF NULLIF(
@ -193,8 +206,8 @@ END IF;
client_bootstrap_object := credentials_value_new::jsonb #> '{bootstrap}' || client_bootstrap_server_object::jsonb;
credentials_value_new :=
jsonb_set(credentials_value_new::jsonb, '{bootstrap}', client_bootstrap_object::jsonb, false)::jsonb;
END IF;
END IF;
END IF;
END IF;
END;
$$;

13
application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java

@ -178,8 +178,8 @@ public class RpcV2Controller extends AbstractRpcController {
@RequestParam int pageSize,
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@ApiParam(value = "Status of the RPC", required = true, allowableValues = RPC_STATUS_ALLOWABLE_VALUES)
@RequestParam RpcStatus rpcStatus,
@ApiParam(value = "Status of the RPC", allowableValues = RPC_STATUS_ALLOWABLE_VALUES)
@RequestParam(required = false) RpcStatus rpcStatus,
@ApiParam(value = RPC_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RPC_SORT_PROPERTY_ALLOWABLE_VALUES)
@ -188,7 +188,7 @@ public class RpcV2Controller extends AbstractRpcController {
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("DeviceId", strDeviceId);
try {
if (rpcStatus.equals(RpcStatus.DELETED)) {
if (rpcStatus != null && rpcStatus.equals(RpcStatus.DELETED)) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "RpcStatus: DELETED");
}
@ -200,7 +200,12 @@ public class RpcV2Controller extends AbstractRpcController {
accessValidator.validate(getCurrentUser(), Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
PageData<Rpc> rpcCalls = rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink);
PageData<Rpc> rpcCalls;
if (rpcStatus != null) {
rpcCalls = rpcService.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink);
} else {
rpcCalls = rpcService.findAllByDeviceId(tenantId, deviceId, pageLink);
}
response.setResult(new ResponseEntity<>(rpcCalls, HttpStatus.OK));
}

4
application/src/main/java/org/thingsboard/server/controller/UserController.java

@ -302,6 +302,10 @@ public class UserController extends BaseController {
UserId userId = new UserId(toUUID(strUserId));
User user = checkUserId(userId, Operation.DELETE);
if (user.getAuthority() == Authority.SYS_ADMIN && getCurrentUser().getId().equals(userId)) {
throw new ThingsboardException("Sysadmin is not allowed to delete himself", ThingsboardErrorCode.PERMISSION_DENIED);
}
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(getTenantId(), userId);
userService.deleteUser(getCurrentUser().getTenantId(), userId);

4
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -476,9 +476,9 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.2", SCHEMA_UPDATE_SQL);
loadSql(schemaUpdateFile, conn);
try {
conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, type, customer_id, alarm_id)" +
conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, alarm_type, customer_id, alarm_id)" +
" select tenant_id, originator_id, created_time, type, customer_id, id from alarm;");
conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, type, customer_id, alarm_id)" +
conn.createStatement().execute("insert into entity_alarm(tenant_id, entity_id, created_time, alarm_type, customer_id, alarm_id)" +
" select a.tenant_id, r.from_id, created_time, type, customer_id, id" +
" from alarm a inner join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id ON CONFLICT DO NOTHING;");
conn.createStatement().execute("delete from relation r where r.relation_type_group = 'ALARM';");

18
application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServiceImpl.java

@ -63,6 +63,12 @@ public class LwM2MServiceImpl implements LwM2MService {
} else {
bsServ.setServerPublicKey(Base64.encodeBase64String(publicKeyBase64));
}
byte[] certificateBase64 = getCertificate(bsServerConfig);
if (certificateBase64 == null) {
bsServ.setServerCertificate("");
} else {
bsServ.setServerCertificate(Base64.encodeBase64String(certificateBase64));
}
return bsServ;
}
@ -77,5 +83,17 @@ public class LwM2MServiceImpl implements LwM2MService {
}
return null;
}
private byte[] getCertificate(LwM2MSecureServerConfig config) {
try {
SslCredentials sslCredentials = config.getSslCredentials();
if (sslCredentials != null) {
return sslCredentials.getCertificateChain()[0].getEncoded();
}
} catch (Exception e) {
log.trace("Failed to fetch certificate from key store!", e);
}
return null;
}
}

2
application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java

@ -257,7 +257,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService<
final TbRuleEngineProcessingStrategy ackStrategy = getAckStrategy(configuration);
submitStrategy.init(msgs);
while (!stopped) {
TbMsgPackProcessingContext ctx = new TbMsgPackProcessingContext(configuration.getName(), submitStrategy);
TbMsgPackProcessingContext ctx = new TbMsgPackProcessingContext(configuration.getName(), submitStrategy, ackStrategy.isSkipTimeoutMsgs());
submitStrategy.submitAttempt((id, msg) -> submitExecutor.submit(() -> submitMessage(configuration, stats, ctx, id, msg)));
final boolean timeout = !ctx.await(configuration.getPackProcessingTimeout(), TimeUnit.MILLISECONDS);

2
application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackCallback.java

@ -68,7 +68,7 @@ public class TbMsgPackCallback implements TbMsgCallback {
@Override
public boolean isMsgValid() {
return !ctx.isComplete();
return !ctx.isCanceled();
}
@Override

14
application/src/main/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContext.java

@ -39,6 +39,7 @@ public class TbMsgPackProcessingContext {
private final String queueName;
private final TbRuleEngineSubmitStrategy submitStrategy;
private final boolean skipTimeoutMsgsPossible;
@Getter
private final boolean profilerEnabled;
private final AtomicInteger pendingCount;
@ -53,12 +54,13 @@ public class TbMsgPackProcessingContext {
private final ConcurrentMap<TenantId, RuleEngineException> exceptionsMap = new ConcurrentHashMap<>();
private final ConcurrentMap<UUID, RuleNodeInfo> lastRuleNodeMap = new ConcurrentHashMap<>();
@Getter
private volatile boolean complete = false;
public TbMsgPackProcessingContext(String queueName, TbRuleEngineSubmitStrategy submitStrategy) {
private volatile boolean canceled = false;
public TbMsgPackProcessingContext(String queueName, TbRuleEngineSubmitStrategy submitStrategy, boolean skipTimeoutMsgsPossible) {
this.queueName = queueName;
this.submitStrategy = submitStrategy;
this.skipTimeoutMsgsPossible = skipTimeoutMsgsPossible;
this.profilerEnabled = log.isDebugEnabled();
this.pendingMap = submitStrategy.getPendingMap();
this.pendingCount = new AtomicInteger(pendingMap.size());
@ -151,9 +153,13 @@ public class TbMsgPackProcessingContext {
}
public void cleanup() {
complete = true;
canceled = true;
pendingMap.clear();
successMap.clear();
failedMap.clear();
}
public boolean isCanceled() {
return skipTimeoutMsgsPossible && canceled;
}
}

2
application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategy.java

@ -17,6 +17,8 @@ package org.thingsboard.server.service.queue.processing;
public interface TbRuleEngineProcessingStrategy {
boolean isSkipTimeoutMsgs();
TbRuleEngineProcessingDecision analyze(TbRuleEngineProcessingResult result);
}

18
application/src/main/java/org/thingsboard/server/service/queue/processing/TbRuleEngineProcessingStrategyFactory.java

@ -36,7 +36,9 @@ public class TbRuleEngineProcessingStrategyFactory {
public TbRuleEngineProcessingStrategy newInstance(String name, TbRuleEngineQueueAckStrategyConfiguration configuration) {
switch (configuration.getType()) {
case "SKIP_ALL_FAILURES":
return new SkipStrategy(name);
return new SkipStrategy(name, false);
case "SKIP_ALL_FAILURES_AND_TIMED_OUT":
return new SkipStrategy(name, true);
case "RETRY_ALL":
return new RetryStrategy(name, true, true, true, configuration);
case "RETRY_FAILED":
@ -75,6 +77,11 @@ public class TbRuleEngineProcessingStrategyFactory {
this.maxPauseBetweenRetries = configuration.getMaxPauseBetweenRetries();
}
@Override
public boolean isSkipTimeoutMsgs() {
return true;
}
@Override
public TbRuleEngineProcessingDecision analyze(TbRuleEngineProcessingResult result) {
if (result.isSuccess()) {
@ -139,9 +146,16 @@ public class TbRuleEngineProcessingStrategyFactory {
private static class SkipStrategy implements TbRuleEngineProcessingStrategy {
private final String queueName;
private final boolean skipTimeoutMsgs;
public SkipStrategy(String name) {
public SkipStrategy(String name, boolean skipTimeoutMsgs) {
this.queueName = name;
this.skipTimeoutMsgs = skipTimeoutMsgs;
}
@Override
public boolean isSkipTimeoutMsgs() {
return skipTimeoutMsgs;
}
@Override

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

@ -743,7 +743,7 @@ transport:
# Server X509 Certificates support
credentials:
# Whether to enable LWM2M server X509 Certificate/RPK support
enabled: "${LWM2M_SERVER_CREDENTIALS_ENABLED:true}"
enabled: "${LWM2M_SERVER_CREDENTIALS_ENABLED:false}"
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${LWM2M_SERVER_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
@ -779,7 +779,7 @@ transport:
# Bootstrap server X509 Certificates support
credentials:
# Whether to enable LWM2M bootstrap server X509 Certificate/RPK support
enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:true}"
enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:false}"
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${LWM2M_BS_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
@ -806,19 +806,19 @@ transport:
# X509 trust certificates
trust-credentials:
# Whether to load X509 trust certificates
enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:true}"
enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:false}"
# Trust certificates store type (PEM - pem certificates file; KEYSTORE - java keystore)
type: "${LWM2M_TRUST_CREDENTIALS_TYPE:PEM}"
# PEM certificates
pem:
# Path to the certificates file (holds trust certificates)
cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mserver.pem}"
cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mtruststorechain.pem}"
# Keystore with trust certificates
keystore:
# Type of the key store
type: "${LWM2M_TRUST_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the X509 certificates
store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mserver.jks}"
store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mtruststorechain.jks}"
# Password used to access the key store
store_password: "${LWM2M_TRUST_KEY_STORE_PASSWORD:server_ks_password}"
recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
@ -1043,7 +1043,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -1060,7 +1060,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -1077,7 +1077,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;

1
application/src/test/java/org/thingsboard/server/service/ServiceSqlTestSuite.java

@ -23,6 +23,7 @@ import org.thingsboard.server.queue.memory.InMemoryStorage;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.service.resource.sql.*Test",
"org.thingsboard.server.service.sql.*Test"
})
public class ServiceSqlTestSuite {

2
application/src/test/java/org/thingsboard/server/service/queue/TbMsgPackProcessingContextTest.java

@ -69,7 +69,7 @@ public class TbMsgPackProcessingContextTest {
TbRuleEngineSubmitStrategy strategyMock = mock(TbRuleEngineSubmitStrategy.class);
when(strategyMock.getPendingMap()).thenReturn(messages);
TbMsgPackProcessingContext context = new TbMsgPackProcessingContext("Main", strategyMock);
TbMsgPackProcessingContext context = new TbMsgPackProcessingContext("Main", strategyMock, false);
for (UUID uuid : messages.keySet()) {
final CountDownLatch readyLatch = new CountDownLatch(parallelCount);
final CountDownLatch startLatch = new CountDownLatch(1);

207
application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java

@ -0,0 +1,207 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.sql;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode;
import org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNodeConfiguration;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.JsonDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@DaoSqlTest
public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest {
static final int TIMEOUT = 30;
final String TOTALIZER = "Totalizer";
final int TTL = 99999;
final String GENERIC_CUMULATIVE_OBJ = "genericCumulativeObj";
final List<Long> ts = List.of(10L, 20L, 30L, 40L, 60L, 70L, 50L, 80L);
final List<Long> msgValue = List.of(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L);
@Autowired
TimeseriesService timeseriesService;
TbMsgTimeseriesNodeConfiguration configuration;
Tenant savedTenant;
User tenantAdmin;
@Before
public void beforeTest() throws Exception {
configuration = new TbMsgTimeseriesNodeConfiguration();
configuration.setUseServerTs(true);
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
}
@After
public void afterTest() throws Exception {
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk());
}
@Test
public void testSequentialTimeseriesPersistence() throws Exception {
Asset asset = saveAsset("Asset");
Device deviceA = saveDevice("Device A");
Device deviceB = saveDevice("Device B");
Device deviceC = saveDevice("Device C");
Device deviceD = saveDevice("Device D");
List<Device> devices = List.of(deviceA, deviceB, deviceC, deviceD);
for (int i = 0; i < 2; i++) {
int idx = i * devices.size();
saveLatestTsForAssetAndDevice(devices, asset, idx);
checkDiffBetweenLatestTsForDevicesAndAsset(devices, asset);
}
}
Device saveDevice(String name) throws Exception {
Device device = new Device();
device.setName(name);
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
Assert.assertNotNull(savedDevice);
return savedDevice;
}
Asset saveAsset(String name) throws Exception {
Asset asset = new Asset();
asset.setName(name);
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
Assert.assertNotNull(savedAsset);
return savedAsset;
}
void saveLatestTsForAssetAndDevice(List<Device> devices, Asset asset, int idx) throws ExecutionException, InterruptedException, TimeoutException {
for (Device device : devices) {
TbMsg tbMsg = TbMsg.newMsg(SessionMsgType.POST_TELEMETRY_REQUEST.name(),
device.getId(),
getTbMsgMetadata(device.getName(), ts.get(idx)),
TbMsgDataType.JSON,
getTbMsgData(msgValue.get(idx)));
saveDeviceTsEntry(device.getId(), tbMsg, msgValue.get(idx));
saveAssetTsEntry(asset, device.getName(), msgValue.get(idx), TbMsgTimeseriesNode.computeTs(tbMsg, configuration.isUseServerTs()));
idx++;
}
}
void checkDiffBetweenLatestTsForDevicesAndAsset(List<Device> devices, Asset asset) throws ExecutionException, InterruptedException, TimeoutException {
TsKvEntry assetTsKvEntry = getTsKvLatest(asset.getId(), GENERIC_CUMULATIVE_OBJ);
Assert.assertTrue(assetTsKvEntry.getJsonValue().isPresent());
JsonObject assetJsonObject = new JsonParser().parse(assetTsKvEntry.getJsonValue().get()).getAsJsonObject();
for (Device device : devices) {
Long assetValue = assetJsonObject.get(device.getName()).getAsLong();
TsKvEntry deviceLatest = getTsKvLatest(device.getId(), TOTALIZER);
Assert.assertTrue(deviceLatest.getLongValue().isPresent());
Long deviceValue = deviceLatest.getLongValue().get();
Assert.assertEquals(assetValue, deviceValue);
}
}
String getTbMsgData(long value) {
return "{\"Totalizer\": " + value + "}";
}
TbMsgMetaData getTbMsgMetadata(String name, long ts) {
Map<String, String> metadata = new HashMap<>();
metadata.put("deviceName", name);
metadata.put("ts", String.valueOf(ts));
return new TbMsgMetaData(metadata);
}
void saveDeviceTsEntry(EntityId entityId, TbMsg tbMsg, long value) throws ExecutionException, InterruptedException, TimeoutException {
TsKvEntry tsKvEntry = new BasicTsKvEntry(TbMsgTimeseriesNode.computeTs(tbMsg, configuration.isUseServerTs()), new LongDataEntry(TOTALIZER, value));
saveTimeseries(entityId, tsKvEntry);
}
void saveAssetTsEntry(Asset asset, String key, long value, long ts) throws ExecutionException, InterruptedException, TimeoutException {
Optional<String> tsKvEntryOpt = getTsKvLatest(asset.getId(), GENERIC_CUMULATIVE_OBJ).getJsonValue();
TsKvEntry saveTsKvEntry = new BasicTsKvEntry(ts, new JsonDataEntry(GENERIC_CUMULATIVE_OBJ, getJsonObject(key, value, tsKvEntryOpt).toString()));
saveTimeseries(asset.getId(), saveTsKvEntry);
}
@NotNull
JsonObject getJsonObject(String key, long value, Optional<String> tsKvEntryOpt) {
JsonObject jsonObject = new JsonObject();
if (tsKvEntryOpt.isPresent()) {
jsonObject = new JsonParser().parse(tsKvEntryOpt.get()).getAsJsonObject();
}
jsonObject.addProperty(key, value);
return jsonObject;
}
void saveTimeseries(EntityId entityId, TsKvEntry saveTsKvEntry) throws InterruptedException, ExecutionException, TimeoutException {
timeseriesService.save(savedTenant.getId(), entityId, List.of(saveTsKvEntry), TTL).get(TIMEOUT, TimeUnit.SECONDS);
}
TsKvEntry getTsKvLatest(EntityId entityId, String key) throws InterruptedException, ExecutionException, TimeoutException {
List<TsKvEntry> tsKvEntries = timeseriesService.findLatest(
savedTenant.getTenantId(),
entityId,
List.of(key)).get(TIMEOUT, TimeUnit.SECONDS);
Assert.assertEquals(1, tsKvEntries.size());
return tsKvEntries.get(0);
}
}

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

@ -69,7 +69,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@DaoSqlTest
public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
protected String transportConfiguration = "{\n" +
protected final String TRANSPORT_CONFIGURATION = "{\n" +
" \"type\": \"LWM2M\",\n" +
" \"observeAttr\": {\n" +
" \"keyName\": {\n" +
@ -133,7 +133,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
protected LwM2MTestClient client;
private final LwM2MBootstrapClientCredentials defaultBootstrapCredentials;
private String[] resources;
protected String endpoint;
public AbstractLwM2MIntegrationTest() {
this.defaultBootstrapCredentials = new LwM2MBootstrapClientCredentials();
@ -177,7 +176,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
LwM2MClientCredential credentials,
NetworkConfig coapConfig,
String endpoint) throws Exception {
createDeviceProfile(transportConfiguration);
createDeviceProfile(TRANSPORT_CONFIGURATION);
Device device = createDevice(credentials);
SingleEntityFilter sef = new SingleEntityFilter();
@ -195,8 +194,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
wsClient.waitForReply();
wsClient.registerWaitForUpdate();
this.endpoint = endpoint;
createNewClient(security, coapConfig, false);
createNewClient(security, coapConfig, false, endpoint);
String msg = wsClient.waitForUpdate();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
@ -261,13 +259,9 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
this.resources = resources;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public void createNewClient(Security security, NetworkConfig coapConfig, boolean isRpc) throws Exception {
public void createNewClient(Security security, NetworkConfig coapConfig, boolean isRpc, String endpoint) throws Exception {
clientDestroy();
client = new LwM2MTestClient(this.executor, this.endpoint);
client = new LwM2MTestClient(this.executor, endpoint);
int clientPort = SocketUtils.findAvailableTcpPort();
client.init(security, coapConfig, clientPort, isRpc);
}

54
application/src/test/java/org/thingsboard/server/transport/lwm2m/Lwm2mTestHelper.java

@ -25,18 +25,16 @@ public class Lwm2mTestHelper {
// Server
public static final int SECURE_PORT = 5686;
public static final int SECURE_PORT_BS = 5688;
public static final String HOST = "localhost";
public static final String HOST_BS = "localhost";
public static final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT));
public static final String ENDPOINT_SECURITY = "deviceAEndpoint";
public static final String SECURE_URI = "coaps://localhost:" + SECURE_PORT;
public static final int PORT = 5685;
public static final int PORT_BS = 5687;
public static final String HOST = "localhost";
public static final String HOST_BS = "localhost";
public static final int SHORT_SERVER_ID = 123;
public static final int SHORT_SERVER_ID_BS = 111;
public static final Security SECURITY = noSec("coap://localhost:" + PORT, SHORT_SERVER_ID);
public static final NetworkConfig SECURE_COAP_CONFIG = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(SECURE_PORT));
public static final String SECURE_URI = "coaps://" + HOST + ":" + SECURE_PORT;
public static final Security SECURITY = noSec("coap://"+ HOST +":" + PORT, SHORT_SERVER_ID);
public static final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT));
// Models
@ -45,26 +43,26 @@ public class Lwm2mTestHelper {
public static final int TEMPERATURE_SENSOR = 3303;
// Ids in Client
public static final int objectId_0 = 0;
public static final int objectInstanceId_0 = 0;
public static final int objectInstanceId_1 = 1;
public static final int objectInstanceId_2 = 2;
public static final int objectInstanceId_12 = 12;
public static final int resourceId_0 = 0;
public static final int resourceId_1 = 1;
public static final int resourceId_2 = 2;
public static final int resourceId_3 = 3;
public static final int resourceId_4 = 4;
public static final int resourceId_8 = 8;
public static final int resourceId_9 = 9;
public static final int resourceId_11 = 11;
public static final int resourceId_14 = 14;
public static final int resourceId_15= 15;
public static final int resourceInstanceId_2 = 2;
public static final String resourceIdName_3_9 = "batteryLevel";
public static final String resourceIdName_3_14 = "UtfOffset";
public static final String resourceIdName_19_0_0 = "dataRead";
public static final String resourceIdName_19_1_0 = "dataWrite";
public static final int OBJECT_ID_0 = 0;
public static final int OBJECT_INSTANCE_ID_0 = 0;
public static final int OBJECT_INSTANCE_ID_1 = 1;
public static final int OBJECT_INSTANCE_ID_2 = 2;
public static final int OBJECT_INSTANCE_ID_12 = 12;
public static final int RESOURCE_ID_0 = 0;
public static final int RESOURCE_ID_1 = 1;
public static final int RESOURCE_ID_2 = 2;
public static final int RESOURCE_ID_3 = 3;
public static final int RESOURCE_ID_4 = 4;
public static final int RESOURCE_ID_7 = 7;
public static final int RESOURCE_ID_8 = 8;
public static final int RESOURCE_ID_9 = 9;
public static final int RESOURCE_ID_11 = 11;
public static final int RESOURCE_ID_14 = 14;
public static final int RESOURCE_ID_15 = 15;
public static final int RESOURCE_INSTANCE_ID_2 = 2;
public static final String RESOURCE_ID_NAME_3_9 = "batteryLevel";
public static final String RESOURCE_ID_NAME_3_14 = "UtfOffset";
public static final String RESOURCE_ID_NAME_19_0_0 = "dataRead";
public static final String RESOURCE_ID_NAME_19_1_0 = "dataWrite";
}

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

@ -62,9 +62,9 @@ import static org.eclipse.leshan.core.LwM2mId.SERVER;
import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_12;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources;
@ -99,12 +99,12 @@ public class LwM2MTestClient {
initializer.setInstancesForObject(FIRMWARE, fwLwM2MDevice = new FwLwM2MDevice());
initializer.setInstancesForObject(SOFTWARE_MANAGEMENT, swLwM2MDevice = new SwLwM2MDevice());
initializer.setClassForObject(ACCESS_CONTROL, DummyInstanceEnabler.class);
initializer.setInstancesForObject(BINARY_APP_DATA_CONTAINER, lwM2MBinaryAppDataContainer = new LwM2mBinaryAppDataContainer(executor, objectInstanceId_0),
new LwM2mBinaryAppDataContainer(executor, objectInstanceId_1));
initializer.setInstancesForObject(BINARY_APP_DATA_CONTAINER, lwM2MBinaryAppDataContainer = new LwM2mBinaryAppDataContainer(executor, OBJECT_INSTANCE_ID_0),
new LwM2mBinaryAppDataContainer(executor, OBJECT_INSTANCE_ID_1));
locationParams = new LwM2MLocationParams();
locationParams.getPos();
initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, objectInstanceId_0));
initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2MTemperatureSensor = new LwM2mTemperatureSensor(executor, objectInstanceId_0), new LwM2mTemperatureSensor(executor, objectInstanceId_12));
initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0));
initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2MTemperatureSensor = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0), new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12));
DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
dtlsConfig.setRecommendedCipherSuitesOnly(true);
@ -130,16 +130,6 @@ public class LwM2MTestClient {
ObservationStore store) {
CoapEndpoint.Builder builder = new CoapEndpoint.Builder();
DtlsConnectorConfig.Builder dtlsConfigBuilder = new DtlsConnectorConfig.Builder(dtlsConfig);
// tricks to be able to change psk information on the fly
// AdvancedPskStore pskStore = dtlsConfig.getAdvancedPskStore();
// if (pskStore != null) {
// PskPublicInformation identity = pskStore.getIdentity(null, null);
// SecretKey key = pskStore
// .requestPskSecretResult(ConnectionId.EMPTY, null, identity, null, null, null).getSecret();
// singlePSKStore = new SinglePSKStore(identity, key);
// dtlsConfigBuilder.setAdvancedPskStore(singlePSKStore);
// }
builder.setConnector(new DTLSConnector(dtlsConfigBuilder.build()));
builder.setNetworkConfig(coapConfig);
return builder.build();
@ -283,5 +273,4 @@ public class LwM2MTestClient {
client.start();
}
}
}

11
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java

@ -66,8 +66,7 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
* "value":4
* },
*/
// private String data = "InNlcnZpY2VJZCI6Ik1ldGVyIiwNCiJzZXJ2aWNlRGF0YSI6ew0KImN1cnJlbnRSZWFkaW5nIjoiNDYuMyIsDQoic2lnbmFsU3RyZW5ndGgiOjE2LA0KImRhaWx5QWN0aXZpdHlUaW1lIjo1NzA2DQo=";
// private byte[] data;
Map<Integer, byte[]> data;
private Integer priority = 0;
private Time timestamp;
@ -83,7 +82,6 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
try {
if (id != null) this.setId(id);
executorService.scheduleWithFixedDelay(() ->
// fireResourcesChange(0, 2), 5000, 5000, TimeUnit.MILLISECONDS);
fireResourcesChange(0, 2), 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN
} catch (Throwable e) {
log.error("[{}]Throwable", e.toString());
@ -93,15 +91,11 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
@Override
public ReadResponse read(ServerIdentity identity, int resourceId) {
// log.warn("Read on Location resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId);
try {
switch (resourceId) {
case 0:
// log.warn("Read on Location resource /[{}]/[{}]/[{}]", getModel().id, getId(), resourceId);
ReadResponse response = ReadResponse.success(resourceId, getData(), ResourceModel.Type.OPAQUE);
// log.warn("Response [{}]", response);
return response;
case 1:
return ReadResponse.success(resourceId, getPriority());
case 2:
@ -168,7 +162,6 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
}
private String getDataFormat() {
// return this.dataFormat == null ? "base64" : this.dataFormat;
return this.dataFormat == null ? "OPAQUE" : this.dataFormat;
}
@ -188,7 +181,6 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
return this.timestamp != null ? this.timestamp : new Time(new Date().getTime());
}
// fireResourcesChange(resourceId);
private boolean setData(LwM2mResource value, boolean replace) {
try {
if (value instanceof LwM2mMultipleResource) {
@ -208,7 +200,6 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements
}
private Map<Integer, byte[]> getData() {
// this.data.put(23, new byte[]{0,0, 2,3});
return data;
}

4
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mLocation.java

@ -101,19 +101,15 @@ public class LwM2mLocation extends BaseInstanceEnabler implements Destroyable {
switch (nextMove.charAt(0)) {
case 'w':
moveLatitude(1.0f);
// log.info("Move to North [{}]/[{}]", getLatitude(), getLongitude());
break;
case 'a':
moveLongitude(-1.0f);
// log.info("Move to East [{}]/[{}]", getLatitude(), getLongitude());
break;
case 's':
moveLatitude(-1.0f);
// log.info("Move to South [{}]/[{}]", getLatitude(), getLongitude());
break;
case 'd':
moveLongitude(1.0f);
// log.info("Move to West [{}]/[{}]", getLatitude(), getLongitude());
break;
}
}

7
application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java

@ -29,10 +29,13 @@ import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE;
@DaoSqlTest
public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
private final String[] resources = new String[]{"3.xml", "5.xml", "9.xml"};
private final String[] RESOURCES_OTA = new String[]{"3.xml", "5.xml", "9.xml"};
protected static final String CLIENT_ENDPOINT_WITHOUT_FW_INFO = "WithoutFirmwareInfoDevice";
protected static final String CLIENT_ENDPOINT_OTA5 = "Ota5_Device";
protected static final String CLIENT_ENDPOINT_OTA9 = "Ota9_Device";
public AbstractOtaLwM2MIntegrationTest() {
setResources(this.resources);
setResources(this.RESOURCES_OTA);
}
protected OtaPackageInfo createFirmware() throws Exception {

20
application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java

@ -138,12 +138,10 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
@Test
public void testFirmwareUpdateWithClientWithoutFirmwareOtaInfoFromProfile() throws Exception {
String endpoint = "WithoutFirmwareInfoDevice";
setEndpoint(endpoint);
createDeviceProfile(transportConfiguration);
NoSecClientCredential credentials = createNoSecClientCredentials(endpoint);
createDeviceProfile(TRANSPORT_CONFIGURATION);
NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
final Device device = createDevice(credentials);
createNewClient(SECURITY, COAP_CONFIG, false);
createNewClient(SECURITY, COAP_CONFIG, false, this.CLIENT_ENDPOINT_WITHOUT_FW_INFO);
Thread.sleep(1000);
@ -165,12 +163,10 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
@Test
public void testFirmwareUpdateByObject5() throws Exception {
String endpoint = "Ota5_Device";
setEndpoint(endpoint);
createDeviceProfile(OTA_TRANSPORT_CONFIGURATION);
NoSecClientCredential credentials = createNoSecClientCredentials(endpoint);
NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA5);
final Device device = createDevice(credentials);
createNewClient(SECURITY, COAP_CONFIG, false);
createNewClient(SECURITY, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA5);
Thread.sleep(1000);
@ -204,12 +200,10 @@ public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
* */
@Test
public void testSoftwareUpdateByObject9() throws Exception {
String endpoint = "Ota9_Device";
setEndpoint(endpoint);
createDeviceProfile(OTA_TRANSPORT_CONFIGURATION);
NoSecClientCredential credentials = createNoSecClientCredentials(endpoint);
NoSecClientCredential credentials = createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA9);
final Device device = createDevice(credentials);
createNewClient(SECURITY, COAP_CONFIG, false);
createNewClient(SECURITY, COAP_CONFIG, false, this.CLIENT_ENDPOINT_OTA9);
Thread.sleep(1000);

63
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/AbstractRpcLwM2MIntegrationTest.java

@ -18,12 +18,10 @@ package org.thingsboard.server.transport.lwm2m.rpc;
import org.junit.Before;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredential;
import org.thingsboard.server.controller.TbTestWebSocketClient;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
@ -36,16 +34,16 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.COAP_CONFIG;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.SECURITY;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_0_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_1_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_0_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_1_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resources;
@DaoSqlTest
@ -53,8 +51,6 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg
protected String RPC_TRANSPORT_CONFIGURATION;
protected ScheduledExecutorService executor;
protected TbTestWebSocketClient wsClient;
protected String deviceId;
public Set expectedObjects;
public Set expectedObjectIdVers;
@ -64,15 +60,16 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg
protected String objectInstanceIdVer_1;
protected String objectIdVer_0;
protected String objectIdVer_2;
private static final Predicate predicate_3 = path -> (!((String) path).contains("/" + TEMPERATURE_SENSOR) && ((String) path).contains("/" + DEVICE));
private static final Predicate PREDICATE_3 = path -> (!((String) path).contains("/" + TEMPERATURE_SENSOR) && ((String) path).contains("/" + DEVICE));
protected String objectIdVer_3;
protected String objectInstanceIdVer_3;
protected String objectInstanceIdVer_5;
protected String objectInstanceIdVer_9;
protected String objectIdVer_19;
protected String objectIdVer_50 = "/50";
protected final String OBJECT_ID_VER_50 = "/50";
protected String objectIdVer_3303;
protected static AtomicInteger endpointSequence = new AtomicInteger();
protected static String DEVICE_ENDPOINT_RPC_PREF = "deviceEndpointRpc";
public AbstractRpcLwM2MIntegrationTest(){
setResources(resources);
@ -80,9 +77,9 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg
@Before
public void beforeTest() throws Exception {
setEndpoint("deviceEndpointRpc" + endpointSequence.incrementAndGet());
String endpoint = DEVICE_ENDPOINT_RPC_PREF + endpointSequence.incrementAndGet();
init();
createNewClient (SECURITY, COAP_CONFIG, true);
createNewClient (SECURITY, COAP_CONFIG, true, endpoint);
expectedObjects = ConcurrentHashMap.newKeySet();
expectedObjectIdVers = ConcurrentHashMap.newKeySet();
@ -103,19 +100,19 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg
});
}
});
String ver_Id_0 = client.getClient().getObjectTree().getModel().getObjectModel(objectId_0).version;
String ver_Id_0 = client.getClient().getObjectTree().getModel().getObjectModel(OBJECT_ID_0).version;
if ("1.0".equals(ver_Id_0)) {
objectIdVer_0 = "/" + objectId_0;
objectIdVer_0 = "/" + OBJECT_ID_0;
}
else {
objectIdVer_0 = "/" + objectId_0 + "_" + ver_Id_0;
objectIdVer_0 = "/" + OBJECT_ID_0 + "_" + ver_Id_0;
}
objectIdVer_2 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + ACCESS_CONTROL)).findFirst().get();
objectIdVer_3 = (String) expectedObjects.stream().filter(predicate_3).findFirst().get();
objectIdVer_3 = (String) expectedObjects.stream().filter(PREDICATE_3).findFirst().get();
objectIdVer_19 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + BINARY_APP_DATA_CONTAINER)).findFirst().get();
objectIdVer_3303 = (String) expectedObjectIdVers.stream().filter(path -> ((String) path).contains("/" + TEMPERATURE_SENSOR)).findFirst().get();
objectInstanceIdVer_1 = (String) expectedObjectIdVerInstances.stream().filter(path -> (!((String) path).contains("/" + BINARY_APP_DATA_CONTAINER) && ((String) path).contains("/" + SERVER))).findFirst().get();
objectInstanceIdVer_3 = (String) expectedObjectIdVerInstances.stream().filter(predicate_3).findFirst().get();
objectInstanceIdVer_3 = (String) expectedObjectIdVerInstances.stream().filter(PREDICATE_3).findFirst().get();
objectInstanceIdVer_5 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + FIRMWARE)).findFirst().get();
objectInstanceIdVer_9 = (String) expectedObjectIdVerInstances.stream().filter(path -> ((String) path).contains("/" + SOFTWARE_MANAGEMENT)).findFirst().get();
@ -123,22 +120,22 @@ public abstract class AbstractRpcLwM2MIntegrationTest extends AbstractLwM2MInteg
" \"type\": \"LWM2M\",\n" +
" \"observeAttr\": {\n" +
" \"keyName\": {\n" +
" \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\": \"" + resourceIdName_3_9 + "\",\n" +
" \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_14 + "\": \"" + resourceIdName_3_14 + "\",\n" +
" \"" + objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\": \"" + resourceIdName_19_0_0 + "\",\n" +
" \"" + objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "\": \"" + resourceIdName_19_1_0 + "\"\n" +
" \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_9 + "\": \"" + RESOURCE_ID_NAME_3_9 + "\",\n" +
" \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_14 + "\": \"" + RESOURCE_ID_NAME_3_14 + "\",\n" +
" \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0 + "\": \"" + RESOURCE_ID_NAME_19_0_0 + "\",\n" +
" \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "\": \"" + RESOURCE_ID_NAME_19_1_0 + "\"\n" +
" },\n" +
" \"observe\": [\n" +
" \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\",\n" +
" \"" + objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\"\n" +
" \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_9 + "\",\n" +
" \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0 + "\"\n" +
" ],\n" +
" \"attribute\": [\n" +
" ],\n" +
" \"telemetry\": [\n" +
" \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_9 + "\",\n" +
" \"" + objectIdVer_3 + "/" + objectInstanceId_0 + "/" + resourceId_14 + "\",\n" +
" \"" + objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "\",\n" +
" \"" + objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "\"\n" +
" \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_9 + "\",\n" +
" \"" + objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_14 + "\",\n" +
" \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0 + "\",\n" +
" \"" + objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "\"\n" +
" ],\n" +
" \"attributeLwm2m\": {}\n" +
" },\n" +

30
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationCreateTest.java

@ -25,10 +25,10 @@ import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTes
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_12;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0;
public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTest {
@ -43,8 +43,8 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe
*/
@Test
public void testCreateObjectInstanceWithInstanceIdByIdKey_Result_CREATED() throws Exception {
String expectedPath = objectIdVer_19 + "/" + objectInstanceId_12;
String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}, \"1\":1}";
String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_12;
String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"0\":\"00AC\"}, \"1\":1}";
String actualResult = sendRPCreateById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CREATED.getName(), rpcActualResult.get("result").asText());
@ -60,12 +60,12 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe
*/
@Test
public void testCreateObjectInstanceWithInstanceIdAlreadyExistsById_Result_BAD_REQUEST() throws Exception {
String expectedPath = objectIdVer_19 + "/" + objectInstanceId_0;
String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}, \"1\":1}";
String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0;
String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"0\":\"00AC\"}, \"1\":1}";
String actualResult = sendRPCreateById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
String expected = "instance " + objectInstanceId_0 + " already exists";
String expected = "instance " + OBJECT_INSTANCE_ID_0 + " already exists";
String actual = rpcActualResult.get("error").asText();
assertTrue(actual.equals(expected));
}
@ -77,8 +77,8 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe
*/
@Test
public void testCreateObjectInstanceWithInstanceIdMandatorySingleObjectById_Result_BAD_REQUEST() throws Exception {
String expectedPath = objectIdVer_3 + "/" + objectInstanceId_1;
String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}}";
String expectedPath = objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_1;
String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"0\":\"00AC\"}}";
String actualResult = sendRPCreateById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
@ -94,8 +94,8 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe
*/
@Test
public void testCreateObjectInstanceWithInstanceIdSecurityObjectById_Result_BAD_REQUEST() throws Exception {
String expectedPath = objectIdVer_0 + "/" + objectInstanceId_1;
String expectedValue = "{\"" + resourceId_0 + "\":{\"2\":4}}";
String expectedPath = objectIdVer_0 + "/" + OBJECT_INSTANCE_ID_1;
String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"2\":4}}";
String actualResult = sendRPCreateById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
@ -113,8 +113,8 @@ public class RpcLwm2mIntegrationCreateTest extends AbstractRpcLwM2MIntegrationTe
*/
@Test
public void testCreateObjectInstanceWithInstanceIdAbsentObjectById_Result_BAD_REQUEST() throws Exception {
String expectedPath = objectIdVer_50+ "/" + objectInstanceId_1;
String expectedValue = "{\"" + resourceId_0 + "\":{\"0\":\"00AC\"}}";
String expectedPath = OBJECT_ID_VER_50 + "/" + OBJECT_INSTANCE_ID_1;
String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"0\":\"00AC\"}}";
String actualResult = sendRPCreateById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());

14
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDeleteTest.java

@ -24,9 +24,9 @@ import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTes
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_12;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_12;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_7;
public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTest {
@ -38,7 +38,7 @@ public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTe
*/
@Test
public void testDeleteObjectInstanceIsSuchByIdKey_Result_DELETED() throws Exception {
String expectedPath = objectIdVer_3303 + "/" + objectInstanceId_12;
String expectedPath = objectIdVer_3303 + "/" + OBJECT_INSTANCE_ID_12;
String actualResult = sendRPCDeleteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.DELETED.getName(), rpcActualResult.get("result").asText());
@ -51,7 +51,7 @@ public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTe
*/
@Test
public void testDeleteObjectInstanceIsNotSuchByIdKey_Result_NOT_FOUND() throws Exception {
String expectedPath = objectIdVer_19 + "/" + objectInstanceId_12;
String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_12;
String actualResult = sendRPCDeleteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText());
@ -76,12 +76,12 @@ public class RpcLwm2mIntegrationDeleteTest extends AbstractRpcLwM2MIntegrationTe
/**
* delete resource
* Delete {"id":"/3/0/9"}
* Delete {"id":"/3/0/7"}
* {"result":"METHOD_NOT_ALLOWED"}
*/
@Test
public void testDeleteResourceByIdKey_Result_METHOD_NOT_ALLOWED() throws Exception {
String expectedPath = objectIdVer_3 + "/" + objectInstanceId_0 + resourceId_9;
String expectedPath = objectIdVer_3 + "/" + OBJECT_INSTANCE_ID_0 + RESOURCE_ID_7;
String actualResult = sendRPCDeleteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.METHOD_NOT_ALLOWED.getName(), rpcActualResult.get("result").asText());

8
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationDiscoverTest.java

@ -31,8 +31,8 @@ import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2;
public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegrationTest {
@ -141,7 +141,7 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration
*/
@Test
public void testDiscoverObjectInstanceAbsentInObject_Return_NOT_FOUND() throws Exception {
String expected = objectIdVer_2 + "/" + objectInstanceId_0;
String expected = objectIdVer_2 + "/" + OBJECT_INSTANCE_ID_0;
String actualResult = sendDiscover(expected);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText());
@ -152,7 +152,7 @@ public class RpcLwm2mIntegrationDiscoverTest extends AbstractRpcLwM2MIntegration
*/
@Test
public void testDiscoverResourceAbsentInObject_Return_NOT_FOUND() throws Exception {
String expected = objectIdVer_2 + "/" + objectInstanceId_0 + "/" + resourceId_2;
String expected = objectIdVer_2 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_2;
String actualResult = sendDiscover(expected);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText());

30
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationExecuteTest.java

@ -25,12 +25,12 @@ import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTes
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_3;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_4;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_8;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_4;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_8;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9;
public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationTest {
@ -43,7 +43,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testExecuteUpdateFWById_Result_CHANGED() throws Exception {
String expectedPath = objectInstanceIdVer_5 + "/" + resourceId_2;
String expectedPath = objectInstanceIdVer_5 + "/" + RESOURCE_ID_2;
String actualResult = sendRPCExecuteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
@ -56,7 +56,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testExecuteUpdateSWById_Result_CHANGED() throws Exception {
String expectedPath = objectInstanceIdVer_9 + "/" + resourceId_4;
String expectedPath = objectInstanceIdVer_9 + "/" + RESOURCE_ID_4;
String actualResult = sendRPCExecuteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
@ -69,7 +69,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testExecuteRebootById_Result_CHANGED() throws Exception {
String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_4;
String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_4;
String actualResult = sendRPCExecuteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
@ -82,7 +82,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testExecuteRegistrationUpdateTriggerById_Result_CHANGED() throws Exception {
String expectedPath = objectInstanceIdVer_1 + "/" + resourceId_8;
String expectedPath = objectInstanceIdVer_1 + "/" + RESOURCE_ID_8;
String actualResult = sendRPCExecuteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
@ -96,7 +96,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testExecuteResourceWithParametersById_Result_CHANGED() throws Exception {
String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_4;
String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_4;
Object expectedValue = 60;
String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
@ -110,7 +110,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testExecuteBootstrapRequestTriggerById_Result_BAD_REQUEST_Error_NoBootstrapServerConfigured() throws Exception {
String expectedPath = objectInstanceIdVer_1 + "/" + resourceId_9;
String expectedPath = objectInstanceIdVer_1 + "/" + RESOURCE_ID_9;
String actualResult = sendRPCExecuteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
@ -126,7 +126,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testExecuteResourceWithOperationNotExecuteById_Result_METHOD_NOT_ALLOWED() throws Exception {
String expectedPath = objectInstanceIdVer_5 + "/" + resourceId_3;
String expectedPath = objectInstanceIdVer_5 + "/" + RESOURCE_ID_3;
String actualResult = sendRPCExecuteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
@ -143,7 +143,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testExecuteNonExistingResourceOnNonExistingObjectById_Result_BAD_REQUEST() throws Exception {
String expectedPath = objectIdVer_50 + "/" + objectInstanceId_0 + "/" + resourceId_3;
String expectedPath = OBJECT_ID_VER_50 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_3;
String actualResult = sendRPCExecuteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());
@ -161,7 +161,7 @@ public class RpcLwm2mIntegrationExecuteTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testExecuteSecurityObjectById_Result_NOT_FOUND() throws Exception {
String expectedPath = objectIdVer_0 + "/" + objectInstanceId_0 + "/" + resourceId_3;
String expectedPath = objectIdVer_0 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_3;
String actualResult = sendRPCExecuteById(expectedPath);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.BAD_REQUEST.getName(), rpcActualResult.get("result").asText());

26
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationObserveTest.java

@ -27,10 +27,10 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_3;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_3;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9;
public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationTest {
@ -55,7 +55,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testObserveSingleResource_Result_CONTENT_Value_SingleResource() throws Exception {
String expectedIdVer = objectInstanceIdVer_3 + "/" + resourceId_9;
String expectedIdVer = objectInstanceIdVer_3 + "/" + RESOURCE_ID_9;
String actualResult = sendObserve("Observe", expectedIdVer);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText());
@ -87,7 +87,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT
@Test
public void testObserveNoImplementedInstanceOnDevice_Result_NotFound() throws Exception {
String objectInstanceIdVer = (String) expectedObjectIdVers.stream().filter(path -> ((String)path).contains("/" + ACCESS_CONTROL)).findFirst().get();
String expected = objectInstanceIdVer + "/" + objectInstanceId_0;
String expected = objectInstanceIdVer + "/" + OBJECT_INSTANCE_ID_0;
String actualResult = sendObserve("Observe", expected);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.NOT_FOUND.getName(), rpcActualResult.get("result").asText());
@ -101,7 +101,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT
@Test
public void testObserveNoImplementedResourceOnDeviceValueNull_Result_BadRequest() throws Exception {
String objectIdVer = (String) expectedObjectIdVers.stream().filter(path -> ((String)path).contains("/" + BINARY_APP_DATA_CONTAINER)).findFirst().get();
String expected = objectIdVer + "/" + objectInstanceId_0 + "/" + resourceId_0;
String expected = objectIdVer + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0;
String actualResult = sendObserve("Observe", expected);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String expectedValue = "values MUST NOT be null";
@ -116,7 +116,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testObserveRSourceNotRead_Result_METHOD_NOT_ALLOWED() throws Exception {
String expectedId = objectInstanceIdVer_5 + "/" + resourceId_0;
String expectedId = objectInstanceIdVer_5 + "/" + RESOURCE_ID_0;
sendObserve("Observe", expectedId);
String actualResult = sendObserve("Observe", expectedId);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
@ -130,7 +130,7 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT
*/
@Test
public void testObserveRepeatedRequestObserveOnDevice_Result_BAD_REQUEST_ErrorMsg_AlreadyRegistered() throws Exception {
String expectedId = objectInstanceIdVer_3 + "/" + resourceId_0;
String expectedId = objectInstanceIdVer_3 + "/" + RESOURCE_ID_0;
sendObserve("Observe", expectedId);
String actualResult = sendObserve("Observe", expectedId);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
@ -146,8 +146,8 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT
@Test
public void testObserveReadAll_Result_CONTENT_Value_Contains_Paths_Count_ObserveAll() throws Exception {
sendObserve("ObserveCancelAll", null);
String expectedId_0 = objectInstanceIdVer_3 + "/" + resourceId_0;
String expectedId_9 = objectInstanceIdVer_3 + "/" + resourceId_9;
String expectedId_0 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_0;
String expectedId_9 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_9;
sendObserve("Observe", expectedId_0);
sendObserve("Observe", expectedId_9);
String actualResult = sendObserve("ObserveReadAll", null);
@ -167,8 +167,8 @@ public class RpcLwm2mIntegrationObserveTest extends AbstractRpcLwM2MIntegrationT
@Test
public void testObserveCancelOneResource_Result_CONTENT_Value_Count_1() throws Exception {
sendObserve("ObserveCancelAll", null);
String expectedId_0 = objectInstanceIdVer_3 + "/" + resourceId_0;
String expectedId_3 = objectInstanceIdVer_5 + "/" + resourceId_3;
String expectedId_0 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_0;
String expectedId_3 = objectInstanceIdVer_5 + "/" + RESOURCE_ID_3;
sendObserve("Observe", expectedId_0);
sendObserve("Observe", expectedId_3);
String actualResult = sendObserve("ObserveCancel", expectedId_0);

68
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationReadTest.java

@ -27,18 +27,18 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_0_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_19_1_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_11;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_0_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_19_1_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_11;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_2;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9;
public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest {
@ -96,11 +96,11 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest
*/
@Test
public void testReadMultipleResourceById_Result_CONTENT_Value_IsLwM2mMultipleResource() throws Exception {
String expectedIdVer = objectInstanceIdVer_3 +"/" + resourceId_11 ;
String expectedIdVer = objectInstanceIdVer_3 +"/" + RESOURCE_ID_11;
String actualResult = sendRPCById(expectedIdVer);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText());
String expected = "LwM2mMultipleResource [id=" + resourceId_11 + ", values={";
String expected = "LwM2mMultipleResource [id=" + RESOURCE_ID_11 + ", values={";
assertTrue(rpcActualResult.get("value").asText().contains(expected));
}
@ -109,11 +109,11 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest
*/
@Test
public void testReadSingleResourceById_Result_CONTENT_Value_IsLwM2mSingleResource() throws Exception {
String expectedIdVer = objectInstanceIdVer_3 +"/" + resourceId_14 ;
String expectedIdVer = objectInstanceIdVer_3 +"/" + RESOURCE_ID_14;
String actualResult = sendRPCById(expectedIdVer);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText());
String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=";
String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=";
assertTrue(rpcActualResult.get("value").asText().contains(expected));
}
@ -122,11 +122,11 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest
*/
@Test
public void testReadSingleResourceByKey_Result_CONTENT_Value_IsLwM2mSingleResource() throws Exception {
String expectedKey = resourceIdName_3_14 ;
String expectedKey = RESOURCE_ID_NAME_3_14;
String actualResult = sendRPCByKey(expectedKey);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText());
String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=";
String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=";
assertTrue(rpcActualResult.get("value").asText().contains(expected));
}
@ -137,16 +137,16 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest
public void testReadCompositeSingleResourceByIds_Result_CONTENT_Value_IsObjectIsLwM2mSingleResourceIsLwM2mMultipleResource() throws Exception {
String expectedIdVer_1 = (String) expectedObjectIdVers.stream().filter(path -> (!((String)path).contains("/" + BINARY_APP_DATA_CONTAINER) && ((String)path).contains("/" + SERVER))).findFirst().get();
String objectId_1 = pathIdVerToObjectId(expectedIdVer_1);
String expectedIdVer3_0_1 = objectInstanceIdVer_3 + "/" + resourceId_1;
String expectedIdVer3_0_11 = objectInstanceIdVer_3 + "/" + resourceId_11;
String expectedIdVer3_0_1 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_1;
String expectedIdVer3_0_11 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_11;
String objectInstanceId_3 = pathIdVerToObjectId(objectInstanceIdVer_3);
String expectedIds = "[\"" + expectedIdVer_1 + "\", \"" + expectedIdVer3_0_1 + "\", \"" + expectedIdVer3_0_11 + "\"]";
String actualResult = sendCompositeRPCByIds(expectedIds);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText());
String expected1 = objectId_1 + "=LwM2mObject [id=" + new LwM2mPath(objectId_1).getObjectId() + ", instances={";
String expected3_0_1 = objectInstanceId_3 + "/" + resourceId_1 + "=LwM2mSingleResource [id=" + resourceId_1 + ", value=";
String expected3_0_11 = objectInstanceId_3 + "/" + resourceId_11 + "=LwM2mMultipleResource [id=" + resourceId_11 + ", values={";
String expected3_0_1 = objectInstanceId_3 + "/" + RESOURCE_ID_1 + "=LwM2mSingleResource [id=" + RESOURCE_ID_1 + ", value=";
String expected3_0_11 = objectInstanceId_3 + "/" + RESOURCE_ID_11 + "=LwM2mMultipleResource [id=" + RESOURCE_ID_11 + ", values={";
String actualValues = rpcActualResult.get("value").asText();
assertTrue(actualValues.contains(expected1));
assertTrue(actualValues.contains(expected3_0_1));
@ -159,8 +159,8 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest
@Test
public void testReadCompositeSingleResourceByIds_Result_CONTENT_Value_IsObjectInstanceIsLwM2mSingleResource() throws Exception {
String expectedIdVer3_0 = objectInstanceIdVer_3;
String expectedIdVer1_0_1 = objectInstanceIdVer_1 + "/" + resourceId_1;
String expectedIdVer1_0_2 = objectInstanceIdVer_1 + "/" + resourceId_2;
String expectedIdVer1_0_1 = objectInstanceIdVer_1 + "/" + RESOURCE_ID_1;
String expectedIdVer1_0_2 = objectInstanceIdVer_1 + "/" + RESOURCE_ID_2;
String expectedIds = "[\"" + expectedIdVer1_0_1 + "\", \"" + expectedIdVer1_0_2 + "\", \"" + expectedIdVer3_0 + "\"]";
String actualResult = sendCompositeRPCByIds(expectedIds);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
@ -169,8 +169,8 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest
LwM2mPath path = new LwM2mPath(objectInstanceId_3);
String expected3_0 = objectInstanceId_3 + "=LwM2mObjectInstance [id=" + path.getObjectInstanceId() + ", resources={";
String objectInstanceId_1 = pathIdVerToObjectId(objectInstanceIdVer_1);
String expected1_0_1 = objectInstanceId_1 + "/" + resourceId_1 + "=LwM2mSingleResource [id=" + resourceId_1 + ", value=";
String expected1_0_2 = objectInstanceId_1 + "/" + resourceId_2 + "=null";
String expected1_0_1 = objectInstanceId_1 + "/" + RESOURCE_ID_1 + "=LwM2mSingleResource [id=" + RESOURCE_ID_1 + ", value=";
String expected1_0_2 = objectInstanceId_1 + "/" + RESOURCE_ID_2 + "=null";
String actualValues = rpcActualResult.get("value").asText();
assertTrue(actualValues.contains(expected3_0));
assertTrue(actualValues.contains(expected1_0_1));
@ -182,20 +182,20 @@ public class RpcLwm2mIntegrationReadTest extends AbstractRpcLwM2MIntegrationTest
*/
@Test
public void testReadCompositeSingleResourceByKeys_Result_CONTENT_Value_3_0_IsLwM2mSingleResource_19_0_0_AND_19_0_1_Null() throws Exception {
String expectedKey3_0_9 = resourceIdName_3_9;
String expectedKey3_0_14 = resourceIdName_3_14;
String expectedKey19_0_0 = resourceIdName_19_0_0;
String expectedKey19_1_0 = resourceIdName_19_1_0;
String expectedKey3_0_9 = RESOURCE_ID_NAME_3_9;
String expectedKey3_0_14 = RESOURCE_ID_NAME_3_14;
String expectedKey19_0_0 = RESOURCE_ID_NAME_19_0_0;
String expectedKey19_1_0 = RESOURCE_ID_NAME_19_1_0;
String expectedKeys = "[\"" + expectedKey3_0_9 + "\", \"" + expectedKey3_0_14 + "\", \"" + expectedKey19_0_0 + "\", \"" + expectedKey19_1_0 + "\"]";
String actualResult = sendCompositeRPCByKeys(expectedKeys);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText());
String objectInstanceId_3 = pathIdVerToObjectId(objectInstanceIdVer_3);
String objectId_19 = pathIdVerToObjectId(objectIdVer_19);
String expected3_0_9 = objectInstanceId_3 + "/" + resourceId_9 + "=LwM2mSingleResource [id=" + resourceId_9 + ", value=";
String expected3_0_14 = objectInstanceId_3 + "/" + resourceId_14 + "=LwM2mSingleResource [id=" + resourceId_14 + ", value=";
String expected19_0_0 = objectId_19 + "/" + objectInstanceId_0 + "/" + resourceId_0 + "=null";
String expected19_1_0 = objectId_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "=null";
String expected3_0_9 = objectInstanceId_3 + "/" + RESOURCE_ID_9 + "=LwM2mSingleResource [id=" + RESOURCE_ID_9 + ", value=";
String expected3_0_14 = objectInstanceId_3 + "/" + RESOURCE_ID_14 + "=LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=";
String expected19_0_0 = objectId_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0 + "=null";
String expected19_1_0 = objectId_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "=null";
String actualValues = rpcActualResult.get("value").asText();
assertTrue(actualValues.contains(expected3_0_9));
assertTrue(actualValues.contains(expected3_0_14));

11
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteAttributesTest.java

@ -17,7 +17,6 @@ package org.thingsboard.server.transport.lwm2m.rpc.sql;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.eclipse.leshan.core.ResponseCode;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTest;
@ -25,13 +24,7 @@ import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTes
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_2;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_3;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_4;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_8;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14;
public class RpcLwm2mIntegrationWriteAttributesTest extends AbstractRpcLwM2MIntegrationTest {
@ -45,7 +38,7 @@ public class RpcLwm2mIntegrationWriteAttributesTest extends AbstractRpcLwM2MInte
*/
@Test
public void testWriteAttributesResourceWithParametersById_Result_INTERNAL_SERVER_ERROR() throws Exception {
String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_14;
String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_14;
String expectedValue = "{\"pmax\":100, \"pmin\":10}";
String actualResult = sendRPCExecuteWithValueById(expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);

77
application/src/test/java/org/thingsboard/server/transport/lwm2m/rpc/sql/RpcLwm2mIntegrationWriteTest.java

@ -25,16 +25,15 @@ import org.thingsboard.server.transport.lwm2m.rpc.AbstractRpcLwM2MIntegrationTes
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.objectInstanceId_2;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceIdName_3_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_15;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceId_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.resourceInstanceId_2;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_1;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INSTANCE_ID_2;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_NAME_3_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_0;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_14;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_15;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_9;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_INSTANCE_ID_2;
public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTest {
@ -46,7 +45,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
*/
@Test
public void testWriteReplaceValueSingleResourceById_Result_CHANGED() throws Exception {
String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_14;
String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_14;
String expectedValue = "+12";
String actualResult = sendRPCWriteStringById("WriteReplace", expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
@ -54,7 +53,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
actualResult = sendRPCReadById(expectedPath);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue + ", type=STRING]";
String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue + ", type=STRING]";
assertTrue(actualValues.contains(expected));
}
@ -65,7 +64,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
*/
@Test
public void testWriteReplaceValueSingleResourceByKey_Result_CHANGED() throws Exception {
String expectedKey = resourceIdName_3_14;
String expectedKey = RESOURCE_ID_NAME_3_14;
String expectedValue = "+09";
String actualResult = sendRPCWriteByKey("WriteReplace", expectedKey, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
@ -73,7 +72,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
actualResult = sendRPCReadByKey(expectedKey);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue + ", type=STRING]";
String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue + ", type=STRING]";
assertTrue(actualValues.contains(expected));
}
@ -85,7 +84,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
*/
@Test
public void testWriteReplaceValueMultipleResource_Result_CHANGED_Value_Multi_Instance_Resource_must_in_Json_format() throws Exception {
String expectedPath = objectIdVer_19 + "/" + objectInstanceId_0 + "/" + resourceId_0;
String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0 + "/" + RESOURCE_ID_0;
int resourceInstanceId0 = 0;
int resourceInstanceId15 = 15;
String expectedValue0 = "0000ad45675600";
@ -115,7 +114,7 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
*/
@Test
public void testWriteReplaceValueSingleResourceR_ById_Result_CHANGED() throws Exception {
String expectedPath = objectInstanceIdVer_3 + "/" + resourceId_9;
String expectedPath = objectInstanceIdVer_3 + "/" + RESOURCE_ID_9;
Integer expectedValue = 90;
String actualResult = sendRPCWriteObjectById("WriteReplace", expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
@ -132,21 +131,21 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
String expectedPath = objectInstanceIdVer_3;
String expectedValue14 = "+5";
String expectedValue15 = "Kiyv/Europe";
String expectedValue = "{\"" + resourceId_14 + "\":\"" + expectedValue14 + "\",\"" + resourceId_15 + "\":\"" + expectedValue15 + "\"}";
String expectedValue = "{\"" + RESOURCE_ID_14 + "\":\"" + expectedValue14 + "\",\"" + RESOURCE_ID_15 + "\":\"" + expectedValue15 + "\"}";
String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
String expectedPath14 = objectInstanceIdVer_3 + "/" + resourceId_14;
String expectedPath15 = objectInstanceIdVer_3 + "/" + resourceId_15;
String expectedPath14 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_14;
String expectedPath15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15;
actualResult = sendRPCReadById(expectedPath14);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue14 + ", type=STRING]";
String expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue14 + ", type=STRING]";
assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadById(expectedPath15);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mSingleResource [id=" + resourceId_15 + ", value=" + expectedValue15 + ", type=STRING]";
expected = "LwM2mSingleResource [id=" + RESOURCE_ID_15 + ", value=" + expectedValue15 + ", type=STRING]";
assertTrue(actualValues.contains(expected));
}
@ -157,17 +156,17 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
*/
@Test
public void testWriteUpdateValueMultipleResourceById_Result_CHANGED() throws Exception {
String expectedPath = objectIdVer_19 + "/" + objectInstanceId_0;
String expectedPath = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_0;
int resourceInstanceId0 = 0;
int resourceInstanceId25 = 25;
String expectedValue0 = "00ad45675600";
String expectedValue25 = "25ad45675600cdef";
String expectedValue = "{\"" + resourceId_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}";
String expectedValue = "{\"" + RESOURCE_ID_0 + "\":{\"" + resourceInstanceId0 + "\":\"" + expectedValue0 + "\", \"" + resourceInstanceId25 + "\":\"" + expectedValue25 + "\"}}";
String actualResult = sendRPCWriteObjectById("WriteUpdate", expectedPath, expectedValue);
ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
assertEquals(ResponseCode.CHANGED.getName(), rpcActualResult.get("result").asText());
String expectedPath0 = expectedPath + "/" + resourceId_0 + "/" + resourceInstanceId0;
String expectedPath25 =expectedPath + "/" + resourceId_0 + "/" + resourceInstanceId25;
String expectedPath0 = expectedPath + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId0;
String expectedPath25 =expectedPath + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId25;
actualResult = sendRPCReadById(expectedPath0);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
@ -188,11 +187,11 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
@Test
public void testWriteCompositeValueSingleResourceResourceInstanceByIdKey_Result_CHANGED() throws Exception {
int resourceInstanceId2 = 2;
String expectedPath19_1_0_2 = objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "/" + resourceInstanceId2;
String expectedPath19_1_0_2 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "/" + resourceInstanceId2;
String expectedValue19_1_0_2 = "00001234";
String expectedKey3_0_14 = resourceIdName_3_14;
String expectedKey3_0_14 = RESOURCE_ID_NAME_3_14;
String expectedValue3_0_14 = "+04";
String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + resourceId_15;
String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15;
String expectedValue3_0_15 = "Kiyv/Europe";
String nodes = "{\"" + expectedPath19_1_0_2 + "\":\"" + expectedValue19_1_0_2 + "\", \"" + expectedKey3_0_14 +
"\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}";
@ -207,12 +206,12 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
actualResult = sendRPCReadByKey(expectedKey3_0_14);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]";
expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]";
assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadById(expectedPath3_0_15);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mSingleResource [id=" + resourceId_15 + ", value=" + expectedValue3_0_15 + ", type=STRING]";
expected = "LwM2mSingleResource [id=" + RESOURCE_ID_15 + ", value=" + expectedValue3_0_15 + ", type=STRING]";
assertTrue(actualValues.contains(expected));
}
@ -246,11 +245,11 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
*/
@Test
public void testWriteCompositeCreateResourceInstanceUpdateSingleResourceByIdKey_Result_CHANGED() throws Exception {
String expectedPath19_1_0_2 = objectIdVer_19 + "/" + objectInstanceId_1 + "/" + resourceId_0 + "/" + resourceInstanceId_2;
String expectedPath19_1_0_2 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_1 + "/" + RESOURCE_ID_0 + "/" + RESOURCE_INSTANCE_ID_2;
String expectedValue19_1_0_2 = "00001234";
String expectedKey3_0_14 = resourceIdName_3_14;
String expectedKey3_0_14 = RESOURCE_ID_NAME_3_14;
String expectedValue3_0_14 = "+04";
String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + resourceId_15;
String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15;
String expectedValue3_0_15 = "Kiyv/Europe";
String nodes = "{\"" + expectedPath19_1_0_2 + "\":\"" + expectedValue19_1_0_2 + "\", \"" + expectedKey3_0_14 +
"\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}";
@ -260,17 +259,17 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
actualResult = sendRPCReadById(expectedPath19_1_0_2);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
String actualValues = rpcActualResult.get("value").asText();
String expected = "LwM2mResourceInstance [id=" + resourceInstanceId_2 + ", value=" + expectedValue19_1_0_2.length()/2 + "Bytes, type=OPAQUE]";
String expected = "LwM2mResourceInstance [id=" + RESOURCE_INSTANCE_ID_2 + ", value=" + expectedValue19_1_0_2.length()/2 + "Bytes, type=OPAQUE]";
assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadByKey(expectedKey3_0_14);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mSingleResource [id=" + resourceId_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]";
expected = "LwM2mSingleResource [id=" + RESOURCE_ID_14 + ", value=" + expectedValue3_0_14 + ", type=STRING]";
assertTrue(actualValues.contains(expected));
actualResult = sendRPCReadById(expectedPath3_0_15);
rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class);
actualValues = rpcActualResult.get("value").asText();
expected = "LwM2mSingleResource [id=" + resourceId_15 + ", value=" + expectedValue3_0_15 + ", type=STRING]";
expected = "LwM2mSingleResource [id=" + RESOURCE_ID_15 + ", value=" + expectedValue3_0_15 + ", type=STRING]";
assertTrue(actualValues.contains(expected));
}
@ -285,11 +284,11 @@ public class RpcLwm2mIntegrationWriteTest extends AbstractRpcLwM2MIntegrationTes
*/
@Test
public void testWriteCompositeCreateObjectInstanceUpdateSingleResourceByIdKey_Result_BAD_REQUEST() throws Exception {
String expectedPath19_1_2_2 = objectIdVer_19 + "/" + objectInstanceId_2 + "/" + resourceId_0 + "/" + resourceInstanceId_2;
String expectedPath19_1_2_2 = objectIdVer_19 + "/" + OBJECT_INSTANCE_ID_2 + "/" + RESOURCE_ID_0 + "/" + RESOURCE_INSTANCE_ID_2;
String expectedValue19_1_0_2 = "00001234";
String expectedKey3_0_14 = resourceIdName_3_14;
String expectedKey3_0_14 = RESOURCE_ID_NAME_3_14;
String expectedValue3_0_14 = "+04";
String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + resourceId_15;
String expectedPath3_0_15 = objectInstanceIdVer_3 + "/" + RESOURCE_ID_15;
String expectedValue3_0_15 = "Kiyv/Europe";
String nodes = "{\"" + expectedPath19_1_2_2 + "\":\"" + expectedValue19_1_0_2 + "\", \"" + expectedKey3_0_14 +
"\":\"" + expectedValue3_0_14 + "\", \"" + expectedPath3_0_15 + "\":\"" + expectedValue3_0_15 + "\"}";

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

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.transport.lwm2m.security;
import org.eclipse.leshan.core.util.Hex;
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredentials;
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecBootstrapClientCredential;
import org.thingsboard.server.dao.service.DaoSqlTest;
@ -24,144 +23,86 @@ import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.KeySpec;
@DaoSqlTest
public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
protected final String pskIdentity; // client public key or id used for PSK
protected final String pskKey; // client private/secret key used for PSK
protected final PublicKey clientPublicKey; // client public key used for RPK
protected final PrivateKey clientPrivateKey; // client private key used for RPK
protected final PublicKey serverPublicKey; // server public key used for RPK
protected final PrivateKey serverPrivateKey; // server private key used for RPK
// client private key used for X509
protected final PrivateKey clientPrivateKeyFromCert;
// server private key used for X509
protected final PrivateKey serverPrivateKeyFromCert;
// client certificate signed by rootCA with a good CN (CN start by leshan_integration_test)
protected final X509Certificate clientX509Cert;
// client certificate signed by rootCA but with bad CN (CN does not start by leshan_integration_test)
protected final X509Certificate clientX509CertWithBadCN;
// client certificate self-signed with a good CN (CN start by leshan_integration_test)
protected final X509Certificate clientX509CertSelfSigned;
// client certificate signed by another CA (not rootCA) with a good CN (CN start by leshan_integration_test)
protected final X509Certificate clientX509CertNotTrusted;
// server certificate signed by rootCA
protected final X509Certificate serverX509Cert;
// self-signed server certificate
protected final X509Certificate serverX509CertSelfSigned;
// rootCA used by the server
protected final X509Certificate rootCAX509Cert;
// certificates trustedby the server (should contain rootCA)
protected final Certificate[] trustedCertificates = new Certificate[1];
protected static final String ENDPOINT = "deviceAEndpoint";
protected final String CREDENTIALS_PATH = "lwm2m/credentials/"; // client public key or id used for PSK
// Get keys PSK
protected final String CLIENT_PSK_IDENTITY = "SOME_PSK_ID"; // client public key or id used for PSK
protected final String CLIENT_PSK_KEY = "73656372657450534b73656372657450"; // client private/secret key used for PSK
// Server
protected static final String SERVER_JKS_FOR_TEST = "lwm2mserver";
protected static final String SERVER_STORE_PWD = "server_ks_password";
protected static final String SERVER_CERT_ALIAS = "server";
protected final X509Certificate serverX509Cert; // server certificate signed by rootCA
protected final PublicKey serverPublicKeyFromCert; // server public key used for RPK
// Client
protected LwM2MTestClient client;
protected static final String CLIENT_ENDPOINT_NO_SEC = "LwNoSec00000000";
protected static final String CLIENT_ENDPOINT_PSK = "LwPsk00000000";
protected static final String CLIENT_ENDPOINT_RPK = "LwRpk00000000";
protected static final String CLIENT_ENDPOINT_X509_TRUST = "LwX50900000000";
protected static final String CLIENT_ENDPOINT_X509_TRUST_NO = "LwX509TrustNo";
protected static final String CLIENT_JKS_FOR_TEST = "lwm2mclient";
protected static final String CLIENT_STORE_PWD = "client_ks_password";
protected static final String CLIENT_ALIAS_CERT_TRUST = "client_alias_00000000";
protected static final String CLIENT_ALIAS_CERT_TRUST_NO = "client_alias_trust_no";
protected final X509Certificate clientX509CertTrust; // client certificate signed by intermediate, rootCA with a good CN ("host name")
protected final PrivateKey clientPrivateKeyFromCertTrust; // client private key used for X509 and RPK
protected final PublicKey clientPublicKeyFromCertTrust; // client public key used for RPK
protected final X509Certificate clientX509CertTrustNo; // client certificate signed by intermediate, rootCA with a good CN ("host name")
protected final PrivateKey clientPrivateKeyFromCertTrustNo; // client private key used for X509 and RPK
protected final PublicKey clientPublicKeyFromCertTrustNo; // client public key used for RPK
private final String[] RESOURCES_SECURITY = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"};
private final LwM2MBootstrapClientCredentials defaultBootstrapCredentials;
private final String[] resources = new String[]{"1.xml", "2.xml", "3.xml", "5.xml", "9.xml"};
public AbstractSecurityLwM2MIntegrationTest() {
// create client credentials
setResources(this.resources);
setEndpoint(ENDPOINT);
setResources(this.RESOURCES_SECURITY);
try {
// Get keys PSK
this.pskIdentity = "SOME_PSK_ID";
this.pskKey = "73656372657450534b73656372657450";
// Get point values
byte[] publicX = Hex
.decodeHex("89c048261979208666f2bfb188be1968fc9021c416ce12828c06f4e314c167b5".toCharArray());
byte[] publicY = Hex
.decodeHex("cbf1eb7587f08e01688d9ada4be859137ca49f79394bad9179326b3090967b68".toCharArray());
byte[] privateS = Hex
.decodeHex("e67b68d2aaeb6550f19d98cade3ad62b39532e02e6b422e1f7ea189dabaea5d2".toCharArray());
// Get Elliptic Curve Parameter spec for secp256r1
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
// Create key specs
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
// Get keys RPK
clientPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
clientPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
// Get certificates from key store
char[] clientKeyStorePwd = "client".toCharArray();
char[] clientKeyStorePwd = CLIENT_STORE_PWD.toCharArray();
KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
try (InputStream clientKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/clientKeyStore.jks")) {
try (InputStream clientKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream(CREDENTIALS_PATH + CLIENT_JKS_FOR_TEST + ".jks")) {
clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd);
}
// Trust
clientPrivateKeyFromCertTrust = (PrivateKey) clientKeyStore.getKey(CLIENT_ALIAS_CERT_TRUST, clientKeyStorePwd);
clientX509CertTrust = (X509Certificate) clientKeyStore.getCertificate(CLIENT_ALIAS_CERT_TRUST);
clientPublicKeyFromCertTrust = clientX509CertTrust != null ? clientX509CertTrust.getPublicKey() : null;
// No trust
clientPrivateKeyFromCertTrustNo = (PrivateKey) clientKeyStore.getKey(CLIENT_ALIAS_CERT_TRUST_NO, clientKeyStorePwd);
clientX509CertTrustNo = (X509Certificate) clientKeyStore.getCertificate(CLIENT_ALIAS_CERT_TRUST_NO);
clientPublicKeyFromCertTrustNo = clientX509CertTrustNo != null ? clientX509CertTrustNo.getPublicKey() : null;
clientPrivateKeyFromCert = (PrivateKey) clientKeyStore.getKey("client", clientKeyStorePwd);
clientX509Cert = (X509Certificate) clientKeyStore.getCertificate("client");
clientX509CertWithBadCN = (X509Certificate) clientKeyStore.getCertificate("client_bad_cn");
clientX509CertSelfSigned = (X509Certificate) clientKeyStore.getCertificate("client_self_signed");
clientX509CertNotTrusted = (X509Certificate) clientKeyStore.getCertificate("client_not_trusted");
} catch (GeneralSecurityException | IOException e) {
throw new RuntimeException(e);
}
// create server credentials
try {
// Get point values
byte[] publicX = Hex
.decodeHex("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73".toCharArray());
byte[] publicY = Hex
.decodeHex("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a".toCharArray());
byte[] privateS = Hex
.decodeHex("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400".toCharArray());
// Get Elliptic Curve Parameter spec for secp256r1
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
// Create key specs
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
// Get keys
serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
// Get certificates from key store
char[] serverKeyStorePwd = "server".toCharArray();
char[] serverKeyStorePwd = SERVER_STORE_PWD.toCharArray();
KeyStore serverKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
try (InputStream serverKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/serverKeyStore.jks")) {
try (InputStream serverKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream(CREDENTIALS_PATH + SERVER_JKS_FOR_TEST + ".jks")) {
serverKeyStore.load(serverKeyStoreFile, serverKeyStorePwd);
}
serverPrivateKeyFromCert = (PrivateKey) serverKeyStore.getKey("server", serverKeyStorePwd);
rootCAX509Cert = (X509Certificate) serverKeyStore.getCertificate("rootCA");
serverX509Cert = (X509Certificate) serverKeyStore.getCertificate("server");
serverX509CertSelfSigned = (X509Certificate) serverKeyStore.getCertificate("server_self_signed");
trustedCertificates[0] = serverX509Cert;
serverX509Cert = (X509Certificate) serverKeyStore.getCertificate(SERVER_CERT_ALIAS);
serverPublicKeyFromCert = serverX509Cert.getPublicKey();
} catch (GeneralSecurityException | IOException e) {
throw new RuntimeException(e);
}

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

@ -28,8 +28,8 @@ public class NoSecLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationT
@Test
public void testConnectAndObserveTelemetry() throws Exception {
NoSecClientCredential clientCredentials = createNoSecClientCredentials(ENDPOINT);
super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, ENDPOINT);
NoSecClientCredential clientCredentials = createNoSecClientCredentials(CLIENT_ENDPOINT_NO_SEC);
super.basicTestConnectionObserveTelemetry(SECURITY, clientCredentials, COAP_CONFIG, CLIENT_ENDPOINT_NO_SEC);
}
}

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

@ -33,13 +33,13 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
@Test
public void testConnectWithPSKAndObserveTelemetry() throws Exception {
PSKClientCredential clientCredentials = new PSKClientCredential();
clientCredentials.setEndpoint(ENDPOINT);
clientCredentials.setKey(pskKey);
clientCredentials.setIdentity(pskIdentity);
clientCredentials.setEndpoint(CLIENT_ENDPOINT_PSK);
clientCredentials.setKey(CLIENT_PSK_KEY);
clientCredentials.setIdentity(CLIENT_PSK_IDENTITY);
Security security = psk(SECURE_URI,
SHORT_SERVER_ID,
pskIdentity.getBytes(StandardCharsets.UTF_8),
Hex.decodeHex(pskKey.toCharArray()));
super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, ENDPOINT);
CLIENT_PSK_IDENTITY.getBytes(StandardCharsets.UTF_8),
Hex.decodeHex(CLIENT_PSK_KEY.toCharArray()));
super.basicTestConnectionObserveTelemetry(security, clientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_PSK);
}
}

12
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/RpkLwM2MIntegrationTest.java

@ -32,13 +32,13 @@ public class RpkLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
@Test
public void testConnectWithRPKAndObserveTelemetry() throws Exception {
RPKClientCredential rpkClientCredentials = new RPKClientCredential();
rpkClientCredentials.setEndpoint(ENDPOINT);
rpkClientCredentials.setKey(new String(Base64.encodeBase64(clientPublicKey.getEncoded())));
rpkClientCredentials.setEndpoint(CLIENT_ENDPOINT_RPK);
rpkClientCredentials.setKey(new String(Base64.encodeBase64(clientPublicKeyFromCertTrust.getEncoded())));
Security security = rpk(SECURE_URI,
SHORT_SERVER_ID,
clientPublicKey.getEncoded(),
clientPrivateKey.getEncoded(),
serverX509Cert.getPublicKey().getEncoded());
super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, ENDPOINT);
clientPublicKeyFromCertTrust.getEncoded(),
clientPrivateKeyFromCertTrust.getEncoded(),
serverPublicKeyFromCert.getEncoded());
super.basicTestConnectionObserveTelemetry(security, rpkClientCredentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_RPK);
}
}

11
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_NoTrustLwM2MIntegrationTest.java

@ -31,14 +31,13 @@ public class X509_NoTrustLwM2MIntegrationTest extends AbstractSecurityLwM2MInteg
@Test
public void testConnectWithCertAndObserveTelemetry() throws Exception {
X509ClientCredential credentials = new X509ClientCredential();
credentials.setEndpoint(ENDPOINT);
credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted));
credentials.setEndpoint(CLIENT_ENDPOINT_X509_TRUST_NO);
credentials.setCert(SslUtil.getCertificateString(clientX509CertTrustNo));
Security security = x509(SECURE_URI,
SHORT_SERVER_ID,
clientX509CertNotTrusted.getEncoded(),
clientPrivateKeyFromCert.getEncoded(),
clientX509CertTrustNo.getEncoded(),
clientPrivateKeyFromCertTrustNo.getEncoded(),
serverX509Cert.getEncoded());
super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT);
super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_X509_TRUST_NO);
}
}

8
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/X509_TrustLwM2MIntegrationTest.java

@ -30,13 +30,13 @@ public class X509_TrustLwM2MIntegrationTest extends AbstractSecurityLwM2MIntegra
@Test
public void testConnectAndObserveTelemetry() throws Exception {
X509ClientCredential credentials = new X509ClientCredential();
credentials.setEndpoint(ENDPOINT);
credentials.setEndpoint(CLIENT_ENDPOINT_X509_TRUST);
Security security = x509(SECURE_URI,
SHORT_SERVER_ID,
clientX509Cert.getEncoded(),
clientPrivateKeyFromCert.getEncoded(),
clientX509CertTrust.getEncoded(),
clientPrivateKeyFromCertTrust.getEncoded(),
serverX509Cert.getEncoded());
super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, ENDPOINT);
super.basicTestConnectionObserveTelemetry(security, credentials, SECURE_COAP_CONFIG, CLIENT_ENDPOINT_X509_TRUST);
}
}

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

@ -1,20 +1,20 @@
transport.lwm2m.server.security.credentials.enabled=true
transport.lwm2m.server.security.credentials.type=KEYSTORE
transport.lwm2m.server.security.credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks
transport.lwm2m.server.security.credentials.keystore.store_password=server
transport.lwm2m.server.security.credentials.keystore.key_alias=server
transport.lwm2m.server.security.credentials.keystore.key_password=server
transport.lwm2m.server.security.credentials.keystore.store_file=lwm2m/credentials/lwm2mserver.jks
#transport.lwm2m.server.security.credentials.keystore.store_password=server
#transport.lwm2m.server.security.credentials.keystore.key_alias=server
#transport.lwm2m.server.security.credentials.keystore.key_password=server
transport.lwm2m.bootstrap.enabled=false
transport.lwm2m.bootstrap.security.credentials.enabled=true
transport.lwm2m.bootstrap.security.credentials.type=KEYSTORE
transport.lwm2m.bootstrap.security.credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks
transport.lwm2m.bootstrap.security.credentials.keystore.store_password=server
transport.lwm2m.bootstrap.security.credentials.keystore.key_alias=server
transport.lwm2m.bootstrap.security.credentials.keystore.key_password=server
transport.lwm2m.bootstrap.security.credentials.keystore.store_file=lwm2m/credentials/lwm2mserver.jks
#transport.lwm2m.bootstrap.security.credentials.keystore.store_password=server
#transport.lwm2m.bootstrap.security.credentials.keystore.key_alias=server
#transport.lwm2m.bootstrap.security.credentials.keystore.key_password=server
transport.lwm2m.security.trust-credentials.enabled=true
transport.lwm2m.security.trust-credentials.type=KEYSTORE
transport.lwm2m.security.trust-credentials.keystore.store_file=lwm2m/credentials/serverKeyStore.jks
transport.lwm2m.security.trust-credentials.keystore.store_password=server
transport.lwm2m.security.trust-credentials.keystore.store_file=lwm2m/credentials/lwm2mtruststorechain.jks
#transport.lwm2m.security.trust-credentials.keystore.store_password=server
edges.enabled=true
edges.storage.no_read_records_sleep=500

BIN
application/src/test/resources/lwm2m/credentials/clientKeyStore.jks

Binary file not shown.

BIN
application/src/test/resources/lwm2m/credentials/lwm2mclient.jks

Binary file not shown.

BIN
application/src/test/resources/lwm2m/credentials/lwm2mserver.jks

Binary file not shown.

BIN
application/src/test/resources/lwm2m/credentials/lwm2mtruststorechain.jks

Binary file not shown.

BIN
application/src/test/resources/lwm2m/credentials/serverKeyStore.jks

Binary file not shown.

2
common/dao-api/src/main/java/org/thingsboard/server/dao/rpc/RpcService.java

@ -35,5 +35,7 @@ public interface RpcService {
ListenableFuture<Rpc> findRpcByIdAsync(TenantId tenantId, RpcId id);
PageData<Rpc> findAllByDeviceId(TenantId tenantId, DeviceId deviceId, PageLink pageLink);
PageData<Rpc> findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink);
}

14
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/lwm2m/bootstrap/LwM2MServerSecurityConfig.java

@ -42,17 +42,23 @@ public class LwM2MServerSecurityConfig {
@ApiModelProperty(position = 8, value = "Server Public Key for 'Security' mode (DTLS): RPK or X509. Format: base64 encoded", example = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAZ0pSaGKHk/GrDaUDnQZpeEdGwX7m3Ws+U/kiVat\n" +
"+44sgk3c8g0LotfMpLlZJPhPwJ6ipXV+O1r7IZUjBs3LNA==", readOnly = true)
protected String serverPublicKey;
@ApiModelProperty(position = 9, value = "Bootstrap Server Account Timeout (If the value is set to 0, or if this resource is not instantiated, the Bootstrap-Server Account lifetime is infinite.)", example = "0", readOnly = true)
@ApiModelProperty(position = 9, value = "Server Public Key for 'Security' mode (DTLS): X509. Format: base64 encoded", example = "MMIICODCCAd6gAwIBAgIUI88U1zowOdrxDK/dOV+36gJxI2MwCgYIKoZIzj0EAwIwejELMAkGA1UEBhMCVUs\n" +
"xEjAQBgNVBAgTCUt5aXYgY2l0eTENMAsGA1UEBxMES3lpdjEUMBIGA1UEChMLVGhpbmdzYm9hcmQxFzAVBgNVBAsMDkRFVkVMT1BFUl9URVNUMRkwFwYDVQQDDBBpbnRlcm1lZGlhdGVfY2EwMB4XDTIyMDEwOTEzMDMwMFoXDTI3MDEwODEzMDMwMFowFDESMBAGA1UEAxM\n" +
"JbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUO3vBo/JTv0eooY7XHiKAIVDoWKFqtrU7C6q8AIKqpLcqhCdW+haFeBOH3PjY6EwaWkY04Bir4oanU0s7tz2uKOBpzCBpDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/\n" +
"BAIwADAdBgNVHQ4EFgQUEjc3Q4a0TxzP/3x3EV4fHxYUg0YwHwYDVR0jBBgwFoAUuSquGycMU6Q0SYNcbtSkSD3TfH0wLwYDVR0RBCgwJoIVbG9jYWxob3N0LmxvY2FsZG9tYWlugglsb2NhbGhvc3SCAiAtMAoGCCqGSM49BAMCA0gAMEUCIQD7dbZObyUaoDiNbX+9fUNp\n" +
"AWrD7N7XuJUwZ9FcN75R3gIgb2RNjDkHoyUyF1YajwkBk+7XmIXNClmizNJigj908mw=", readOnly = true)
protected String serverCertificate;
@ApiModelProperty(position = 10, value = "Bootstrap Server Account Timeout (If the value is set to 0, or if this resource is not instantiated, the Bootstrap-Server Account lifetime is infinite.)", example = "0", readOnly = true)
Integer bootstrapServerAccountTimeout = 0;
/** Config -> ObjectId = 1 'LwM2M Server' */
@ApiModelProperty(position = 10, value = "Specify the lifetime of the registration in seconds.", example = "300", readOnly = true)
@ApiModelProperty(position = 11, value = "Specify the lifetime of the registration in seconds.", example = "300", readOnly = true)
private Integer lifetime = 300;
@ApiModelProperty(position = 11, value = "The default value the LwM2M Client should use for the Minimum Period of an Observation in the absence of this parameter being included in an Observation. " +
@ApiModelProperty(position = 12, value = "The default value the LwM2M Client should use for the Minimum Period of an Observation in the absence of this parameter being included in an Observation. " +
"If this Resource doesn’t exist, the default value is 0.", example = "1", readOnly = true)
private Integer defaultMinPeriod = 1;
/** ResourceID=6 'Notification Storing When Disabled or Offline' */
@ApiModelProperty(position = 12, value = "If true, the LwM2M Client stores “Notify” operations to the LwM2M Server while the LwM2M Server account is disabled or the LwM2M Client is offline. After the LwM2M Server account is enabled or the LwM2M Client is online, the LwM2M Client reports the stored “Notify” operations to the Server. " +
@ApiModelProperty(position = 13, value = "If true, the LwM2M Client stores “Notify” operations to the LwM2M Server while the LwM2M Server account is disabled or the LwM2M Client is offline. After the LwM2M Server account is enabled or the LwM2M Client is online, the LwM2M Client reports the stored “Notify” operations to the Server. " +
"If false, the LwM2M Client discards all the “Notify” operations or temporarily disables the Observe function while the LwM2M Server is disabled or the LwM2M Client is offline. " +
"The default value is true.", example = "true", readOnly = true)
private boolean notifIfDisabled = true;

2
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@ApiModel
@Data
@ -41,6 +42,7 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo<RuleNodeId> impl
@Length(fieldName = "type")
@ApiModelProperty(position = 4, value = "Full Java Class Name of the rule node implementation. ", example = "com.mycompany.iot.rule.engine.ProcessingNode")
private String type;
@NoXss
@Length(fieldName = "name")
@ApiModelProperty(position = 5, value = "User defined name of the rule node. Used on UI and for logging. ", example = "Process sensor reading")
private String name;

50
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/secure/TbLwM2MDtlsCertificateVerifier.java

@ -50,13 +50,22 @@ import org.thingsboard.server.transport.lwm2m.server.store.TbMainSecurityStore;
import javax.annotation.PostConstruct;
import javax.security.auth.x500.X500Principal;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mTypeServer.CLIENT;
@ -119,8 +128,8 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
TbLwM2MSecurityInfo securityInfo = null;
// verify if trust
if (config.getTrustSslCredentials().getTrustedCertificates().length > 0) {
if (verifyIssuer(cert, config.getTrustSslCredentials().getTrustedCertificates()) != null) {
if (config.getTrustSslCredentials() != null && config.getTrustSslCredentials().getTrustedCertificates().length > 0) {
if (verifyTrust(cert, config.getTrustSslCredentials().getTrustedCertificates()) != null) {
String endpoint = config.getTrustSslCredentials().getValueFromSubjectNameByKey(cert.getSubjectX500Principal().getName(), "CN");
securityInfo = StringUtils.isNotEmpty(endpoint) ? securityInfoValidator.getEndpointSecurityInfoByCredentialsId(endpoint, CLIENT) : null;
}
@ -193,31 +202,26 @@ public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVer
}
private X509Certificate verifyIssuer(X509Certificate certificate, X509Certificate[] certificates) {
String issuerCN = config.getTrustSslCredentials().getValueFromSubjectNameByKey(certificate.getIssuerX500Principal().getName(), "CN");
if (!StringUtils.isBlank(issuerCN)) {
private X509Certificate verifyTrust(X509Certificate certificate, X509Certificate[] certificates) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
CertPath cp = cf.generateCertPath(Arrays.asList(new X509Certificate[]{certificate}));
for (int index = 0; index < certificates.length; ++index) {
X509Certificate trust = certificates[index];
String trustCN = config.getTrustSslCredentials().getValueFromSubjectNameByKey(trust.getSubjectX500Principal().getName(), "CN");
if (!StringUtils.isBlank(trustCN) && issuerCN.length() >= trustCN.length() && issuerCN.substring(issuerCN.length()-trustCN.length()).equals(trustCN)) {
if (verifyCertificate(certificate)) {
return certificate;
}
X509Certificate caCert = certificates[index];
try {
TrustAnchor trustAnchor = new TrustAnchor(caCert, null);
CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
PKIXParameters pkixParams = new PKIXParameters(
Collections.singleton(trustAnchor));
pkixParams.setRevocationEnabled(false);
if (cpv.validate(cp, pkixParams) != null) return certificate;
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | CertPathValidatorException e) {
log.trace("[{}]. [{}]", certificate.getSubjectDN(), e.getMessage());
}
}
} catch (CertificateException e) {
log.trace("[{}] certPath not valid. [{}]", certificate.getSubjectDN(), e.getMessage());
}
return null;
}
private static boolean verifyCertificate(X509Certificate certificate) {
try {
// date
certificate.checkValidity();
// Validate X509.
SecurityUtil.certificate.decode(certificate.getEncoded());
return true;
} catch (Exception e) {
return false;
}
}
}

2
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/attributes/DefaultLwM2MAttributesService.java

@ -198,7 +198,7 @@ public class DefaultLwM2MAttributesService implements LwM2MAttributesService {
if (pathIdVer != null) {
// #1.1
if (lwM2MClient.getSharedAttributes().containsKey(pathIdVer)) {
if (tsKvProto.getTs() >= lwM2MClient.getSharedAttributes().get(pathIdVer).getTs()) {
if (tsKvProto.getTs() > lwM2MClient.getSharedAttributes().get(pathIdVer).getTs()) {
lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto);
attributesUpdate.put(pathIdVer, tsKvProto);
}

359
common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_credentials.sh

@ -1,359 +0,0 @@
#!/bin/sh
#
# Copyright © 2016-2021 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.
#
#/home/nick/Igor_project/Thingsboard_Perfrmance_test/performance-tests/src/main/resources/credentials/shell/lwM2M_credentials.sh -p LwX509 -s 0 -f 2000 -a client_alias_ -e client_self_signed_ -b bootstrap -d server -j serverKeyStore.jks -k clientKeyStore.jks -c client_ks_password -w server_ks_password
#p) CLIENT_CN=$CLIENT_PREFIX00000000
#s) client_start=0
#f) client_finish=1
#a) CLIENT_ALIAS=CLIENT_ALIAS_PREFIX_00000000
#e) CLIENT_SELF_ALIAS=CLIENT_SELF_ALIAS_PREFIX_00000000
#b) BOOTSTRAP_ALIAS=bootstrap
#d) SERVER_ALIAS=server
#j) SERVER_STORE=serverKeyStore.jks
#k) CLIENT_STORE=clientKeyStore.jks
#c) CLIENT_STORE_PWD=client_ks_password
#w) SERVER_STORE_PWD=server_ks_password
#l) ROOT_KEY_ALIAS=root_key_alias
while getopts p:s:f:a:e:b:d:j:k:c:w:l: flag; do
case "${flag}" in
p) client_pref=${OPTARG} ;;
s) client_start=${OPTARG} ;;
f) client_finish=${OPTARG} ;;
a) client_alias_pref=${OPTARG} ;;
e) client_self_alias_pref=${OPTARG} ;;
b) bootstrap_alias=${OPTARG} ;;
d) server_alias=${OPTARG} ;;
j) key_store_server_file=${OPTARG} ;;
k) key_store_client_file=${OPTARG} ;;
c) client_key_store_pwd=${OPTARG} ;;
w) server_key_store_pwd=${OPTARG} ;;
w) root_key_alias=${OPTARG} ;;
esac
done
# cd to dir of script
script_dir=$(dirname $0)
echo "script_dir: $script_dir"
cd $script_dir
# source the properties:
. ./lwM2M_keygen.properties
if [ -n "$client_pref" ]; then
CLIENT_PREFIX=$client_pref
fi
if [ -z "$client_start" ]; then
client_start=0
fi
if [ -z "$client_finish" ]; then
client_finish=1
fi
if [ -n "$client_alias_pref" ]; then
CLIENT_ALIAS_PREFIX=$client_alias_pref
fi
if [ -n "$client_self_alias_pref" ]; then
CLIENT_SELF_ALIAS_PREFIX=$client_self_alias_pref
fi
if [ -n "$bootstrap_alias" ]; then
BOOTSTRAP_ALIAS=$bootstrap_alias
fi
if [ -n "$server_alias" ]; then
SERVER_ALIAS=$server_alias
fi
if [ -n "$key_store_server_file" ]; then
SERVER_STORE=$key_store_server_file
fi
if [ -n "$key_store_client_file" ]; then
CLIENT_STORE=$key_store_client_file
fi
if [ -n "$client_key_store_pwd" ]; then
CLIENT_STORE_PWD=$client_key_store_pwd
fi
if [ -n "$server_key_store_pwd" ]; then
SERVER_STORE_PWD=$server_key_store_pwd
fi
if [ -n "$root_key_alias" ]; then
ROOT_KEY_ALIAS=$root_key_alias
fi
CLIENT_NUMBER=$client_start
echo "==Start=="
echo "CLIENT_PREFIX: $CLIENT_PREFIX"
echo "client_start: $client_start"
echo "client_finish: $client_finish"
echo "CLIENT_ALIAS_PREFIX: $CLIENT_ALIAS_PREFIX"
echo "CLIENT_SELF_ALIAS_PREFIX: $CLIENT_SELF_ALIAS_PREFIX"
echo "BOOTSTRAP_ALIAS: $BOOTSTRAP_ALIAS"
echo "SERVER_ALIAS: $SERVER_ALIAS"
echo "SERVER_STORE: $SERVER_STORE"
echo "CLIENT_STORE: $CLIENT_STORE"
echo "CLIENT_STORE_PWD: $CLIENT_STORE_PWD"
echo "SERVER_STORE_PWD: $SERVER_STORE_PWD"
echo "CLIENT_NUMBER: $CLIENT_NUMBER"
echo "ROOT_KEY_ALIAS: $ROOT_KEY_ALIAS"
end_point() {
echo "$CLIENT_PREFIX$(printf "%08d" $CLIENT_NUMBER)"
}
client_alias_point() {
echo "$CLIENT_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)"
}
client_self_alias_point() {
echo "$CLIENT_SELF_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)"
}
# Generation of the keystore.
echo "${H0}====START========${RESET}"
echo "${H1}Server Keystore : ${RESET}"
echo "${H1}==================${RESET}"
echo "${H2}Creating the trusted root CA key and certificate...${RESET}"
# -keysize
# 1024 (when using -genkeypair)
keytool \
-genkeypair \
-alias $ROOT_KEY_ALIAS \
-keyalg EC \
-dname "CN=$ROOT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-validity $VALIDITY \
-storetype $STORETYPE \
-keypass $SERVER_STORE_PWD \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD
echo
echo "${H2}Creating server key and self-signed certificate ...${RESET}"
keytool \
-genkeypair \
-alias $SERVER_ALIAS \
-keyalg EC \
-dname "CN=$SERVER_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-validity $VALIDITY \
-storetype $STORETYPE \
-keypass $SERVER_STORE_PWD \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD
keytool \
-exportcert \
-alias $SERVER_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD |
keytool \
-importcert \
-alias $SERVER_SELF_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD \
-noprompt
echo
echo "${H2}Creating server certificate signed by root CA...${RESET}"
keytool \
-certreq \
-alias $SERVER_ALIAS \
-dname "CN=$SERVER_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD |
keytool \
-gencert \
-alias $ROOT_KEY_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD \
-storetype $STORETYPE \
-validity $VALIDITY |
keytool \
-importcert \
-alias $SERVER_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD
echo
echo "${H2}Creating bootstrap key and self-signed certificate ...${RESET}"
keytool \
-genkeypair \
-alias $BOOTSTRAP_ALIAS \
-keyalg EC \
-dname "CN=$BOOTSTRAP_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-validity $VALIDITY \
-storetype $STORETYPE \
-keypass $SERVER_STORE_PWD \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD
keytool \
-exportcert \
-alias $BOOTSTRAP_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD |
keytool \
-importcert \
-alias $BOOTSTRAP_SELF_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD \
-noprompt
echo
echo "${H2}Creating bootstrap certificate signed by root CA...${RESET}"
keytool \
-certreq \
-alias $BOOTSTRAP_ALIAS \
-dname "CN=$BOOTSTRAP_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD |
keytool \
-gencert \
-alias $ROOT_KEY_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD \
-storetype $STORETYPE \
-validity $VALIDITY |
keytool \
-importcert \
-alias $BOOTSTRAP_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD
if [ "$client_start" -lt "$client_finish" ]; then
echo
echo "${H2}Import root certificate just to be able to import need by root CA with expected CN to $CLIENT_STORE${RESET}"
keytool \
-exportcert \
-alias $ROOT_KEY_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD |
keytool \
-importcert \
-alias $ROOT_KEY_ALIAS \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD \
-noprompt
fi
cert_end_point() {
echo
echo "${H1}Client Keystore : ${RESET}"
echo "${H1}==================${RESET}"
echo "${H2}Creating client key and self-signed certificate with expected CN CLIENT_ALIAS: $CLIENT_ALIAS${RESET}"
keytool \
-genkeypair \
-alias $CLIENT_ALIAS \
-keyalg EC \
-dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-validity $VALIDITY \
-storetype $STORETYPE \
-keypass $CLIENT_STORE_PWD \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD
keytool \
-exportcert \
-alias $CLIENT_ALIAS \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD |
keytool \
-importcert \
-alias $CLIENT_SELF_ALIAS \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD \
-noprompt
#
# echo
# echo "${H2}Import root certificate just to be able to import ned by root CA with expected CN...${RESET}"
# keytool \
# -exportcert \
# -alias $ROOT_KEY_ALIAS \
# -keystore $SERVER_STORE \
# -storepass $SERVER_STORE_PWD |
# keytool \
# -importcert \
# -alias $ROOT_KEY_ALIAS \
# -keystore $CLIENT_STORE \
# -storepass $CLIENT_STORE_PWD \
# -noprompt
#
echo
echo "${H2}Creating client certificate signed by root CA with expected CN CLIENT_ALIAS: $CLIENT_ALIAS CLIENT_CN: $CLIENT_CN${RESET}"
keytool \
-certreq \
-alias $CLIENT_ALIAS \
-dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD |
keytool \
-gencert \
-alias $ROOT_KEY_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD \
-storetype $STORETYPE \
-validity $VALIDITY |
keytool \
-importcert \
-alias $CLIENT_ALIAS \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD \
-noprompt
}
if [ "$client_start" -lt "$client_finish" ]; then
echo
echo "==Start Client=="
while [ "$CLIENT_NUMBER" -lt "$client_finish" ]; do
echo "number $CLIENT_NUMBER"
echo "finish $client_finish"
CLIENT_CN=$(end_point)
CLIENT_ALIAS=$(client_alias_point)
CLIENT_SELF_ALIAS=$(client_self_alias_point)
echo "CLIENT_CN $CLIENT_CN"
echo "CLIENT_ALIAS $CLIENT_ALIAS"
echo "CLIENT_SELF_ALIAS $CLIENT_SELF_ALIAS"
cert_end_point
CLIENT_NUMBER=$(($CLIENT_NUMBER + 1))
echo
done
fi
echo
echo "${H0}!!! Warning ${H2}Migrate ${H1}${SERVER_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}"
keytool \
-importkeystore \
-srckeystore $SERVER_STORE \
-destkeystore $SERVER_STORE \
-deststoretype pkcs12 \
-srcstorepass $SERVER_STORE_PWD
if [ "$client_start" -lt "$client_finish" ]; then
echo
echo "${H0}!!! Warning ${H2}Migrate ${H1}${CLIENT_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}"
keytool \
-importkeystore \
-srckeystore $CLIENT_STORE \
-destkeystore $CLIENT_STORE \
-deststoretype pkcs12 \
-srcstorepass $CLIENT_STORE_PWD
fi

57
common/transport/lwm2m/src/main/resources/credentials/shell/lwM2M_keygen.properties

@ -1,57 +0,0 @@
#
# Copyright © 2016-2017 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.
#
# Keystore common parameters
ROOT_KEY_ALIAS=rootCA
DOMAIN_SUFFIX="$(hostname)"
ROOT_CN="$DOMAIN_SUFFIX $ROOT_KEY_ALIAS"
ORGANIZATIONAL_UNIT=Thingsboard
ORGANIZATION=Thingsboard
CITY=SF
STATE_OR_PROVINCE=CA
TWO_LETTER_COUNTRY_CODE=US
VALIDITY=36500 #days
STORETYPE="JKS"
#Server
SERVER_STORE=serverKeyStore1.jks
SERVER_STORE_PWD=server_ks_password1
SERVER_ALIAS=server1
SERVER_CN="$DOMAIN_SUFFIX server LwM2M signed by root CA"
SERVER_SELF_ALIAS=server_self_signed
SERVER_SELF_CN="$DOMAIN_SUFFIX server LwM2M self-signed"
BOOTSTRAP_ALIAS=bootstrap1
BOOTSTRAP_CN="$DOMAIN_SUFFIX bootstrap server LwM2M signed by root CA"
BOOTSTRAP_SELF_ALIAS=bootstrap_self_signed
BOOTSTRAP_SELF_CN="$DOMAIN_SUFFIX bootstrap server LwM2M self-signed"
# Client
CLIENT_STORE=clientKeyStore1.jks
CLIENT_STORE_PWD=client_ks_password1
CLIENT_ALIAS_PREFIX=client_alias_1
CLIENT_PREFIX=LwX509___
CLIENT_SELF_ALIAS_PREFIX=client_self_signed_1
CLIENT_SELF_CN="$DOMAIN_SUFFIX client LwM2M self-signed"
# Color output stuff
red=`tput setaf 1`
green=`tput setaf 2`
blue=`tput setaf 4`
bold=`tput bold`
H0=${red}${bold}
H1=${green}${bold}
H2=${blue}
RESET=`tput sgr0`

BIN
common/transport/lwm2m/src/main/resources/lwm2mserver.jks

Binary file not shown.

26
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/AbstractSslCredentials.java

@ -61,7 +61,7 @@ public abstract class AbstractSslCredentials implements SslCredentials {
this.keyPasswordArray = keyPassword.toCharArray();
}
this.keyStore = this.loadKeyStore(trustsOnly, this.keyPasswordArray);
Set<X509Certificate> trustedCerts = getTrustedCerts(this.keyStore);
Set<X509Certificate> trustedCerts = getTrustedCerts(this.keyStore, trustsOnly);
this.trusts = trustedCerts.toArray(new X509Certificate[0]);
if (!trustsOnly) {
PrivateKeyEntry privateKeyEntry = null;
@ -179,7 +179,7 @@ public abstract class AbstractSslCredentials implements SslCredentials {
return entry;
}
private static Set<X509Certificate> getTrustedCerts(KeyStore ks) {
private static Set<X509Certificate> getTrustedCerts(KeyStore ks, boolean trustsOnly) {
Set<X509Certificate> set = new HashSet<>();
try {
for (Enumeration<String> e = ks.aliases(); e.hasMoreElements(); ) {
@ -187,19 +187,33 @@ public abstract class AbstractSslCredentials implements SslCredentials {
if (ks.isCertificateEntry(alias)) {
Certificate cert = ks.getCertificate(alias);
if (cert instanceof X509Certificate) {
set.add((X509Certificate)cert);
if (trustsOnly) {
// is CA certificate
if (((X509Certificate) cert).getBasicConstraints()>=0) {
set.add((X509Certificate) cert);
}
} else {
set.add((X509Certificate) cert);
}
}
} else if (ks.isKeyEntry(alias)) {
Certificate[] certs = ks.getCertificateChain(alias);
if ((certs != null) && (certs.length > 0) &&
(certs[0] instanceof X509Certificate)) {
set.add((X509Certificate)certs[0]);
if (trustsOnly) {
for (Certificate cert : certs) {
// is CA certificate
if (((X509Certificate) cert).getBasicConstraints()>=0) {
set.add((X509Certificate) cert);
}
}
} else {
set.add((X509Certificate)certs[0]);
}
}
}
}
} catch (KeyStoreException ignored) {}
return Collections.unmodifiableSet(set);
}
}

3
dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogLevelFilter.java

@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class AuditLogLevelFilter {
@ -28,7 +29,7 @@ public class AuditLogLevelFilter {
public AuditLogLevelFilter(Map<String, String> mask) {
entityTypeMask.clear();
mask.forEach((entityTypeStr, logLevelMaskStr) -> {
EntityType entityType = EntityType.valueOf(entityTypeStr.toUpperCase());
EntityType entityType = EntityType.valueOf(entityTypeStr.toUpperCase(Locale.ENGLISH));
AuditLogLevelMask logLevelMask = AuditLogLevelMask.valueOf(logLevelMaskStr.toUpperCase());
entityTypeMask.put(entityType, logLevelMask);
});

6
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -765,15 +765,15 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
X509LwM2MBootstrapServerCredential x509ServerCredentials = (X509LwM2MBootstrapServerCredential) bootstrapServerConfig;
server = x509ServerCredentials.isBootstrapServerIs() ? "Bootstrap Server" : "LwM2M Server";
if (StringUtils.isEmpty(x509ServerCredentials.getServerPublicKey())) {
throw new DeviceCredentialsValidationException(server + " X509 public key must be specified!");
throw new DeviceCredentialsValidationException(server + " X509 certificate must be specified!");
}
try {
String certServer = EncryptionUtil.certTrimNewLines(x509ServerCredentials.getServerPublicKey());
x509ServerCredentials.setServerPublicKey(certServer);
SecurityUtil.publicKey.decode(x509ServerCredentials.getDecodedCServerPublicKey());
SecurityUtil.certificate.decode(x509ServerCredentials.getDecodedCServerPublicKey());
} catch (Exception e) {
throw new DeviceCredentialsValidationException(server + " X509 public key must be in standard [RFC7250] and then encoded to Base64 format!");
throw new DeviceCredentialsValidationException(server + " X509 certificate must be in DER-encoded X509v3 format and support only EC algorithm and then encoded to Base64 format!");
}
break;
}

10
dao/src/main/java/org/thingsboard/server/dao/rpc/BaseRpcService.java

@ -82,7 +82,15 @@ public class BaseRpcService implements RpcService {
log.trace("Executing findAllByDeviceIdAndStatus, tenantId [{}], deviceId [{}], rpcStatus [{}], pageLink [{}]", tenantId, deviceId, rpcStatus, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validatePageLink(pageLink);
return rpcDao.findAllByDeviceId(tenantId, deviceId, rpcStatus, pageLink);
return rpcDao.findAllByDeviceIdAndStatus(tenantId, deviceId, rpcStatus, pageLink);
}
@Override
public PageData<Rpc> findAllByDeviceId(TenantId tenantId, DeviceId deviceId, PageLink pageLink) {
log.trace("Executing findAllByDeviceIdAndStatus, tenantId [{}], deviceId [{}], pageLink [{}]", tenantId, deviceId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validatePageLink(pageLink);
return rpcDao.findAllByDeviceId(tenantId, deviceId, pageLink);
}
private PaginatedRemover<TenantId, Rpc> tenantRpcRemover =

4
dao/src/main/java/org/thingsboard/server/dao/rpc/RpcDao.java

@ -24,7 +24,9 @@ import org.thingsboard.server.common.data.rpc.RpcStatus;
import org.thingsboard.server.dao.Dao;
public interface RpcDao extends Dao<Rpc> {
PageData<Rpc> findAllByDeviceId(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink);
PageData<Rpc> findAllByDeviceId(TenantId tenantId, DeviceId deviceId, PageLink pageLink);
PageData<Rpc> findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink);
PageData<Rpc> findAllRpcByTenantId(TenantId tenantId, PageLink pageLink);

7
dao/src/main/java/org/thingsboard/server/dao/sql/rpc/JpaRpcDao.java

@ -50,7 +50,12 @@ public class JpaRpcDao extends JpaAbstractDao<RpcEntity, Rpc> implements RpcDao
}
@Override
public PageData<Rpc> findAllByDeviceId(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) {
public PageData<Rpc> findAllByDeviceId(TenantId tenantId, DeviceId deviceId, PageLink pageLink) {
return DaoUtil.toPageData(rpcRepository.findAllByTenantIdAndDeviceId(tenantId.getId(), deviceId.getId(), DaoUtil.toPageable(pageLink)));
}
@Override
public PageData<Rpc> findAllByDeviceIdAndStatus(TenantId tenantId, DeviceId deviceId, RpcStatus rpcStatus, PageLink pageLink) {
return DaoUtil.toPageData(rpcRepository.findAllByTenantIdAndDeviceIdAndStatus(tenantId.getId(), deviceId.getId(), rpcStatus, DaoUtil.toPageable(pageLink)));
}

2
dao/src/main/java/org/thingsboard/server/dao/sql/rpc/RpcRepository.java

@ -26,6 +26,8 @@ import org.thingsboard.server.dao.model.sql.RpcEntity;
import java.util.UUID;
public interface RpcRepository extends CrudRepository<RpcEntity, UUID> {
Page<RpcEntity> findAllByTenantIdAndDeviceId(UUID tenantId, UUID deviceId, Pageable pageable);
Page<RpcEntity> findAllByTenantIdAndDeviceIdAndStatus(UUID tenantId, UUID deviceId, RpcStatus status, Pageable pageable);
Page<RpcEntity> findAllByTenantId(UUID tenantId, Pageable pageable);

10
dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java

@ -38,15 +38,15 @@ public class AggregationRepository {
public static final String FROM_WHERE_CLAUSE = "FROM ts_kv tskv WHERE tskv.entity_id = cast(:entityId AS uuid) AND tskv.key= cast(:entityKey AS int) AND tskv.ts > :startTs AND tskv.ts <= :endTs GROUP BY tskv.entity_id, tskv.key, tsBucket ORDER BY tskv.entity_id, tskv.key, tsBucket";
public static final String FIND_AVG_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'AVG' AS aggType ";
public static final String FIND_AVG_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts, :startTs) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'AVG' AS aggType ";
public static final String FIND_MAX_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MAX(COALESCE(tskv.long_v, -9223372036854775807)) AS longValue, MAX(COALESCE(tskv.dbl_v, -1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MAX(tskv.str_v) AS strValue, 'MAX' AS aggType ";
public static final String FIND_MAX_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts, :startTs) AS tsBucket, :timeBucket AS interval, MAX(COALESCE(tskv.long_v, -9223372036854775807)) AS longValue, MAX(COALESCE(tskv.dbl_v, -1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MAX(tskv.str_v) AS strValue, 'MAX' AS aggType ";
public static final String FIND_MIN_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MIN(COALESCE(tskv.long_v, 9223372036854775807)) AS longValue, MIN(COALESCE(tskv.dbl_v, 1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MIN(tskv.str_v) AS strValue, 'MIN' AS aggType ";
public static final String FIND_MIN_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts, :startTs) AS tsBucket, :timeBucket AS interval, MIN(COALESCE(tskv.long_v, 9223372036854775807)) AS longValue, MIN(COALESCE(tskv.dbl_v, 1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MIN(tskv.str_v) AS strValue, 'MIN' AS aggType ";
public static final String FIND_SUM_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, null AS jsonValue, 'SUM' AS aggType ";
public static final String FIND_SUM_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts, :startTs) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, null AS jsonValue, 'SUM' AS aggType ";
public static final String FIND_COUNT_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(CASE WHEN tskv.bool_v IS NULL THEN 0 ELSE 1 END) AS booleanValueCount, SUM(CASE WHEN tskv.str_v IS NULL THEN 0 ELSE 1 END) AS strValueCount, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longValueCount, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleValueCount, SUM(CASE WHEN tskv.json_v IS NULL THEN 0 ELSE 1 END) AS jsonValueCount ";
public static final String FIND_COUNT_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts, :startTs) AS tsBucket, :timeBucket AS interval, SUM(CASE WHEN tskv.bool_v IS NULL THEN 0 ELSE 1 END) AS booleanValueCount, SUM(CASE WHEN tskv.str_v IS NULL THEN 0 ELSE 1 END) AS strValueCount, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longValueCount, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleValueCount, SUM(CASE WHEN tskv.json_v IS NULL THEN 0 ELSE 1 END) AS jsonValueCount ";
@PersistenceContext
private EntityManager entityManager;

34
pom.xml

@ -403,39 +403,9 @@
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>../common/transport/lwm2m/src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.jks</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-lwm2m-resources</id>
<phase>${pkg.process-resources.phase}</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>../transport/lwm2m/src/main/data</outputDirectory>
<resources>
<resource>
<directory>../common/transport/lwm2m/src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.jks</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
<execution>
<id>copy-docker-config</id>
<phase>${pkg.process-resources.phase}</phase>
@ -829,10 +799,6 @@
<exclude>**/*.proto.js</exclude>
<exclude>docker/haproxy/**</exclude>
<exclude>docker/tb-node/**</exclude>
<exclude>src/main/resources/models/*.xml</exclude>
<exclude>src/main/resources/credentials/*.jks</exclude>
<exclude>src/main/resources/credentials/shell/*.jks</exclude>
<exclude>src/main/resources/credentials/shell/*.jks.old</exclude>
<exclude>ui/**</exclude>
<exclude>src/.browserslistrc</exclude>
<exclude>**/yarn.lock</exclude>

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

@ -179,14 +179,19 @@ public class TbSaveToCustomCassandraTableNode implements TbNode {
if (key.equals(ENTITY_ID)) {
stmtBuilder.setUuid(i.get(), msg.getOriginator().getId());
} else if (dataAsObject.has(key)) {
if (dataAsObject.get(key).isJsonPrimitive()) {
JsonPrimitive primitive = dataAsObject.get(key).getAsJsonPrimitive();
JsonElement dataKeyElement = dataAsObject.get(key);
if (dataKeyElement.isJsonPrimitive()) {
JsonPrimitive primitive = dataKeyElement.getAsJsonPrimitive();
if (primitive.isNumber()) {
stmtBuilder.setLong(i.get(), dataAsObject.get(key).getAsLong());
if (primitive.getAsString().contains(".")) {
stmtBuilder.setDouble(i.get(), primitive.getAsDouble());
} else {
stmtBuilder.setLong(i.get(), primitive.getAsLong());
}
} else if (primitive.isBoolean()) {
stmtBuilder.setBoolean(i.get(), dataAsObject.get(key).getAsBoolean());
stmtBuilder.setBoolean(i.get(), primitive.getAsBoolean());
} else if (primitive.isString()) {
stmtBuilder.setString(i.get(), dataAsObject.get(key).getAsString());
stmtBuilder.setString(i.get(), primitive.getAsString());
} else {
stmtBuilder.setToNull(i.get());
}

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

@ -104,7 +104,8 @@ public class TbMqttNode implements TbNode {
protected MqttClient initClient(TbContext ctx) throws Exception {
MqttClientConfig config = new MqttClientConfig(getSslContext());
if (!StringUtils.isEmpty(this.mqttNodeConfiguration.getClientId())) {
config.setClientId(this.mqttNodeConfiguration.getClientId());
config.setClientId(this.mqttNodeConfiguration.isAppendClientIdSuffix() ?
this.mqttNodeConfiguration.getClientId() + "_" + ctx.getServiceId() : this.mqttNodeConfiguration.getClientId());
}
config.setCleanSession(this.mqttNodeConfiguration.isCleanSession());

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

@ -28,6 +28,7 @@ public class TbMqttNodeConfiguration implements NodeConfiguration<TbMqttNodeConf
private int port;
private int connectTimeoutSec;
private String clientId;
private boolean appendClientIdSuffix;
private boolean cleanSession;
private boolean ssl;

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

@ -46,8 +46,16 @@ import java.util.concurrent.TimeUnit;
configClazz = TbMsgTimeseriesNodeConfiguration.class,
nodeDescription = "Saves timeseries data",
nodeDetails = "Saves timeseries telemetry data based on configurable TTL parameter. Expects messages with 'POST_TELEMETRY_REQUEST' message type. " +
"Timestamp in milliseconds will be taken from metadata.ts, otherwise 'now' timestamp will be applied. " +
"Allows stopping updating values for incoming keys in the latest ts_kv table if 'skipLatestPersistence' is set to true.",
"Timestamp in milliseconds will be taken from metadata.ts, otherwise 'now' message timestamp will be applied. " +
"Allows stopping updating values for incoming keys in the latest ts_kv table if 'skipLatestPersistence' is set to true.\n " +
"<br/>" +
"Enable 'useServerTs' param to use the timestamp of the message processing instead of the timestamp from the message. " +
"Useful for all sorts of sequential processing if you merge messages from multiple sources (devices, assets, etc).\n" +
"<br/>" +
"In the case of sequential processing, the platform guarantees that the messages are processed in the order of their submission to the queue. " +
"However, the timestamp of the messages originated by multiple devices/servers may be unsynchronized long before they are pushed to the queue. " +
"The DB layer has certain optimizations to ignore the updates of the \"attributes\" and \"latest values\" tables if the new record has a timestamp that is older than the previous record. " +
"So, to make sure that all the messages will be processed correctly, one should enable this parameter for sequential message processing scenarios.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeTimeseriesConfig",
icon = "file_upload"
@ -77,7 +85,7 @@ public class TbMsgTimeseriesNode implements TbNode {
ctx.tellFailure(msg, new IllegalArgumentException("Unsupported msg type: " + msg.getType()));
return;
}
long ts = getTs(msg);
long ts = computeTs(msg, config.isUseServerTs());
String src = msg.getData();
Map<Long, List<KvEntry>> tsKvMap = JsonConverter.convertToTelemetry(new JsonParser().parse(src), ts);
if (tsKvMap.isEmpty()) {
@ -102,6 +110,10 @@ public class TbMsgTimeseriesNode implements TbNode {
}
}
public static long computeTs(TbMsg msg, boolean ignoreMetadataTs) {
return ignoreMetadataTs ? System.currentTimeMillis() : getTs(msg);
}
public static long getTs(TbMsg msg) {
long ts = -1;
String tsStr = msg.getMetaData().getValue("ts");

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java

@ -23,12 +23,14 @@ public class TbMsgTimeseriesNodeConfiguration implements NodeConfiguration<TbMsg
private long defaultTTL;
private boolean skipLatestPersistence;
private boolean useServerTs;
@Override
public TbMsgTimeseriesNodeConfiguration defaultConfiguration() {
TbMsgTimeseriesNodeConfiguration configuration = new TbMsgTimeseriesNodeConfiguration();
configuration.setDefaultTTL(0L);
configuration.setSkipLatestPersistence(false);
configuration.setUseServerTs(false);
return configuration;
}
}

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

File diff suppressed because one or more lines are too long

423
tools/src/main/shell/lwm2m/lwM2M_cfssl_chain_clients_for_test.sh

@ -0,0 +1,423 @@
#!/usr/bin/env bash
#
# Copyright © 2016-2021 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.
#
# Change working directory
cd -- "$(
dirname "${0}"
)" || exit 1
readonly TRUST_PATH="Trust"
readonly CA_ROOT_CERT_KEY="ca-root"
readonly CA_ROOT_ALIAS="root"
readonly CA_INTERMEDIATE_CERT_KEY_PREF="intermediate_ca"
CA_INTERMEDIATE_START=0
CA_INTERMEDIATE_FINISH=2
CA_INTERMEDIATE_NUMBER=${CA_INTERMEDIATE_START}
CA_INTERMEDIATE_CERT_SIGN=${CA_ROOT_CERT_KEY}
CA_LIST_CERT_FOR_CAT=""
readonly CA_TRUST_STORE_ALL_CHAIN="lwm2mtruststorechain"
readonly CA_TRUST_STORE_PWD="server_ks_password"
readonly CA_TRUST_CERT_ALIAS="root"
readonly CA_TRUST_CERT_CHAIN_JKS="lwm2mtruststorechain"
readonly CA_TRUST_STORE_CHAIN_ALIAS="trust_cert_chain_alias"
readonly CLIENT_PATH="Client"
readonly CLIENT_JKS_FOR_TEST="lwm2mclient"
readonly CLIENT_CERT_KEY_PREF="LwX509"
readonly CLIENT_CERT_ALIAS_PREF="client_alias_"
readonly CLIENT_STORE_PWD="client_ks_password"
readonly CLIENT_HOST_NAME="thingsboard_test.io"
readonly TRUST_NO_PATH="TrustNo"
readonly CA_ROOT_NO_ALIAS="root-no"
readonly CLIENT_CERT_TRUST_NO_KEY="LwX509TrustNo"
readonly CLIENT_CERT_ALIAS_TRUST_NO="client_alias_trust_no"
CLIENT_START=0
CLIENT_FINISH=1
CLIENT_NUMBER=${CLIENT_START}
SERVER_HOST_NAME="localhost.localdomain"
SERVER_LOCAL_HOST_NAME="localhost"
SERVER_PUBLIC_HOST_NAMES="-"
readonly CF_COMMANDS="
cfssl
cfssljson
"
if [ ! -z "$1" ]; then
CA_INTERMEDIATE_START=$1
CA_INTERMEDIATE_NUMBER=${CA_INTERMEDIATE_START}
fi
if [ ! -z "$2" ]; then
CA_INTERMEDIATE_FINISH=$2
fi
if [ ! -z "$3" ]; then
CLIENT_START=$1
CLIENT_NUMBER=${CLIENT_START}
fi
if [ ! -z "$4" ]; then
CLIENT_FINISH=$4
fi
# Change working directory
rm -rf ${TRUST_PATH}
mkdir -p ${TRUST_PATH}
rm -rf ${TRUST_NO_PATH}
mkdir -p ${TRUST_NO_PATH}
rm -rf ${CLIENT_PATH}
mkdir -p ${CLIENT_PATH}
cd -- "$(
dirname "${0}"
)" || exit 1
rm *.csr
rm *.p12
rm *.json
rm *.pem
rm *.jks
intermediate_common_name() {
echo "${CA_INTERMEDIATE_CERT_KEY_PREF}${CA_INTERMEDIATE_NUMBER}"
}
set_list_sert_for_cat() {
local first="$1"
echo "$first ${CA_LIST_CERT_FOR_CAT}"
}
client_common_name() {
echo "${CLIENT_CERT_KEY_PREF}$(printf "%08d" ${CLIENT_NUMBER})"
}
client_alias_name() {
echo "${CLIENT_CERT_ALIAS_PREF}$(printf "%08d" ${CLIENT_NUMBER})"
}
for COMMAND in ${CF_COMMANDS}; do
if ! command -v ${COMMAND} &> /dev/null; then
echo "ERROR: Missing command ${COMMAND}" >&2
echo "Install the package from: https://pkg.cfssl.org/" >&2
exit 1
fi
done
tee ./${TRUST_PATH}/ca-config.json 1> /dev/null <<-CONFIG
{
"signing": {
"default": {
"expiry": "8760h",
"names": [
{
"C": "UK",
"ST": "Kyiv city",
"L": "Kyiv",
"O": "Thingsboard",
"OU": "DEVELOPER_TEST"
}
]
},
"profiles": {
"server": {
"expiry": "43800h",
"key": {
"algo": "ecdsa",
"size": 256
},
"usages": [
"signing",
"key encipherment",
"server auth"
]
},
"client": {
"expiry": "43800h",
"key": {
"algo": "ecdsa",
"size": 256
},
"usages": [
"signing",
"key encipherment",
"client auth"
]
},
"client-server": {
"expiry": "43800h",
"key": {
"algo": "ecdsa",
"size": 256
},
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
CONFIG
tee ./${TRUST_PATH}/ca-root-to-intermediate-config.json 1> /dev/null <<-CONFIG
{
"signing": {
"default": {
"expiry": "43800h",
"ca_constraint": {
"is_ca": true,
"max_path_len": 0,
"max_path_len_zero": true
},
"key": {
"algo": "ecdsa",
"size": 256
},
"usages": [
"digital signature",
"cert sign",
"crl sign",
"signing"
]
}
}
}
CONFIG
echo "===================================================="
echo -e "Generate the root of certificates: \n-${CA_ROOT_CERT_KEY}-key.pem (certificate key)\n-${CA_ROOT_CERT_KEY}.pem (certificate)\n-${CA_ROOT_CERT_KEY}.csr (sign request)"
echo "===================================================="
cfssl genkey \
-initca \
- \
<<-CONFIG | cfssljson -bare ./${TRUST_PATH}/${CA_ROOT_CERT_KEY}
{
"CN": "ROOT CA",
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"C": "UK",
"ST": "Kyiv city",
"L": "Kyiv",
"O": "Thingsboard",
"OU": "DEVELOPER_TEST"
}
],
"ca": {
"expiry": "131400h"
}
}
CONFIG
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${TRUST_PATH}/${CA_ROOT_CERT_KEY}.pem)
echo "===================================================="
echo -e "Generate and Signed the intermediates of our certificates: \n-${CA_INTERMEDIATE_CERT_KEY_PREF}?-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY_PREF}?.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY_PREF}?.csr (sign request)"
echo "===================================================="
while [[ ${CA_INTERMEDIATE_NUMBER} -lt ${CA_INTERMEDIATE_FINISH} ]];
do
CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name)
CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1))
cfssl gencert \
-ca ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \
-ca-key ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \
-config ./${TRUST_PATH}/ca-root-to-intermediate-config.json \
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \
- \
<<-CONFIG | cfssljson -bare ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}
{
"CN": "${CA_INTERMEDIATE_CERT_KEY}",
"names": [
{
"C": "UK",
"ST": "Kyiv city",
"L": "Kyiv",
"O": "Thingsboard",
"OU": "DEVELOPER_TEST"
}
]
}
CONFIG
#openssl x509 -in ${CA_INTERMEDIATE_CERT_KEY}.pem -text -noout
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem)
CA_INTERMEDIATE_CERT_SIGN=${CA_INTERMEDIATE_CERT_KEY}
done
echo "===================================================="
echo -e "Add the CA_certificate to keystore: ${CA_TRUST_CERT_CHAIN_JKS}.jks"
echo "===================================================="
cat ${CA_LIST_CERT_FOR_CAT} > ./${TRUST_PATH}/${CA_TRUST_STORE_ALL_CHAIN}.pem
openssl pkcs12 -export -in ./${TRUST_PATH}/${CA_TRUST_STORE_ALL_CHAIN}.pem -inkey ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem -out ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.p12 -name ${CA_TRUST_STORE_CHAIN_ALIAS} -CAfile ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${CA_TRUST_STORE_PWD} -passout pass:${CA_TRUST_STORE_PWD}
keytool -importkeystore -deststorepass ${CA_TRUST_STORE_PWD} -destkeypass ${CA_TRUST_STORE_PWD} -destkeystore ./${TRUST_PATH}/${CA_TRUST_CERT_CHAIN_JKS}.jks -srckeystore ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${CA_TRUST_STORE_PWD} -alias ${CA_TRUST_STORE_CHAIN_ALIAS}
keytool -list -v -keystore ./${TRUST_PATH}/lwm2mtruststorechain.jks -storepass server_ks_password -storetype PKCS12
echo "===================================================="
echo -e "Generate and Signed the clients of our certificates: \n-${CLIENT_CERT_KEY_PREF}?-key.pem (certificate key)\n-${CLIENT_CERT_KEY_PREF}?.pem (certificate)\n-${CCLIENT_CERT_KEY_PREF}?.csr (sign request)"
echo "===================================================="
while [[ ${CLIENT_NUMBER} -lt ${CLIENT_FINISH} ]];
do
CLIENT_CERT_KEY=$(client_common_name)
CLIENT_CERT_ALIAS=$(client_alias_name)
CLIENT_NUMBER=$((${CLIENT_NUMBER} + 1))
cfssl gencert \
-ca ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \
-ca-key ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \
-config ./${TRUST_PATH}/ca-config.json \
-profile client \
-hostname "${CLIENT_HOST_NAME}" \
- \
<<-CONFIG | cfssljson -bare ./${CLIENT_PATH}/${CLIENT_CERT_KEY}
{
"CN": "${CLIENT_CERT_KEY}"
}
CONFIG
echo "===================================================="
echo -e "Add the client certificate (${CLIENT_CERT_KEY}.pem) to keystore: ${CLIENT_JKS_FOR_TEST}.jks"
echo "===================================================="
cat ./${CLIENT_PATH}/${CLIENT_CERT_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${CLIENT_PATH}/${CLIENT_CERT_KEY}_chain.pem
openssl pkcs12 -export -in ./${CLIENT_PATH}/${CLIENT_CERT_KEY}_chain.pem -inkey ./${CLIENT_PATH}/${CLIENT_CERT_KEY}-key.pem -out ./${CLIENT_PATH}/${CLIENT_CERT_KEY}.p12 -name ${CLIENT_CERT_ALIAS} -CAfile ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${CLIENT_STORE_PWD} -passout pass:${CLIENT_STORE_PWD}
keytool -importkeystore -deststorepass ${CLIENT_STORE_PWD} -destkeypass ${CLIENT_STORE_PWD} -destkeystore ./${CLIENT_PATH}/${CLIENT_JKS_FOR_TEST}.jks -srckeystore ./${CLIENT_PATH}/${CLIENT_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${CLIENT_STORE_PWD} -alias ${CLIENT_CERT_ALIAS}
done
#keytool -list -v -keystore ./${CLIENT_PATH}/lwm2mclient.jks -storepass client_ks_password -storetype PKCS12
echo "===================================================="
echo -e "Generate the root no trust in ${TRUST_NO_PATH} of certificates: \n-${CA_ROOT_CERT_KEY}-key.pem (certificate key)\n-${CA_ROOT_CERT_KEY}.pem (certificate)\n-${CA_ROOT_CERT_KEY}.csr (sign request)"
echo "===================================================="
cfssl genkey \
-initca \
- \
<<-CONFIG | cfssljson -bare ./${TRUST_NO_PATH}/${CA_ROOT_CERT_KEY}
{
"CN": "ROOT CA NO TRUST",
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"C": "UK",
"ST": "Kyiv city",
"L": "Kyiv",
"O": "Thingsboard",
"OU": "DEVELOPER_TEST"
}
],
"ca": {
"expiry": "131400h"
}
}
CONFIG
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${TRUST_NO_PATH}/${CA_ROOT_CERT_KEY}.pem)
echo "===================================================="
echo -e "Generate and Signed the intermediates of our no trust in ${TRUST_NO_PATH} certificate: \n-${CA_INTERMEDIATE_CERT_KEY_PREF}?-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY_PREF}?.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY_PREF}?.csr (sign request)"
echo "===================================================="
CA_INTERMEDIATE_CERT_SIGN=${CA_ROOT_CERT_KEY}
CA_LIST_CERT_FOR_CAT=""
CA_INTERMEDIATE_NUMBER=0
while [[ ${CA_INTERMEDIATE_NUMBER} -lt ${CA_INTERMEDIATE_FINISH} ]];
do
CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name)
CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1))
cfssl gencert \
-ca ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \
-ca-key ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \
-config ./${TRUST_PATH}/ca-root-to-intermediate-config.json \
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \
- \
<<-CONFIG | cfssljson -bare ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_KEY}
{
"CN": "${CA_INTERMEDIATE_CERT_KEY}_TRUST_NO",
"names": [
{
"C": "UK",
"ST": "Kyiv city",
"L": "Kyiv",
"O": "Thingsboard",
"OU": "DEVELOPER_TEST"
}
]
}
CONFIG
#openssl x509 -in ${CA_INTERMEDIATE_CERT_KEY}.pem -text -noout
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem)
CA_INTERMEDIATE_CERT_SIGN=${CA_INTERMEDIATE_CERT_KEY}
done
echo "===================================================="
echo -e "Generate and Signed the client no trust of our certificate: \n-${CLIENT_CERT_TRUST_NO_KEY}-key.pem (certificate key)\n-${CLIENT_CERT_TRUST_NO_KEY}.pem (certificate)\n-${CLIENT_CERT_TRUST_NO_KEY}.csr (sign request)"
echo "===================================================="
CLIENT_CERT_ALIAS=$(client_alias_name)
CLIENT_NUMBER=$((${CLIENT_NUMBER} + 1))
cfssl gencert \
-ca ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \
-ca-key ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \
-config ./${TRUST_PATH}/ca-config.json \
-profile client \
-hostname "${CLIENT_HOST_NAME}" \
- \
<<-CONFIG | cfssljson -bare ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}
{
"CN": "${CLIENT_CERT_TRUST_NO_KEY}"
}
CONFIG
echo "===================================================="
echo -e "Add the client certificate no trust (${CLIENT_CERT_TRUST_NO_KEY}.pem) to keystore: ${CLIENT_JKS_FOR_TEST}.jks"
echo "===================================================="
cat ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}_chain.pem
openssl pkcs12 -export -in ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}_chain.pem -inkey ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}-key.pem -out ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}.p12 -name ${CLIENT_CERT_ALIAS_TRUST_NO} -CAfile ./${TRUST_NO_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_NO_ALIAS} -passin pass:${CLIENT_STORE_PWD} -passout pass:${CLIENT_STORE_PWD}
keytool -importkeystore -deststorepass ${CLIENT_STORE_PWD} -destkeypass ${CLIENT_STORE_PWD} -destkeystore ./${CLIENT_PATH}/${CLIENT_JKS_FOR_TEST}.jks -srckeystore ./${CLIENT_PATH}/${CLIENT_CERT_TRUST_NO_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${CLIENT_STORE_PWD} -alias ${CLIENT_CERT_ALIAS_TRUST_NO}
keytool -list -v -keystore ./${CLIENT_PATH}/lwm2mclient.jks -storepass client_ks_password -storetype PKCS12
rm ./${TRUST_PATH}/*.p12 2> /dev/null
rm ./${TRUST_PATH}/*.csr 2> /dev/null
rm ./${TRUST_PATH}/*.json 2> /dev/null
rm ./${TRUST_PATH}/${CA_ROOT_CERT_KEY}* 2> /dev/null
rm ./${TRUST_PATH}/${CA_INTERMEDIATE_CERT_KEY_PREF}* 2> /dev/null
rm -rf ${TRUST_NO_PATH} 2> /dev/null
rm ./${CLIENT_PATH}/*.p12 2> /dev/null
rm ./${CLIENT_PATH}/*.csr 2> /dev/null

81
tools/src/main/shell/lwm2m/lwm2m_cfssl_chain_all_for_test.sh

@ -0,0 +1,81 @@
#!/usr/bin/env bash
#
# Copyright © 2016-2021 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.
#
readonly INTERMEDIATE_START=0
readonly INTERMEDIATE_FINISH=2
readonly CLIENT_START=0
readonly CLIENT_FINISH=5
IS_IHFO=false
IS_SERVER_CREATED_KEY=true
IS_TRUST_CLIENT_CREATED_KEY=true
cd -- "$(
dirname "${0}"
)" || exit 1
Help()
{
# Display Help
echo "Description of the script functions."
echo
echo "Syntax: scriptTemplate [-g|h|v|V]"
echo "options:"
echo "h Print this Help."
echo "v Verbose mode."
echo "V Print software version and exit."
echo
}
if [ "$1" == "-h" ] ; then
echo -e "Usage 1: ./`basename $0` \"Information is not displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are generated\""
echo -e "Usage 2: ./`basename $0` true \"Information is displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are generated\""
echo -e "Usage 3: ./`basename $0` true false \"Information is displayed\" : \"Keys for the server are not generated\" : \"Keys for the clients and trusts are generated\""
echo -e "Usage 4: ./`basename $0` true false false \"Information is displayed\" : \"Keys for the server are not generated\" : \"Keys for the clients and trusts are not generated\""
echo -e "Usage 5: ./`basename $0` true true false \"Information is displayed\" : \"Keys for the server are generated\" : \"Keys for the clients and trusts are not generated\""
echo "This Help File: ./`basename $0` -h"
exit 0
fi
if [ -n "$1" ]; then
IS_IHFO=$1
fi
if [ -n "$2" ]; then
IS_SERVER_CREATED_KEY=$2
fi
if [ -n "$3" ]; then
IS_TRUST_CLIENT_CREATED_KEY=$3
fi
if [ "$IS_IHFO" = false ] ; then
if [ "$IS_SERVER_CREATED_KEY" = true ] ; then
./lwm2m_cfssl_chain_server_for_test.sh > /dev/null 2>&1 &
fi
if [ "$IS_TRUST_CLIENT_CREATED_KEY" = true ] ; then
./lwM2M_cfssl_chain_clients_for_test.sh ${INTERMEDIATE_START} ${INTERMEDIATE_FINISH} ${CLIENT_START} ${CLIENT_FINISH} > /dev/null 2>&1 &
fi
else
if [ "$IS_SERVER_CREATED_KEY" = true ] ; then
./lwm2m_cfssl_chain_server_for_test.sh
fi
if [ "$IS_TRUST_CLIENT_CREATED_KEY" = true ] ; then
./lwM2M_cfssl_chain_clients_for_test.sh ${INTERMEDIATE_START} ${INTERMEDIATE_FINISH} ${CLIENT_START} ${CLIENT_FINISH}
fi
fi

314
tools/src/main/shell/lwm2m/lwm2m_cfssl_chain_server_for_test.sh

@ -0,0 +1,314 @@
#!/usr/bin/env bash
#
# Copyright © 2016-2021 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.
#
# REF: https://github.com/cloudflare/cfssl
# Change working directory
cd -- "$(
dirname "${0}"
)" || exit 1
readonly CA_ROOT_CERT_KEY="ca-root"
readonly CA_ROOT_ALIAS="root"
readonly CA_INTERMEDIATE_CERT_KEY_PREF="intermediate_ca"
CA_INTERMEDIATE_NUMBER=0
CA_LIST_CERT_FOR_CAT=""
readonly CF_COMMANDS="
cfssl
cfssljson
"
readonly SERVER_JKS_FOR_TEST="lwm2mserver"
readonly STORE_PASS_PWD="server_ks_password"
readonly SERVER_PATH="Server"
readonly SERVER_CERT_KEY="lwm2mserver"
readonly SERVER_CERT_CHAIN="lwm2mserver_chain"
readonly SERVER_CERT_ALIAS="server"
readonly BS_SERVER_CERT_KEY="lwm2mserverbs"
readonly BS_SERVER_CERT_CHAIN="lwm2mserverbs_chain"
readonly BS_SERVER_CERT_ALIAS="bootstrap"
SERVER_HOST_NAME="localhost.localdomain"
SERVER_LOCAL_HOST_NAME="localhost"
SERVER_PUBLIC_HOST_NAMES="-"
intermediate_common_name() {
echo "${CA_INTERMEDIATE_CERT_KEY_PREF}${CA_INTERMEDIATE_NUMBER}"
}
set_list_sert_for_cat() {
local first="$1"
echo "$first ${CA_LIST_CERT_FOR_CAT}"
}
# Change working directory
rm -rf ${SERVER_PATH}
mkdir -p ${SERVER_PATH}
cd -- "$(
dirname ./${SERVER_PATH}
)" || exit 1
rm *.csr
rm *.p12
rm *.json
rm *.pem
rm *.jks
CA_INTERMEDIATE_CERT_SIGN=${CA_ROOT_CERT_KEY}
CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name)
CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1))
CA_LIST_CERT_FOR_CAT=""
for COMMAND in ${CF_COMMANDS}; do
if ! command -v ${COMMAND} &> /dev/null; then
echo "ERROR: Missing command ${COMMAND}" >&2
echo "Install the package from: https://pkg.cfssl.org/" >&2
exit 1
fi
done
tee ./${SERVER_PATH}/ca-config.json 1> /dev/null <<-CONFIG
{
"signing": {
"default": {
"expiry": "8760h",
"names": [
{
"C": "UK",
"ST": "Kyiv city",
"L": "Kyiv",
"O": "Thingsboard",
"OU": "DEVELOPER_TEST"
}
]
},
"profiles": {
"server": {
"expiry": "43800h",
"key": {
"algo": "ecdsa",
"size": 256
},
"usages": [
"signing",
"key encipherment",
"server auth"
]
},
"client": {
"expiry": "43800h",
"key": {
"algo": "ecdsa",
"size": 256
},
"usages": [
"signing",
"key encipherment",
"client auth"
]
},
"client-server": {
"expiry": "43800h",
"key": {
"algo": "ecdsa",
"size": 256
},
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
CONFIG
tee ./${SERVER_PATH}/ca-root-to-intermediate-config.json 1> /dev/null <<-CONFIG
{
"signing": {
"default": {
"expiry": "43800h",
"ca_constraint": {
"is_ca": true,
"max_path_len": 0,
"max_path_len_zero": true
},
"key": {
"algo": "ecdsa",
"size": 256
},
"usages": [
"digital signature",
"cert sign",
"crl sign",
"signing"
]
}
}
}
CONFIG
echo "===================================================="
echo -e "Generate the root of certificates: \n-${CA_ROOT_KEY}-key.pem (certificate key)\n-${CA_ROOT_KEY}.pem (certificate)\n-${CA_ROOT_KEY}.csr (sign request)"
echo "===================================================="
cfssl genkey \
-initca \
- \
<<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${CA_ROOT_CERT_KEY}
{
"CN": "ROOT CA for servers",
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"C": "UK",
"ST": "Kyiv city",
"L": "Kyiv",
"O": "Thingsboard",
"OU": "DEVELOPER_TEST"
}
],
"ca": {
"expiry": "131400h"
}
}
CONFIG
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${SERVER_PATH}/${CA_ROOT_CERT_KEY}.pem)
echo "===================================================="
echo -e "Generate and Signed the first intermediates of our certificates: \n-${CA_INTERMEDIATE_CERT_KEY}-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY}.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY}.csr (sign request)"
echo "===================================================="
cfssl gencert \
-ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \
-ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \
-config ./${SERVER_PATH}/ca-root-to-intermediate-config.json \
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \
- \
<<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}
{
"CN": "${CA_INTERMEDIATE_CERT_KEY}",
"names": [
{
"C": "UK",
"ST": "Kyiv city",
"L": "Kyiv",
"O": "Thingsboard",
"OU": "DEVELOPER_TEST"
}
]
}
CONFIG
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem)
## Lwm2m Server certificate
echo "===================================================="
echo -e "Generate and Signed the server certificate: \n-${SERVER_CERT_KEY}-key.pem (certificate key)\n-${SERVER_CERT_KEY}.pem (certificate)\n-${SERVER_CERT_KEY}.csr (sign request)"
echo "===================================================="
cfssl gencert \
-ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \
-ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \
-config ./${SERVER_PATH}/ca-config.json \
-profile server \
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \
- \
<<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${SERVER_CERT_KEY}
{
"CN": "${SERVER_LOCAL_HOST_NAME}"
}
CONFIG
echo "===================================================="
echo -e "Add the server certificate (${SERVER_CERT_KEY}.pem) to keystore: ${SERVER_JKS_FOR_TEST}.jks"
echo "===================================================="
cat ./${SERVER_PATH}/${SERVER_CERT_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${SERVER_PATH}/${SERVER_CERT_CHAIN}.pem
openssl pkcs12 -export -in ./${SERVER_PATH}/${SERVER_CERT_CHAIN}.pem -inkey ./${SERVER_PATH}/${SERVER_CERT_KEY}-key.pem -out ./${SERVER_PATH}/${SERVER_CERT_KEY}.p12 -name ${SERVER_CERT_ALIAS} -CAfile ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${STORE_PASS_PWD} -passout pass:${STORE_PASS_PWD}
keytool -importkeystore -deststorepass ${STORE_PASS_PWD} -destkeypass ${STORE_PASS_PWD} -destkeystore ./${SERVER_PATH}/${SERVER_JKS_FOR_TEST}.jks -srckeystore ./${SERVER_PATH}/${SERVER_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${STORE_PASS_PWD} -alias ${SERVER_CERT_ALIAS}
CA_INTERMEDIATE_CERT_SIGN=${CA_INTERMEDIATE_CERT_KEY}
CA_INTERMEDIATE_CERT_KEY=$(intermediate_common_name)
CA_INTERMEDIATE_NUMBER=$((${CA_INTERMEDIATE_NUMBER} + 1))
echo "===================================================="
echo -e "Generate and Signed the second intermediates of our certificates: \n-${CA_INTERMEDIATE_CERT_KEY}-key.pem (certificate key)\n-${CA_INTERMEDIATE_CERT_KEY}.pem (certificate)\n-${CA_INTERMEDIATE_CERT_KEY}.csr (sign request)"
echo "===================================================="
cfssl gencert \
-ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}.pem \
-ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_SIGN}-key.pem \
-config ./${SERVER_PATH}/ca-root-to-intermediate-config.json \
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \
- \
<<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}
{
"CN": "${CA_INTERMEDIATE_CERT_KEY}",
"names": [
{
"C": "UK",
"ST": "Kyiv city",
"L": "Kyiv",
"O": "Thingsboard",
"OU": "DEVELOPER_TEST"
}
]
}
CONFIG
CA_LIST_CERT_FOR_CAT=$(set_list_sert_for_cat ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem)
## Bootstrap server certificate
echo "===================================================="
echo -e "Generate and Signed the server certificate: \n-${BS_SERVER_CERT_KEY}-key.pem (certificate key)\n-${BS_SERVER_CERT_KEY}.pem (certificate)\n-${BS_SERVER_CERT_KEY}.csr (sign request)"
echo "===================================================="
cfssl gencert \
-ca ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem \
-ca-key ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}-key.pem \
-config ./${SERVER_PATH}/ca-config.json \
-profile server \
-hostname "${SERVER_HOST_NAME},${SERVER_LOCAL_HOST_NAME}${SERVER_PUBLIC_HOST_NAMES:+, }${SERVER_PUBLIC_HOST_NAMES}" \
- \
<<-CONFIG | cfssljson -bare ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}
{
"CN": "${SERVER_LOCAL_HOST_NAME}"
}
CONFIG
echo "===================================================="
echo -e "Add the Bootstrap server certificate (${BS_SERVER_CERT_KEY}.pem) to keystore: ${SERVER_JKS_FOR_TEST}.jks"
echo "===================================================="
cat ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}.pem ${CA_LIST_CERT_FOR_CAT} > ./${SERVER_PATH}/${BS_SERVER_CERT_CHAIN}.pem
openssl pkcs12 -export -in ./${SERVER_PATH}/${BS_SERVER_CERT_CHAIN}.pem -inkey ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}-key.pem -out ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}.p12 -name ${BS_SERVER_CERT_ALIAS} -CAfile ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY}.pem -caname ${CA_ROOT_ALIAS} -passin pass:${STORE_PASS_PWD} -passout pass:${STORE_PASS_PWD}
keytool -importkeystore -deststorepass ${STORE_PASS_PWD} -destkeypass ${STORE_PASS_PWD} -destkeystore ./${SERVER_PATH}/${SERVER_JKS_FOR_TEST}.jks -srckeystore ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}.p12 -srcstoretype PKCS12 -srcstorepass ${STORE_PASS_PWD} -alias ${BS_SERVER_CERT_ALIAS}
keytool -list -v -keystore ./${SERVER_PATH}/lwm2mserver.jks -storepass server_ks_password -storetype PKCS12
rm ./${SERVER_PATH}/*.p12 2> /dev/null
rm ./${SERVER_PATH}/*.csr 2> /dev/null
rm ./${SERVER_PATH}/*.json 2> /dev/null
rm ./${SERVER_PATH}/${CA_INTERMEDIATE_CERT_KEY_PREF}* 2> /dev/null
rm ./${SERVER_PATH}/${CA_ROOT_CERT_KEY}* 2> /dev/null
mv ./${SERVER_PATH}/${SERVER_CERT_KEY}-key.pem ./${SERVER_PATH}/${SERVER_CERT_KEY}_key.pem
mv ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}-key.pem ./${SERVER_PATH}/${BS_SERVER_CERT_KEY}_key.pem

6
transport/coap/src/main/resources/tb-coap-transport.yml

@ -271,7 +271,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -286,7 +286,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -301,7 +301,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;

6
transport/http/src/main/resources/tb-http-transport.yml

@ -259,7 +259,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -274,7 +274,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -289,7 +289,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;

360
transport/lwm2m/src/main/data/credentials/shell/lwM2M_credentials.sh

@ -1,360 +0,0 @@
#!/bin/sh
#
# Copyright © 2016-2021 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.
#
#/home/nick/Igor_project/Thingsboard_Perfrmance_test/performance-tests/src/main/resources/credentials/shell/lwM2M_credentials.sh -p LwX509 -s 0 -f 2000 -a client_alias_ -e client_self_signed_ -b bootstrap -d server -j serverKeyStore.jks -k clientKeyStore.jks -c client_ks_password -w server_ks_password
#p) CLIENT_CN=$CLIENT_PREFIX00000000
#s) client_start=0
#f) client_finish=1
#a) CLIENT_ALIAS=CLIENT_ALIAS_PREFIX_00000000
#e) CLIENT_SELF_ALIAS=CLIENT_SELF_ALIAS_PREFIX_00000000
#b) BOOTSTRAP_ALIAS=bootstrap
#d) SERVER_ALIAS=server
#j) SERVER_STORE=serverKeyStore.jks
#k) CLIENT_STORE=clientKeyStore.jks
#c) CLIENT_STORE_PWD=client_ks_password
#w) SERVER_STORE_PWD=server_ks_password
#l) ROOT_KEY_ALIAS=root_key_alias
while getopts p:s:f:a:e:b:d:j:k:c:w:l: flag; do
case "${flag}" in
p) client_pref=${OPTARG} ;;
s) client_start=${OPTARG} ;;
f) client_finish=${OPTARG} ;;
a) client_alias_pref=${OPTARG} ;;
e) client_self_alias_pref=${OPTARG} ;;
b) bootstrap_alias=${OPTARG} ;;
d) server_alias=${OPTARG} ;;
j) key_store_server_file=${OPTARG} ;;
k) key_store_client_file=${OPTARG} ;;
c) client_key_store_pwd=${OPTARG} ;;
w) server_key_store_pwd=${OPTARG} ;;
w) root_key_alias=${OPTARG} ;;
esac
done
# cd to dir of script
script_dir=$(dirname $0)
echo "script_dir: $script_dir"
cd $script_dir
# source the properties:
. ./lwM2M_keygen.properties
if [ -n "$client_pref" ]; then
CLIENT_PREFIX=$client_pref
fi
if [ -z "$client_start" ]; then
client_start=0
fi
if [ -z "$client_finish" ]; then
client_finish=1
fi
if [ -n "$client_alias_pref" ]; then
CLIENT_ALIAS_PREFIX=$client_alias_pref
fi
if [ -n "$client_self_alias_pref" ]; then
CLIENT_SELF_ALIAS_PREFIX=$client_self_alias_pref
fi
if [ -n "$bootstrap_alias" ]; then
BOOTSTRAP_ALIAS=$bootstrap_alias
fi
if [ -n "$server_alias" ]; then
SERVER_ALIAS=$server_alias
fi
if [ -n "$key_store_server_file" ]; then
SERVER_STORE=$key_store_server_file
fi
if [ -n "$key_store_client_file" ]; then
CLIENT_STORE=$key_store_client_file
fi
if [ -n "$client_key_store_pwd" ]; then
CLIENT_STORE_PWD=$client_key_store_pwd
fi
if [ -n "$server_key_store_pwd" ]; then
SERVER_STORE_PWD=$server_key_store_pwd
fi
if [ -n "$root_key_alias" ]; then
ROOT_KEY_ALIAS=$root_key_alias
fi
CLIENT_NUMBER=$client_start
echo "==Start=="
echo "CLIENT_PREFIX: $CLIENT_PREFIX"
echo "client_start: $client_start"
echo "client_finish: $client_finish"
echo "CLIENT_ALIAS_PREFIX: $CLIENT_ALIAS_PREFIX"
echo "CLIENT_SELF_ALIAS_PREFIX: $CLIENT_SELF_ALIAS_PREFIX"
echo "BOOTSTRAP_ALIAS: $BOOTSTRAP_ALIAS"
echo "SERVER_ALIAS: $SERVER_ALIAS"
echo "SERVER_STORE: $SERVER_STORE"
echo "CLIENT_STORE: $CLIENT_STORE"
echo "CLIENT_STORE_PWD: $CLIENT_STORE_PWD"
echo "SERVER_STORE_PWD: $SERVER_STORE_PWD"
echo "CLIENT_NUMBER: $CLIENT_NUMBER"
echo "ROOT_KEY_ALIAS: $ROOT_KEY_ALIAS"
end_point() {
echo "$CLIENT_PREFIX$(printf "%08d" $CLIENT_NUMBER)"
}
client_alias_point() {
echo "$CLIENT_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)"
}
client_self_alias_point() {
echo "$CLIENT_SELF_ALIAS_PREFIX$(printf "%08d" $CLIENT_NUMBER)"
}
# Generation of the keystore.
echo "${H0}====START========${RESET}"
echo "${H1}Server Keystore : ${RESET}"
echo "${H1}==================${RESET}"
echo "${H2}Creating the trusted root CA key and certificate...${RESET}"
# -keysize
# 1024 (when using -genkeypair)
keytool \
-genkeypair \
-alias $ROOT_KEY_ALIAS \
-keyalg EC \
-dname "CN=$ROOT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-validity $VALIDITY \
-storetype $STORETYPE \
-keypass $SERVER_STORE_PWD \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD
echo
echo "${H2}Creating server key and self-signed certificate ...${RESET}"
keytool \
-genkeypair \
-alias $SERVER_ALIAS \
-keyalg EC \
-dname "CN=$SERVER_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-validity $VALIDITY \
-storetype $STORETYPE \
-keypass $SERVER_STORE_PWD \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD
keytool \
-exportcert \
-alias $SERVER_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD |
keytool \
-importcert \
-alias $SERVER_SELF_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD \
-noprompt
echo
echo "${H2}Creating server certificate signed by root CA...${RESET}"
keytool \
-certreq \
-alias $SERVER_ALIAS \
-dname "CN=$SERVER_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD |
keytool \
-gencert \
-alias $ROOT_KEY_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD \
-storetype $STORETYPE \
-validity $VALIDITY |
keytool \
-importcert \
-alias $SERVER_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD
echo
echo "${H2}Creating bootstrap key and self-signed certificate ...${RESET}"
keytool \
-genkeypair \
-alias $BOOTSTRAP_ALIAS \
-keyalg EC \
-dname "CN=$BOOTSTRAP_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-validity $VALIDITY \
-storetype $STORETYPE \
-keypass $SERVER_STORE_PWD \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD
keytool \
-exportcert \
-alias $BOOTSTRAP_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD |
keytool \
-importcert \
-alias $BOOTSTRAP_SELF_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD \
-noprompt
echo
echo "${H2}Creating bootstrap certificate signed by root CA...${RESET}"
keytool \
-certreq \
-alias $BOOTSTRAP_ALIAS \
-dname "CN=$BOOTSTRAP_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD |
keytool \
-gencert \
-alias $ROOT_KEY_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD \
-storetype $STORETYPE \
-validity $VALIDITY |
keytool \
-importcert \
-alias $BOOTSTRAP_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD
if [ "$client_start" -lt "$client_finish" ]; then
echo
echo "${H2}Import root certificate just to be able to import need by root CA with expected CN to $CLIENT_STORE${RESET}"
keytool \
-exportcert \
-alias $ROOT_KEY_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD |
keytool \
-importcert \
-alias $ROOT_KEY_ALIAS \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD \
-noprompt
fi
cert_end_point() {
echo
echo "${H1}Client Keystore : ${RESET}"
echo "${H1}==================${RESET}"
echo "${H2}Creating client key and self-signed certificate with expected CN CLIENT_ALIAS: $CLIENT_ALIAS${RESET}"
keytool \
-genkeypair \
-alias $CLIENT_ALIAS \
-keyalg EC \
-dname "CN=$CLIENT_SELF_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-validity $VALIDITY \
-storetype $STORETYPE \
-keypass $CLIENT_STORE_PWD \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD
keytool \
-exportcert \
-alias $CLIENT_ALIAS \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD |
keytool \
-importcert \
-alias $CLIENT_SELF_ALIAS \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD \
-noprompt
#
# echo
# echo "${H2}Import root certificate just to be able to import ned by root CA with expected CN...${RESET}"
# keytool \
# -exportcert \
# -alias $ROOT_KEY_ALIAS \
# -keystore $SERVER_STORE \
# -storepass $SERVER_STORE_PWD |
# keytool \
# -importcert \
# -alias $ROOT_KEY_ALIAS \
# -keystore $CLIENT_STORE \
# -storepass $CLIENT_STORE_PWD \
# -noprompt
#
echo
echo "${H2}Creating client certificate signed by root CA with expected CN CLIENT_ALIAS: $CLIENT_ALIAS CLIENT_CN: $CLIENT_CN${RESET}"
keytool \
-certreq \
-alias $CLIENT_ALIAS \
-dname "CN=$CLIENT_CN, OU=$ORGANIZATIONAL_UNIT, O=$ORGANIZATION, L=$CITY, ST=$STATE_OR_PROVINCE, C=$TWO_LETTER_COUNTRY_CODE" \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD |
keytool \
-gencert \
-alias $ROOT_KEY_ALIAS \
-keystore $SERVER_STORE \
-storepass $SERVER_STORE_PWD \
-storetype $STORETYPE \
-validity $VALIDITY |
keytool \
-importcert \
-alias $CLIENT_ALIAS \
-keystore $CLIENT_STORE \
-storepass $CLIENT_STORE_PWD \
-noprompt
}
if [ "$client_start" -lt "$client_finish" ]; then
echo
echo "==Start Client=="
while [ "$CLIENT_NUMBER" -lt "$client_finish" ]; do
echo "number $CLIENT_NUMBER"
echo "finish $client_finish"
CLIENT_CN=$(end_point)
CLIENT_ALIAS=$(client_alias_point)
CLIENT_SELF_ALIAS=$(client_self_alias_point)
echo "CLIENT_CN $CLIENT_CN"
echo "CLIENT_ALIAS $CLIENT_ALIAS"
echo "CLIENT_SELF_ALIAS $CLIENT_SELF_ALIAS"
cert_end_point
CLIENT_NUMBER=$(($CLIENT_NUMBER + 1))
echo
done
fi
echo
echo "${H0}!!! Warning ${H2}Migrate ${H1}${SERVER_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}"
keytool \
-importkeystore \
-srckeystore $SERVER_STORE \
-destkeystore $SERVER_STORE \
-deststoretype pkcs12 \
-srcstorepass $SERVER_STORE_PWD
if [ "$client_start" -lt "$client_finish" ]; then
echo
echo "${H0}!!! Warning ${H2}Migrate ${H1}${CLIENT_STORE} ${H2}to ${H1}PKCS12 ${H2}which is an industry standard format..${RESET}"
keytool \
-importkeystore \
-srckeystore $CLIENT_STORE \
-destkeystore $CLIENT_STORE \
-deststoretype pkcs12 \
-srcstorepass $CLIENT_STORE_PWD
fi

57
transport/lwm2m/src/main/data/credentials/shell/lwM2M_keygen.properties

@ -1,57 +0,0 @@
#
# Copyright © 2016-2017 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.
#
# Keystore common parameters
ROOT_KEY_ALIAS=rootCA
DOMAIN_SUFFIX="$(hostname)"
ROOT_CN="$DOMAIN_SUFFIX $ROOT_KEY_ALIAS"
ORGANIZATIONAL_UNIT=Thingsboard
ORGANIZATION=Thingsboard
CITY=SF
STATE_OR_PROVINCE=CA
TWO_LETTER_COUNTRY_CODE=US
VALIDITY=36500 #days
STORETYPE="JKS"
#Server
SERVER_STORE=serverKeyStore1.jks
SERVER_STORE_PWD=server_ks_password1
SERVER_ALIAS=server1
SERVER_CN="$DOMAIN_SUFFIX server LwM2M signed by root CA"
SERVER_SELF_ALIAS=server_self_signed
SERVER_SELF_CN="$DOMAIN_SUFFIX server LwM2M self-signed"
BOOTSTRAP_ALIAS=bootstrap1
BOOTSTRAP_CN="$DOMAIN_SUFFIX bootstrap server LwM2M signed by root CA"
BOOTSTRAP_SELF_ALIAS=bootstrap_self_signed
BOOTSTRAP_SELF_CN="$DOMAIN_SUFFIX bootstrap server LwM2M self-signed"
# Client
CLIENT_STORE=clientKeyStore1.jks
CLIENT_STORE_PWD=client_ks_password1
CLIENT_ALIAS_PREFIX=client_alias_1
CLIENT_PREFIX=LwX509___
CLIENT_SELF_ALIAS_PREFIX=client_self_signed_1
CLIENT_SELF_CN="$DOMAIN_SUFFIX client LwM2M self-signed"
# Color output stuff
red=`tput setaf 1`
green=`tput setaf 2`
blue=`tput setaf 4`
bold=`tput bold`
H0=${red}${bold}
H1=${green}${bold}
H2=${blue}
RESET=`tput sgr0`

BIN
transport/lwm2m/src/main/data/lwm2mserver.jks

Binary file not shown.

16
transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml

@ -114,7 +114,7 @@ transport:
# Server X509 Certificates support
credentials:
# Whether to enable LWM2M server X509 Certificate/RPK support
enabled: "${LWM2M_SERVER_CREDENTIALS_ENABLED:true}"
enabled: "${LWM2M_SERVER_CREDENTIALS_ENABLED:false}"
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${LWM2M_SERVER_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
@ -150,7 +150,7 @@ transport:
# Bootstrap server X509 Certificates support
credentials:
# Whether to enable LWM2M bootstrap server X509 Certificate/RPK support
enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:true}"
enabled: "${LWM2M_BS_CREDENTIALS_ENABLED:false}"
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${LWM2M_BS_CREDENTIALS_TYPE:PEM}"
# PEM server credentials
@ -177,19 +177,19 @@ transport:
# X509 trust certificates
trust-credentials:
# Whether to load X509 trust certificates
enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:true}"
enabled: "${LWM2M_TRUST_CREDENTIALS_ENABLED:false}"
# Trust certificates store type (PEM - pem certificates file; KEYSTORE - java keystore)
type: "${LWM2M_TRUST_CREDENTIALS_TYPE:PEM}"
# PEM certificates
pem:
# Path to the certificates file (holds trust certificates)
cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mserver.pem}"
cert_file: "${LWM2M_TRUST_PEM_CERT:lwm2mtruststorechain.pem}"
# Keystore with trust certificates
keystore:
# Type of the key store
type: "${LWM2M_TRUST_KEY_STORE_TYPE:JKS}"
# Path to the key store that holds the X509 certificates
store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mserver.jks}"
store_file: "${LWM2M_TRUST_KEY_STORE:lwm2mtruststorechain.jks}"
# Password used to access the key store
store_password: "${LWM2M_TRUST_KEY_STORE_PASSWORD:server_ks_password}"
recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}"
@ -340,7 +340,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -355,7 +355,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -370,7 +370,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;

6
transport/mqtt/src/main/resources/tb-mqtt-transport.yml

@ -289,7 +289,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -304,7 +304,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -319,7 +319,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;

6
transport/snmp/src/main/resources/tb-snmp-transport.yml

@ -239,7 +239,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_MAIN_SUBMIT_STRATEGY_BATCH_SIZE:1000}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_TYPE:SKIP_ALL_FAILURES}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_MAIN_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -254,7 +254,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_HP_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_RETRIES:0}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_HP_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;
@ -269,7 +269,7 @@ queue:
# For BATCH only
batch-size: "${TB_QUEUE_RE_SQ_SUBMIT_STRATEGY_BATCH_SIZE:100}" # Maximum number of messages in batch
processing-strategy:
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
type: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_TYPE:RETRY_FAILED_AND_TIMED_OUT}" # SKIP_ALL_FAILURES, SKIP_ALL_FAILURES_AND_TIMED_OUT, RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
# For RETRY_ALL, RETRY_FAILED, RETRY_TIMED_OUT, RETRY_FAILED_AND_TIMED_OUT
retries: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_RETRIES:3}" # Number of retries, 0 is unlimited
failure-percentage: "${TB_QUEUE_RE_SQ_PROCESSING_STRATEGY_FAILURE_PERCENTAGE:0}" # Skip retry if failures or timeouts are less then X percentage of messages;

5
ui-ngx/angular.json

@ -63,11 +63,6 @@
"glob": "marker-shadow.png",
"input": "node_modules/leaflet/dist/images/",
"output": "/"
},
{
"glob": "**/*",
"input": "node_modules/material-design-icons/iconfont/",
"output": "assets/fonts"
}
],
"styles": [

3
ui-ngx/package.json

@ -28,7 +28,7 @@
"@date-io/date-fns": "^2.11.0",
"@flowjs/flow.js": "^2.14.1",
"@flowjs/ngx-flow": "~0.4.6",
"@geoman-io/leaflet-geoman-free": "^2.11.3",
"@geoman-io/leaflet-geoman-free": "^2.11.4",
"@juggle/resize-observer": "^3.3.1",
"@mat-datetimepicker/core": "~7.0.1",
"@material-ui/core": "^4.12.3",
@ -61,7 +61,6 @@
"leaflet-providers": "^1.13.0",
"leaflet.gridlayer.googlemutant": "^0.13.4",
"leaflet.markercluster": "^1.5.3",
"material-design-icons": "^3.0.1",
"messageformat": "^2.3.0",
"moment": "^2.29.1",
"moment-timezone": "^0.5.34",

11
ui-ngx/src/app/core/api/widget-api.models.ts

@ -55,6 +55,7 @@ import { TranslateService } from '@ngx-translate/core';
import { AlarmDataService } from '@core/api/alarm-data.service';
import { IDashboardController } from '@home/components/dashboard-page/dashboard-page.models';
import { PopoverPlacement } from '@shared/components/popover.models';
import { PersistentRpc } from '@shared/models/rpc.models';
export interface TimewindowFunctions {
onUpdateTimewindow: (startTimeMs: number, endTimeMs: number, interval?: number) => void;
@ -71,9 +72,9 @@ export interface WidgetSubscriptionApi {
export interface RpcApi {
sendOneWayCommand: (method: string, params?: any, timeout?: number, persistent?: boolean,
persistentPollingInterval?: number, requestUUID?: string) => Observable<any>;
persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string) => Observable<any>;
sendTwoWayCommand: (method: string, params?: any, timeout?: number, persistent?: boolean,
persistentPollingInterval?: number, requestUUID?: string) => Observable<any>;
persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string) => Observable<any>;
completedCommand: () => void;
}
@ -288,6 +289,8 @@ export interface IWidgetSubscription {
comparisonEnabled?: boolean;
comparisonTimeWindow?: WidgetTimewindow;
persistentRequests?: PageData<PersistentRpc>;
alarms?: PageData<AlarmData>;
alarmSource?: Datasource;
@ -314,9 +317,9 @@ export interface IWidgetSubscription {
updateTimewindowConfig(newTimewindow: Timewindow): void;
sendOneWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean,
persistentPollingInterval?: number, requestUUID?: string): Observable<any>;
persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string): Observable<any>;
sendTwoWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean,
persistentPollingInterval?: number, requestUUID?: string): Observable<any>;
persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string): Observable<any>;
clearRpcError(): void;
subscribe(): void;

13
ui-ngx/src/app/core/api/widget-subscription.ts

@ -656,13 +656,13 @@ export class WidgetSubscription implements IWidgetSubscription {
}
sendOneWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean,
persistentPollingInterval?: number, requestUUID?: string): Observable<any> {
return this.sendCommand(true, method, params, timeout, persistent, persistentPollingInterval, requestUUID);
persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string): Observable<any> {
return this.sendCommand(true, method, params, timeout, persistent, persistentPollingInterval, retries, additionalInfo, requestUUID);
}
sendTwoWayCommand(method: string, params?: any, timeout?: number, persistent?: boolean,
persistentPollingInterval?: number, requestUUID?: string): Observable<any> {
return this.sendCommand(false, method, params, timeout, persistent, persistentPollingInterval, requestUUID);
persistentPollingInterval?: number, retries?: number, additionalInfo?: any, requestUUID?: string): Observable<any> {
return this.sendCommand(false, method, params, timeout, persistent, persistentPollingInterval, retries, additionalInfo, requestUUID);
}
clearRpcError(): void {
@ -679,7 +679,8 @@ export class WidgetSubscription implements IWidgetSubscription {
}
sendCommand(oneWayElseTwoWay: boolean, method: string, params?: any, timeout?: number,
persistent?: boolean, persistentPollingInterval?: number, requestUUID?: string): Observable<any> {
persistent?: boolean, persistentPollingInterval?: number, retries?: number,
additionalInfo?: any, requestUUID?: string): Observable<any> {
if (!this.rpcEnabled) {
return throwError(new Error('Rpc disabled!'));
} else {
@ -692,6 +693,8 @@ export class WidgetSubscription implements IWidgetSubscription {
method,
params,
persistent,
retries,
additionalInfo,
requestUUID
};
if (timeout && timeout > 0) {

15
ui-ngx/src/app/core/http/device.service.ts

@ -31,7 +31,7 @@ import {
import { EntitySubtype } from '@app/shared/models/entity-type.models';
import { AuthService } from '@core/auth/auth.service';
import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models';
import { PersistentRpc } from '@shared/models/rpc.models';
import { PersistentRpc, RpcStatus } from '@shared/models/rpc.models';
@Injectable({
providedIn: 'root'
@ -143,6 +143,19 @@ export class DeviceService {
return this.http.get<PersistentRpc>(`/api/rpc/persistent/${rpcId}`, defaultHttpOptionsFromConfig(config));
}
public deletePersistedRpc(rpcId: string, config?: RequestConfig) {
return this.http.delete<PersistentRpc>(`/api/rpc/persistent/${rpcId}`, defaultHttpOptionsFromConfig(config));
}
public getPersistedRpcRequests(deviceId: string, pageLink: PageLink,
rpcStatus?: RpcStatus, config?: RequestConfig): Observable<PageData<PersistentRpc>> {
let url = `/api/rpc/persistent/device/${deviceId}${pageLink.toQuery()}`;
if (rpcStatus && rpcStatus.length) {
url += `&rpcStatus=${rpcStatus}`;
}
return this.http.get<PageData<PersistentRpc>>(url, defaultHttpOptionsFromConfig(config));
}
public findByQuery(query: DeviceSearchQuery,
config?: RequestConfig): Observable<Array<Device>> {
return this.http.post<Array<Device>>('/api/devices', query, defaultHttpOptionsFromConfig(config));

2142
ui-ngx/src/app/core/services/material-icons-codepoints.raw

File diff suppressed because it is too large

2
ui-ngx/src/app/core/services/utils.service.ts

@ -41,7 +41,7 @@ import { alarmFields } from '@shared/models/alarm.models';
import { materialColors } from '@app/shared/models/material.models';
import { WidgetInfo } from '@home/models/widget-component.models';
import jsonSchemaDefaults from 'json-schema-defaults';
import materialIconsCodepoints from '!raw-loader!material-design-icons/iconfont/codepoints';
import materialIconsCodepoints from '!raw-loader!./material-icons-codepoints.raw';
import { Observable, of, ReplaySubject } from 'rxjs';
const i18nRegExp = new RegExp(`{${i18nPrefix}:[^{}]+}`, 'g');

5
ui-ngx/src/app/core/utils.ts

@ -20,6 +20,7 @@ import { finalize, share } from 'rxjs/operators';
import { Datasource } from '@app/shared/models/widget.models';
import { EntityId } from '@shared/models/id/entity-id';
import { NULL_UUID } from '@shared/models/id/has-uuid';
import { EntityType, baseDetailsPageByEntityType } from '@shared/models/entity-type.models';
const varsRegex = /\${([^}]*)}/g;
@ -460,3 +461,7 @@ export function randomAlphanumeric(length: number): string {
}
return result;
}
export function getEntityDetailsPageURL(id: string, entityType: EntityType): string {
return `${baseDetailsPageByEntityType.get(entityType)}/${id}`;
}

2
ui-ngx/src/app/modules/common/modules-map.ts

@ -139,6 +139,7 @@ import * as QueueTypeListComponent from '@shared/components/queue/queue-type-lis
import * as RelationTypeAutocompleteComponent from '@shared/components/relation/relation-type-autocomplete.component';
import * as SocialSharePanelComponent from '@shared/components/socialshare-panel.component';
import * as JsonObjectEditComponent from '@shared/components/json-object-edit.component';
import * as JsonObjectViewComponent from '@shared/components/json-object-view.component';
import * as JsonContentComponent from '@shared/components/json-content.component';
import * as JsFuncComponent from '@shared/components/js-func.component';
import * as FabToolbarComponent from '@shared/components/fab-toolbar.component';
@ -420,6 +421,7 @@ class ModulesMap implements IModulesMap {
'@shared/components/relation/relation-type-autocomplete.component': RelationTypeAutocompleteComponent,
'@shared/components/socialshare-panel.component': SocialSharePanelComponent,
'@shared/components/json-object-edit.component': JsonObjectEditComponent,
'@shared/components/json-object-view.component': JsonObjectViewComponent,
'@shared/components/json-content.component': JsonContentComponent,
'@shared/components/js-func.component': JsFuncComponent,
'@shared/components/fab-toolbar.component': FabToolbarComponent,

1
ui-ngx/src/app/modules/home/components/alarm/alarm-table-config.ts

@ -60,6 +60,7 @@ export class AlarmTableConfig extends EntityTableConfig<AlarmInfo, TimePageLink>
this.loadDataOnInit = false;
this.tableTitle = '';
this.useTimePageLink = true;
this.pageMode = false;
this.defaultTimewindowInterval = historyInterval(DAY * 30);
this.detailsPanelEnabled = false;
this.selectionEnabled = false;

1
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html

@ -196,6 +196,7 @@
[pageIndex]="pageLink.page"
[pageSize]="pageLink.pageSize"
[pageSizeOptions]="[10, 20, 30]"
[hidePageSize]="hidePageSize"
showFirstLastButtons></mat-paginator>
<ngx-hm-carousel fxFlex *ngIf="mode === 'widget' && widgetsList.length > 0"
#carousel

1
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.scss

@ -18,6 +18,7 @@
:host {
width: 100%;
height: 100%;
display: block;
.tb-entity-table {
.tb-entity-table-content {
width: 100%;

25
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts

@ -84,6 +84,8 @@ import {
} from '@home/components/attribute/add-widget-to-dashboard-dialog.component';
import { deepClone } from '@core/utils';
import { Filters } from '@shared/models/query/query.models';
import { hidePageSizePixelValue } from '@shared/models/constants';
import { ResizeObserver } from '@juggle/resize-observer';
@Component({
@ -109,6 +111,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
pageLink: PageLink;
textSearchMode = false;
dataSource: AttributeDatasource;
hidePageSize = false;
activeValue = false;
dirtyValue = false;
@ -127,10 +130,14 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
aliasController: IAliasController;
private widgetDatasource: Datasource;
private widgetResize$: ResizeObserver;
private disableAttributeScopeSelectionValue: boolean;
get disableAttributeScopeSelection(): boolean {
return this.disableAttributeScopeSelectionValue;
}
@Input()
set disableAttributeScopeSelection(value: boolean) {
this.disableAttributeScopeSelectionValue = coerceBooleanProperty(value);
@ -184,7 +191,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
private dashboardUtils: DashboardUtilsService,
private widgetService: WidgetService,
private zone: NgZone,
private cd: ChangeDetectorRef) {
private cd: ChangeDetectorRef,
private elementRef: ElementRef) {
super(store);
this.dirtyValue = !this.activeValue;
const sortOrder: SortOrder = { property: 'key', direction: Direction.ASC };
@ -193,6 +201,20 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
}
ngOnInit() {
this.widgetResize$ = new ResizeObserver(() => {
const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue;
if (showHidePageSize !== this.hidePageSize) {
this.hidePageSize = showHidePageSize;
this.cd.markForCheck();
}
});
this.widgetResize$.observe(this.elementRef.nativeElement);
}
ngOnDestroy() {
if (this.widgetResize$) {
this.widgetResize$.disconnect();
}
}
attributeScopeChanged(attributeScope: TelemetryType) {
@ -263,6 +285,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
this.attributeScopeSelectionReadonly = true;
}
this.mode = 'default';
this.selectedWidgetsBundleAlias = null;
this.attributeScope = this.defaultAttributeScope;
this.pageLink.textSearch = null;
if (this.viewsInited) {

4
ui-ngx/src/app/modules/home/components/audit-log/audit-log-table-config.ts

@ -52,11 +52,13 @@ export class AuditLogTableConfig extends EntityTableConfig<AuditLog, TimePageLin
public entityId: EntityId = null,
public userId: UserId = null,
public customerId: CustomerId = null,
updateOnInit = true) {
updateOnInit = true,
pageMode = false) {
super();
this.loadDataOnInit = updateOnInit;
this.tableTitle = '';
this.useTimePageLink = true;
this.pageMode = pageMode;
this.detailsPanelEnabled = false;
this.selectionEnabled = false;
this.searchEnabled = true;

8
ui-ngx/src/app/modules/home/components/audit-log/audit-log-table.component.ts

@ -29,6 +29,7 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { Authority } from '@shared/models/authority.enum';
import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'tb-audit-log-table',
@ -104,7 +105,8 @@ export class AuditLogTableComponent implements OnInit {
private translate: TranslateService,
private datePipe: DatePipe,
private dialog: MatDialog,
private store: Store<AppState>) {
private store: Store<AppState>,
private route: ActivatedRoute) {
}
ngOnInit() {
@ -117,6 +119,7 @@ export class AuditLogTableComponent implements OnInit {
}
updateOnInit = true;
}
const pageMode = !!this.route.snapshot.data.isPage;
this.auditLogTableConfig = new AuditLogTableConfig(
this.auditLogService,
this.translate,
@ -126,7 +129,8 @@ export class AuditLogTableComponent implements OnInit {
this.entityIdValue,
this.userIdValue,
this.customerIdValue,
updateOnInit
updateOnInit,
pageMode
);
}

1
ui-ngx/src/app/modules/home/components/entity/entities-table.component.html

@ -266,6 +266,7 @@
[pageIndex]="pageLink.page"
[pageSize]="pageLink.pageSize"
[pageSizeOptions]="pageSizeOptions"
[hidePageSize]="hidePageSize"
showFirstLastButtons></mat-paginator>
</div>
</div>

1
ui-ngx/src/app/modules/home/components/entity/entities-table.component.scss

@ -18,6 +18,7 @@
:host {
width: 100%;
height: 100%;
display: block;
.tb-entity-table {
.tb-entity-table-content {
width: 100%;

143
ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts

@ -31,19 +31,20 @@ import {
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { MAX_SAFE_PAGE_SIZE, PageLink, TimePageLink } from '@shared/models/page/page-link';
import { MAX_SAFE_PAGE_SIZE, PageLink, PageQueryParam, TimePageLink } from '@shared/models/page/page-link';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatSort, SortDirection } from '@angular/material/sort';
import { EntitiesDataSource } from '@home/models/datasource/entity-datasource';
import { catchError, debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators';
import { catchError, debounceTime, distinctUntilChanged, map, skip, tap } from 'rxjs/operators';
import { Direction, SortOrder } from '@shared/models/page/sort-order';
import { forkJoin, fromEvent, merge, Observable, of, Subscription } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import { BaseData, HasId } from '@shared/models/base-data';
import { ActivatedRoute } from '@angular/router';
import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router';
import {
CellActionDescriptor, CellActionDescriptorType,
CellActionDescriptor,
CellActionDescriptorType,
EntityActionTableColumn,
EntityColumn,
EntityTableColumn,
@ -55,15 +56,13 @@ import { EntityTypeTranslation } from '@shared/models/entity-type.models';
import { DialogService } from '@core/services/dialog.service';
import { AddEntityDialogComponent } from './add-entity-dialog.component';
import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models';
import {
calculateIntervalStartEndTime,
HistoryWindowType,
Timewindow
} from '@shared/models/time/time.models';
import { calculateIntervalStartEndTime, HistoryWindowType, Timewindow } from '@shared/models/time/time.models';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TbAnchorComponent } from '@shared/components/tb-anchor.component';
import { isDefined, isUndefined } from '@core/utils';
import { isDefined, isEmptyStr, isUndefined } from '@core/utils';
import { HasUUID } from '@shared/models/id/has-uuid';
import { ResizeObserver } from '@juggle/resize-observer';
import { hidePageSizePixelValue } from '@shared/models/constants';
import { IEntitiesTableComponent } from '@home/models/entity/entity-table-component.models';
@Component({
@ -99,8 +98,10 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
defaultPageSize = 10;
displayPagination = true;
hidePageSize = false;
pageSizeOptions;
pageLink: PageLink;
pageMode = true;
textSearchMode = false;
timewindow: Timewindow;
dataSource: EntitiesDataSource<BaseData<HasId>>;
@ -117,10 +118,11 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
private sortSubscription: Subscription;
private updateDataSubscription: Subscription;
private viewInited = false;
private widgetResize$: ResizeObserver;
constructor(protected store: Store<AppState>,
public route: ActivatedRoute,
public translate: TranslateService,
@ -128,7 +130,9 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
private dialogService: DialogService,
private domSanitizer: DomSanitizer,
private cd: ChangeDetectorRef,
private componentFactoryResolver: ComponentFactoryResolver) {
private router: Router,
private componentFactoryResolver: ComponentFactoryResolver,
private elementRef: ElementRef) {
super(store);
}
@ -138,6 +142,20 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
} else {
this.init(this.route.snapshot.data.entitiesTableConfig);
}
this.widgetResize$ = new ResizeObserver(() => {
const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue;
if (showHidePageSize !== this.hidePageSize) {
this.hidePageSize = showHidePageSize;
this.cd.markForCheck();
}
});
this.widgetResize$.observe(this.elementRef.nativeElement);
}
ngOnDestroy() {
if (this.widgetResize$) {
this.widgetResize$.disconnect();
}
}
ngOnChanges(changes: SimpleChanges): void {
@ -196,16 +214,19 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
this.columnsUpdated();
const routerQueryParams: PageQueryParam = this.route.snapshot.queryParams;
let sortOrder: SortOrder = null;
if (this.entitiesTableConfig.defaultSortOrder) {
if (this.entitiesTableConfig.defaultSortOrder || routerQueryParams.hasOwnProperty('direction') || routerQueryParams.hasOwnProperty('property')) {
sortOrder = {
property: this.entitiesTableConfig.defaultSortOrder.property,
direction: this.entitiesTableConfig.defaultSortOrder.direction
property: routerQueryParams?.property || this.entitiesTableConfig.defaultSortOrder.property,
direction: routerQueryParams?.direction || this.entitiesTableConfig.defaultSortOrder.direction
};
}
this.displayPagination = this.entitiesTableConfig.displayPagination;
this.defaultPageSize = this.entitiesTableConfig.defaultPageSize;
this.pageMode = this.entitiesTableConfig.pageMode;
this.pageSizeOptions = [this.defaultPageSize, this.defaultPageSize * 2, this.defaultPageSize * 3];
if (this.entitiesTableConfig.useTimePageLink) {
@ -217,6 +238,16 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
this.pageLink = new PageLink(10, 0, null, sortOrder);
}
this.pageLink.pageSize = this.displayPagination ? this.defaultPageSize : MAX_SAFE_PAGE_SIZE;
if (routerQueryParams.hasOwnProperty('page')) {
this.pageLink.page = Number(routerQueryParams.page);
}
if (routerQueryParams.hasOwnProperty('pageSize')) {
this.pageLink.pageSize = Number(routerQueryParams.pageSize);
}
if (routerQueryParams.hasOwnProperty('textSearch') && !isEmptyStr(routerQueryParams.textSearch)) {
this.textSearchMode = true;
this.pageLink.textSearch = decodeURI(routerQueryParams.textSearch);
}
this.dataSource = this.entitiesTableConfig.dataSource(this.dataLoaded.bind(this));
if (this.entitiesTableConfig.onLoadAction) {
this.entitiesTableConfig.onLoadAction(this.route);
@ -238,36 +269,73 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
debounceTime(150),
distinctUntilChanged(),
tap(() => {
const queryParams: PageQueryParam = {
textSearch: encodeURI(this.pageLink.textSearch) || null
};
if (this.displayPagination) {
this.paginator.pageIndex = 0;
queryParams.page = null;
}
this.updateData();
this.updatedRouterParamsAndData(queryParams);
})
)
.subscribe();
this.route.queryParams.pipe(skip(1)).subscribe((params: PageQueryParam) => {
this.paginator.pageIndex = Number(params.page) || 0;
this.paginator.pageSize = Number(params.pageSize) || this.defaultPageSize;
this.sort.active = params.property || this.entitiesTableConfig.defaultSortOrder.property;
this.sort.direction = (params.direction || this.entitiesTableConfig.defaultSortOrder.direction).toLowerCase() as SortDirection;
if (params.hasOwnProperty('textSearch') && !isEmptyStr(params.textSearch)) {
this.textSearchMode = true;
this.pageLink.textSearch = decodeURI(params.textSearch);
} else {
this.textSearchMode = false;
this.pageLink.textSearch = null;
}
this.updateData();
});
this.updatePaginationSubscriptions();
this.viewInited = true;
}
private updatePaginationSubscriptions() {
if (this.sortSubscription) {
this.sortSubscription.unsubscribe();
this.sortSubscription = null;
}
if (this.updateDataSubscription) {
this.updateDataSubscription.unsubscribe();
this.updateDataSubscription = null;
}
let paginatorSubscription$: Observable<object>;
const sortSubscription$: Observable<object> = this.sort.sortChange.asObservable().pipe(
map((data) => {
const direction = data.direction.toUpperCase();
const queryParams: PageQueryParam = {
direction: (this.entitiesTableConfig?.defaultSortOrder?.direction === direction ? null : direction) as Direction,
property: this.entitiesTableConfig?.defaultSortOrder?.property === data.active ? null : data.active
};
if (this.displayPagination) {
queryParams.page = null;
this.paginator.pageIndex = 0;
}
return queryParams;
})
);
if (this.displayPagination) {
this.sortSubscription = this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);
paginatorSubscription$ = this.paginator.page.asObservable().pipe(
map((data) => {
return {
page: data.pageIndex === 0 ? null : data.pageIndex,
pageSize: data.pageSize === this.defaultPageSize ? null : data.pageSize
};
})
);
}
this.updateDataSubscription = ((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page)
: this.sort.sortChange) as Observable<any>)
.pipe(
tap(() => this.updateData())
)
.subscribe();
this.updateDataSubscription = ((this.displayPagination ? merge(sortSubscription$, paginatorSubscription$)
: sortSubscription$) as Observable<PageQueryParam>).pipe(
tap((queryParams) => {
this.updatedRouterParamsAndData(queryParams);
})
).subscribe();
}
addEnabled() {
@ -451,10 +519,14 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
exitFilterMode() {
this.textSearchMode = false;
this.pageLink.textSearch = null;
const queryParams: PageQueryParam = {
textSearch: null
};
if (this.displayPagination) {
this.paginator.pageIndex = 0;
queryParams.page = null;
}
this.updateData();
this.updatedRouterParamsAndData(queryParams);
}
resetSortAndFilter(update: boolean = true, preserveTimewindow: boolean = false) {
@ -469,7 +541,7 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
this.sort.active = sortable.id;
this.sort.direction = this.entitiesTableConfig.defaultSortOrder.direction === Direction.ASC ? 'asc' : 'desc';
if (update) {
this.updateData();
this.updatedRouterParamsAndData({}, 'preserve');
}
}
@ -587,4 +659,15 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
return entity.id.id;
}
protected updatedRouterParamsAndData(queryParams: object, queryParamsHandling: QueryParamsHandling = 'merge') {
if (this.pageMode) {
this.router.navigate([], {
relativeTo: this.route,
queryParams,
queryParamsHandling
});
} else {
this.updateData();
}
}
}

57
ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.html

@ -0,0 +1,57 @@
<!--
Copyright © 2016-2021 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.
-->
<mat-card class="settings-card">
<mat-toolbar class="details-toolbar">
<div class="mat-toolbar-tools" fxLayout="row" fxLayoutAlign="start center">
<div class="tb-details-title-header" fxLayout="column" fxLayoutAlign="center start">
<div class="tb-details-title tb-ellipsis">{{ headerTitle }}</div>
<div class="tb-details-subtitle tb-ellipsis">{{ headerSubtitle }}</div>
</div>
<div class="tb-help" [tb-help]="helpLinkId()"></div>
<span fxFlex></span>
<section *ngIf="!isReadOnly" fxLayout="row" class="tb-header-button" fxLayoutGap="8px">
<button [disabled]="(isLoading$ | async) || detailsForm.invalid || !detailsForm.dirty"
mat-fab
matTooltip="{{ 'action.apply-changes' | translate }}"
matTooltipPosition="above"
color="accent" class="tb-btn-header"
[ngClass]="{'tb-hide': !isEdit}"
(click)="onApplyDetails()">
<mat-icon class="material-icons">done</mat-icon>
</button>
<button [disabled]="(isLoading$ | async)"
mat-fab
matTooltip="{{ 'action.decline-changes' | translate }}"
matTooltipPosition="above"
color="accent" class="tb-btn-header"
(click)="onToggleDetailsEditMode()">
<mat-icon class="material-icons">{{isEdit ? 'close' : 'edit'}}</mat-icon>
</button>
</section>
</div>
</mat-toolbar>
<mat-card-content fxFlex="100">
<mat-tab-group class="tb-absolute-fill" [ngClass]="{'tb-headless': hideDetailsTabs()}" [(selectedIndex)]="selectedTab" fxFill>
<mat-tab label="{{ 'details.details' | translate }}">
<tb-anchor #entityDetailsForm></tb-anchor>
</mat-tab>
<tb-anchor #entityTabs></tb-anchor>
</mat-tab-group>
</mat-card-content>
</mat-card>

119
ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.scss

@ -0,0 +1,119 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@import "../../../../../scss/constants";
:host {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
.settings-card {
margin: 8px;
padding: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.details-toolbar {
height: 84px;
min-height: 84px;
border-radius: 4px 4px 0 0;
background: #fff;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
.mat-toolbar-tools {
padding: 0 8px;
}
.tb-details-title-header {
min-width: 0;
width: auto;
}
.tb-details-title {
font-size: 1rem;
font-weight: 500;
@media #{$mat-gt-sm} {
font-size: 1.2rem;
}
}
.tb-details-subtitle {
font-size: 0.9rem;
opacity: .8;
}
.tb-ellipsis {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
@media #{$mat-md} {
width: 80%;
}
@media #{$mat-gt-md} {
width: 60%;
}
.tb-header-button {
.tb-btn-header {
position: relative !important;
display: inline-block !important;
animation: tbMoveFromTopFade .3s ease both;
&.tb-hide {
animation: tbMoveToTopFade .3s ease both;
}
}
}
}
}
:host ::ng-deep {
.tb-help {
.mat-icon-button.mat-primary {
color: rgba(0, 0, 0, 0.52);
}
}
.mat-card-content {
position: relative;
overflow: hidden;
> .mat-tab-group {
> .mat-tab-body-wrapper {
position: absolute;
top: 49px;
left: 0;
right: 0;
bottom: 0;
}
> .mat-tab-header {
.mat-tab-label {
min-width: 40px;
}
}
}
}
}

182
ui-ngx/src/app/modules/home/components/entity/entity-details-page.component.ts

@ -0,0 +1,182 @@
///
/// Copyright © 2016-2021 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ComponentFactoryResolver,
HostBinding,
Injector,
OnDestroy,
OnInit
} from '@angular/core';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { BaseData, HasId } from '@shared/models/base-data';
import { ActivatedRoute, Router } from '@angular/router';
import { FormGroup } from '@angular/forms';
import { AssetId } from '@shared/models/id/asset-id';
import { TranslateService } from '@ngx-translate/core';
import { deepClone, mergeDeep } from '@core/utils';
import { BroadcastService } from '@core/services/broadcast.service';
import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component';
import { DialogService } from '@core/services/dialog.service';
@Component({
selector: 'tb-entity-details-page',
templateUrl: './entity-details-page.component.html',
styleUrls: ['./entity-details-page.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EntityDetailsPageComponent extends EntityDetailsPanelComponent implements OnInit, OnDestroy {
headerTitle: string;
headerSubtitle: string;
isReadOnly = false;
set entitiesTableConfig(entitiesTableConfig: EntityTableConfig<BaseData<HasId>>) {
if (this.entitiesTableConfigValue !== entitiesTableConfig) {
this.entitiesTableConfigValue = entitiesTableConfig;
if (this.entitiesTableConfigValue) {
this.isEdit = false;
this.entity = null;
}
}
}
get entitiesTableConfig(): EntityTableConfig<BaseData<HasId>> {
return this.entitiesTableConfigValue;
}
@HostBinding('class') 'tb-absolute-fill';
constructor(private route: ActivatedRoute,
private router: Router,
protected injector: Injector,
protected cd: ChangeDetectorRef,
protected componentFactoryResolver: ComponentFactoryResolver,
private broadcast: BroadcastService,
private translate: TranslateService,
private dialogService: DialogService,
protected store: Store<AppState>) {
super(store, injector, cd, componentFactoryResolver);
this.entitiesTableConfig = this.route.snapshot.data.entitiesTableConfig;
}
ngOnInit() {
this.headerSubtitle = '';
this.route.paramMap.subscribe( paramMap => {
this.entityId = new AssetId(paramMap.get('entityId'));
});
this.headerSubtitle = this.translate.instant(this.entitiesTableConfig.entityTranslations.details);
super.init();
this.entityComponent.isDetailsPage = true;
this.subscriptions.push(this.entityAction.subscribe((action) => {
if (action.action === 'delete') {
this.deleteEntity(action.event, action.entity);
}
}));
}
ngOnDestroy() {
super.ngOnDestroy();
}
reload(): void {
this.isEdit = false;
this.entitiesTableConfig.loadEntity(this.currentEntityId).subscribe(
(entity) => {
this.entity = entity;
this.broadcast.broadcast('updateBreadcrumb');
this.isReadOnly = this.entitiesTableConfig.detailsReadonly(entity);
this.headerTitle = this.entitiesTableConfig.entityTitle(entity);
this.entityComponent.entity = entity;
this.entityComponent.isEdit = false;
if (this.entityTabsComponent) {
this.entityTabsComponent.entity = entity;
}
}
);
}
onToggleDetailsEditMode() {
if (this.isEdit) {
this.entityComponent.entity = this.entity;
if (this.entityTabsComponent) {
this.entityTabsComponent.entity = this.entity;
}
this.isEdit = !this.isEdit;
} else {
this.isEdit = !this.isEdit;
this.editingEntity = deepClone(this.entity);
this.entityComponent.entity = this.editingEntity;
if (this.entityTabsComponent) {
this.entityTabsComponent.entity = this.editingEntity;
}
if (this.entitiesTableConfig.hideDetailsTabsOnEdit) {
this.selectedTab = 0;
}
}
}
onApplyDetails() {
if (this.detailsForm && this.detailsForm.valid) {
const editingEntity = {...this.editingEntity, ...this.detailsForm.getRawValue()};
if (this.detailsForm.hasOwnProperty('additionalInfo')) {
editingEntity.additionalInfo =
mergeDeep((this.editingEntity as any).additionalInfo, this.detailsForm.getRawValue()?.additionalInfo);
}
this.entitiesTableConfig.saveEntity(editingEntity, this.editingEntity).subscribe(
(entity) => {
this.entity = entity;
this.entityComponent.entity = entity;
if (this.entityTabsComponent) {
this.entityTabsComponent.entity = entity;
}
this.isEdit = false;
}
);
}
}
confirmForm(): FormGroup {
return this.detailsForm;
}
private deleteEntity($event: Event, entity: BaseData<HasId>) {
if ($event) {
$event.stopPropagation();
}
this.dialogService.confirm(
this.entitiesTableConfig.deleteEntityTitle(entity),
this.entitiesTableConfig.deleteEntityContent(entity),
this.translate.instant('action.no'),
this.translate.instant('action.yes'),
true
).subscribe((result) => {
if (result) {
this.entitiesTableConfig.deleteEntity(entity.id).subscribe(
() => {
this.router.navigate(['../'], {relativeTo: this.route});
}
);
}
});
}
}

16
ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts

@ -88,15 +88,15 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV
entity: BaseData<HasId>;
editingEntity: BaseData<HasId>;
private currentEntityId: HasId;
private subscriptions: Subscription[] = [];
private viewInited = false;
private pendingTabs: MatTab[];
protected currentEntityId: HasId;
protected subscriptions: Subscription[] = [];
protected viewInited = false;
protected pendingTabs: MatTab[];
constructor(protected store: Store<AppState>,
private injector: Injector,
private cd: ChangeDetectorRef,
private componentFactoryResolver: ComponentFactoryResolver) {
protected injector: Injector,
protected cd: ChangeDetectorRef,
protected componentFactoryResolver: ComponentFactoryResolver) {
super(store);
}
@ -139,7 +139,7 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV
return this.isEditValue;
}
private init() {
protected init() {
this.translations = this.entitiesTableConfig.entityTranslations;
this.resources = this.entitiesTableConfig.entityResources;
this.buildEntityComponent();

2
ui-ngx/src/app/modules/home/components/entity/entity.component.ts

@ -38,6 +38,8 @@ export abstract class EntityComponent<T extends BaseData<HasId>,
isEditValue: boolean;
isDetailsPage = false;
@Input()
set entitiesTableConfig(entitiesTableConfig: C) {
this.setEntitiesTableConfig(entitiesTableConfig);

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

Loading…
Cancel
Save