Browse Source

Merge branch 'master' of https://github.com/thingsboard/thingsboard into feature/notify-gateway-about-device-changed

pull/5740/head
zbeacon 5 years ago
parent
commit
e8f3950c05
  1. 9
      application/pom.xml
  2. 41
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  3. 6
      application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
  4. 9
      application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java
  5. 2
      application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java
  6. 4
      application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java
  7. 58
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  8. 60
      application/src/main/resources/thingsboard.yml
  9. 257
      application/src/test/java/org/thingsboard/server/actors/ActorSystemContextTest.java
  10. 4
      application/src/test/java/org/thingsboard/server/cache/CaffeineCacheDefaultConfigurationTest.java
  11. 42
      application/src/test/java/org/thingsboard/server/controller/AbstractInMemoryStorageTest.java
  12. 47
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  13. 196
      application/src/test/java/org/thingsboard/server/controller/BaseRpcControllerTest.java
  14. 7
      application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java
  15. 174
      application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
  16. 15
      application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java
  17. 3
      application/src/test/java/org/thingsboard/server/controller/sql/ComponentDescriptorControllerSqlTest.java
  18. 17
      application/src/test/java/org/thingsboard/server/controller/sql/RpcControllerTest.java
  19. 16
      application/src/test/java/org/thingsboard/server/edge/EdgeSqlTestSuite.java
  20. 16
      application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java
  21. 7
      application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java
  22. 15
      application/src/test/java/org/thingsboard/server/service/ServiceSqlTestSuite.java
  23. 16
      application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java
  24. 13
      application/src/test/java/org/thingsboard/server/transport/TransportNoSqlTestSuite.java
  25. 13
      application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java
  26. 12
      application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestProtoIntegrationTest.java
  27. 11
      application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesProtoIntegrationTest.java
  28. 20
      application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesProtoIntegrationTest.java
  29. 23
      application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesProtoIntegrationTest.java
  30. 31
      application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java
  31. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java
  32. 2
      common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseReadTsKvQuery.java
  33. 2
      common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java
  34. 2
      common/data/src/main/java/org/thingsboard/server/common/data/security/model/UserPasswordPolicy.java
  35. 4
      common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryStorage.java
  36. 54
      common/queue/src/test/java/org/thingsboard/server/queue/memory/InMemoryStorageTest.java
  37. 12
      dao/pom.xml
  38. 2
      dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvInsertRepository.java
  39. 4
      dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityDataAdapter.java
  40. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java
  41. 2
      dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/AbstractInsertRepository.java
  42. 29
      dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java
  43. 2
      dao/src/main/java/org/thingsboard/server/dao/timeseries/NoSqlTsPartitionDate.java
  44. 3
      dao/src/test/java/org/thingsboard/server/dao/CustomSqlUnit.java
  45. 26
      dao/src/test/java/org/thingsboard/server/dao/JpaDaoTestSuite.java
  46. 9
      dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java
  47. 1
      dao/src/test/java/org/thingsboard/server/dao/PostgreSqlInitializer.java
  48. 36
      dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java
  49. 8
      dao/src/test/java/org/thingsboard/server/dao/service/sql/EntityServiceSqlTest.java
  50. 98
      dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java
  51. 24
      dao/src/test/java/org/thingsboard/server/dao/sql/query/EntityDataAdapterTest.java
  52. 12
      dao/src/test/resources/nosql-test.properties
  53. 47
      dao/src/test/resources/psql-test.properties
  54. 12
      dao/src/test/resources/sql-test.properties
  55. 2
      pom.xml
  56. 2
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java
  57. 15
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNode.java
  58. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/telemetry/TbMsgTimeseriesNodeConfiguration.java
  59. 4265
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  60. 43
      transport/snmp/src/main/resources/tb-snmp-transport.yml
  61. 10
      ui-ngx/src/app/modules/common/modules-map.ts
  62. 4
      ui-ngx/src/app/modules/home/components/device/device-credentials.component.html
  63. 4
      ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts
  64. 3
      ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html
  65. 3
      ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts
  66. 3
      ui-ngx/src/app/shared/components/json-form/json-form.component.html
  67. 11
      ui-ngx/src/app/shared/components/json-form/json-form.component.ts
  68. 109
      ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx
  69. 4
      ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx
  70. 2
      ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts
  71. 1
      ui-ngx/src/app/shared/models/settings.models.ts
  72. 2
      ui-ngx/src/assets/locale/locale.constant-cs_CZ.json
  73. 2
      ui-ngx/src/assets/locale/locale.constant-de_DE.json
  74. 2
      ui-ngx/src/assets/locale/locale.constant-el_GR.json
  75. 5
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  76. 2
      ui-ngx/src/assets/locale/locale.constant-es_ES.json
  77. 2
      ui-ngx/src/assets/locale/locale.constant-fa_IR.json
  78. 2
      ui-ngx/src/assets/locale/locale.constant-fr_FR.json
  79. 2
      ui-ngx/src/assets/locale/locale.constant-it_IT.json
  80. 2
      ui-ngx/src/assets/locale/locale.constant-ja_JA.json
  81. 2
      ui-ngx/src/assets/locale/locale.constant-ka_GE.json
  82. 2
      ui-ngx/src/assets/locale/locale.constant-ko_KR.json
  83. 2
      ui-ngx/src/assets/locale/locale.constant-lv_LV.json
  84. 2
      ui-ngx/src/assets/locale/locale.constant-pt_BR.json
  85. 2
      ui-ngx/src/assets/locale/locale.constant-ro_RO.json
  86. 2
      ui-ngx/src/assets/locale/locale.constant-ru_RU.json
  87. 2
      ui-ngx/src/assets/locale/locale.constant-sl_SI.json
  88. 2
      ui-ngx/src/assets/locale/locale.constant-tr_TR.json
  89. 2
      ui-ngx/src/assets/locale/locale.constant-uk_UA.json
  90. 2
      ui-ngx/src/assets/locale/locale.constant-zh_CN.json
  91. 2
      ui-ngx/src/assets/locale/locale.constant-zh_TW.json

9
application/pom.xml

@ -311,8 +311,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>jdbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>

41
application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java

@ -36,6 +36,7 @@ import org.thingsboard.rule.engine.api.SmsService;
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Event;
import org.thingsboard.server.common.data.id.EntityId;
@ -82,7 +83,6 @@ import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
import org.thingsboard.server.service.mail.MailExecutorService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
import org.thingsboard.server.service.rpc.TbRpcService;
import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
@ -95,6 +95,7 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.service.transport.TbCoreToTransportService;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
@ -333,30 +334,42 @@ public class ActorSystemContext {
@Getter
private long maxConcurrentSessionsPerDevice;
@Value("${actors.session.sync.timeout}")
@Value("${actors.session.sync.timeout:10000}")
@Getter
private long syncSessionTimeout;
@Value("${actors.rule.chain.error_persist_frequency}")
@Value("${actors.rule.chain.error_persist_frequency:3000}")
@Getter
private long ruleChainErrorPersistFrequency;
@Value("${actors.rule.node.error_persist_frequency}")
@Value("${actors.rule.node.error_persist_frequency:3000}")
@Getter
private long ruleNodeErrorPersistFrequency;
@Value("${actors.statistics.enabled}")
@Value("${actors.statistics.enabled:true}")
@Getter
private boolean statisticsEnabled;
@Value("${actors.statistics.persist_frequency}")
@Value("${actors.statistics.persist_frequency:3600000}")
@Getter
private long statisticsPersistFrequency;
@Value("${edges.enabled}")
@Value("${edges.enabled:true}")
@Getter
private boolean edgesEnabled;
@Value("${cache.type:caffeine}")
@Getter
private String cacheType;
@Getter
private boolean localCacheType;
@PostConstruct
public void init() {
this.localCacheType = "caffeine".equals(cacheType);
}
@Scheduled(fixedDelayString = "${actors.statistics.js_print_interval_ms}")
public void printStats() {
if (statisticsEnabled) {
@ -368,31 +381,31 @@ public class ActorSystemContext {
}
}
@Value("${actors.tenant.create_components_on_init}")
@Value("${actors.tenant.create_components_on_init:true}")
@Getter
private boolean tenantComponentsInitEnabled;
@Value("${actors.rule.allow_system_mail_service}")
@Value("${actors.rule.allow_system_mail_service:true}")
@Getter
private boolean allowSystemMailService;
@Value("${actors.rule.allow_system_sms_service}")
@Value("${actors.rule.allow_system_sms_service:true}")
@Getter
private boolean allowSystemSmsService;
@Value("${transport.sessions.inactivity_timeout}")
@Value("${transport.sessions.inactivity_timeout:300000}")
@Getter
private long sessionInactivityTimeout;
@Value("${transport.sessions.report_timeout}")
@Value("${transport.sessions.report_timeout:3000}")
@Getter
private long sessionReportTimeout;
@Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.enabled}")
@Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.enabled:true}")
@Getter
private boolean debugPerTenantEnabled;
@Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.configuration}")
@Value("${actors.rule.chain.debug_mode_rate_limits_per_tenant.configuration:50000:3600}")
@Getter
private String debugPerTenantLimitsConfiguration;

6
application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java

@ -871,6 +871,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
}
void restoreSessions() {
if (systemContext.isLocalCacheType()) {
return;
}
log.debug("[{}] Restoring sessions from cache", deviceId);
DeviceSessionsCacheEntry sessionsDump = null;
try {
@ -905,6 +908,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor {
}
private void dumpSessions() {
if (systemContext.isLocalCacheType()) {
return;
}
log.debug("[{}] Dumping sessions: {}, rpc subscriptions: {}, attribute subscriptions: {} to cache", deviceId, sessions.size(), rpcSubscriptions.size(), attributeSubscriptions.size());
List<SessionSubscriptionInfoProto> sessionsList = new ArrayList<>(sessions.size());
sessions.forEach((uuid, sessionMD) -> {

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

@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.server.ResponseStatusException;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceId;
@ -187,10 +188,15 @@ public class RpcV2Controller extends AbstractRpcController {
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("DeviceId", strDeviceId);
try {
if (rpcStatus.equals(RpcStatus.DELETED)) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "RpcStatus: DELETED");
}
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
DeviceId deviceId = new DeviceId(UUID.fromString(strDeviceId));
final DeferredResult<ResponseEntity> response = new DeferredResult<>();
accessValidator.validate(getCurrentUser(), Operation.RPC_CALL, deviceId, new HttpValidationCallback(response, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
@ -219,7 +225,7 @@ public class RpcV2Controller extends AbstractRpcController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.DELETE)
@ResponseBody
public void deleteResource(
public void deleteRpc(
@ApiParam(value = RPC_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(RPC_ID) String strRpc) throws ThingsboardException {
checkParameter("RpcId", strRpc);
@ -235,6 +241,7 @@ public class RpcV2Controller extends AbstractRpcController {
}
rpcService.deleteRpc(getTenantId(), rpcId);
rpc.setStatus(RpcStatus.DELETED);
TbMsg msg = TbMsg.newMsg(RPC_DELETED, rpc.getDeviceId(), TbMsgMetaData.EMPTY, JacksonUtil.toString(rpc));
tbClusterService.pushMsgToRuleEngine(getTenantId(), rpc.getDeviceId(), msg, null);

2
application/src/main/java/org/thingsboard/server/service/security/auth/rest/RestLoginProcessingFilter.java

@ -73,7 +73,7 @@ public class RestLoginProcessingFilter extends AbstractAuthenticationProcessingF
throw new AuthenticationServiceException("Invalid login request payload");
}
if (StringUtils.isBlank(loginRequest.getUsername()) || StringUtils.isBlank(loginRequest.getPassword())) {
if (StringUtils.isBlank(loginRequest.getUsername()) || StringUtils.isEmpty(loginRequest.getPassword())) {
throw new AuthenticationServiceException("Username or Password not provided");
}

4
application/src/main/java/org/thingsboard/server/service/security/system/DefaultSystemSecurityService.java

@ -27,6 +27,7 @@ import org.passay.PasswordData;
import org.passay.PasswordValidator;
import org.passay.Rule;
import org.passay.RuleResult;
import org.passay.WhitespaceRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
@ -174,6 +175,9 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
if (isPositiveInteger(passwordPolicy.getMinimumSpecialCharacters())) {
passwordRules.add(new CharacterRule(EnglishCharacterData.Special, passwordPolicy.getMinimumSpecialCharacters()));
}
if (passwordPolicy.getAllowWhitespaces() != null && !passwordPolicy.getAllowWhitespaces()) {
passwordRules.add(new WhitespaceRule());
}
PasswordValidator validator = new PasswordValidator(passwordRules);
PasswordData passwordData = new PasswordData(password);
RuleResult result = validator.validate(passwordData);

58
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java

@ -20,8 +20,10 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
@ -45,7 +47,6 @@ import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.subscription.TbSubscriptionUtils;
import javax.annotation.Nullable;
@ -118,28 +119,46 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
@Override
public void saveAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) {
doSaveAndNotify(tenantId, customerId, entityId, ts, ttl, callback, true);
}
@Override
public void saveWithoutLatestAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback) {
doSaveAndNotify(tenantId, customerId, entityId, ts, ttl, callback, false);
}
private void doSaveAndNotify(TenantId tenantId, CustomerId customerId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback, boolean saveLatest) {
checkInternalEntity(entityId);
boolean sysTenant = TenantId.SYS_TENANT_ID.equals(tenantId) || tenantId == null;
if (sysTenant || apiUsageStateService.getApiUsageState(tenantId).isDbStorageEnabled()) {
saveAndNotifyInternal(tenantId, entityId, ts, ttl, new FutureCallback<Integer>() {
@Override
public void onSuccess(Integer result) {
if (!sysTenant && result != null && result > 0) {
apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.STORAGE_DP_COUNT, result);
}
callback.onSuccess(null);
}
@Override
public void onFailure(Throwable t) {
callback.onFailure(t);
}
});
if (saveLatest) {
saveAndNotifyInternal(tenantId, entityId, ts, ttl, getCallback(tenantId, customerId, sysTenant, callback));
} else {
saveWithoutLatestAndNotifyInternal(tenantId, entityId, ts, ttl, getCallback(tenantId, customerId, sysTenant, callback));
}
} else {
callback.onFailure(new RuntimeException("DB storage writes are disabled due to API limits!"));
}
}
@NotNull
private FutureCallback<Integer> getCallback(TenantId tenantId, CustomerId customerId, boolean sysTenant, FutureCallback<Void> callback) {
return new FutureCallback<>() {
@Override
public void onSuccess(Integer result) {
if (!sysTenant && result != null && result > 0) {
apiUsageClient.report(tenantId, customerId, ApiUsageRecordKey.STORAGE_DP_COUNT, result);
}
callback.onSuccess(null);
}
@Override
public void onFailure(Throwable t) {
callback.onFailure(t);
}
};
}
@Override
public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Integer> callback) {
saveAndNotifyInternal(tenantId, entityId, ts, 0L, callback);
@ -148,6 +167,15 @@ public class DefaultTelemetrySubscriptionService extends AbstractSubscriptionSer
@Override
public void saveAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Integer> callback) {
ListenableFuture<Integer> saveFuture = tsService.save(tenantId, entityId, ts, ttl);
addCallbacks(tenantId, entityId, ts, callback, saveFuture);
}
private void saveWithoutLatestAndNotifyInternal(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Integer> callback) {
ListenableFuture<Integer> saveFuture = tsService.saveWithoutLatest(tenantId, entityId, ts, ttl);
addCallbacks(tenantId, entityId, ts, callback, saveFuture);
}
private void addCallbacks(TenantId tenantId, EntityId entityId, List<TsKvEntry> ts, FutureCallback<Integer> callback, ListenableFuture<Integer> saveFuture) {
addMainCallback(saveFuture, callback);
addWsCallback(saveFuture, success -> onTimeSeriesUpdate(tenantId, entityId, ts));
if (EntityType.DEVICE.equals(entityId.getEntityType()) || EntityType.ASSET.equals(entityId.getEntityType())) {

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

@ -380,50 +380,50 @@ cache:
caffeine:
specs:
relations:
timeToLiveInMinutes: 1440
maxSize: 10000 # maxSize: 0 means the cache is disabled
timeToLiveInMinutes: "${CACHE_SPECS_RELATIONS_TTL:1440}"
maxSize: "${CACHE_SPECS_RELATIONS_MAX_SIZE:10000}" # maxSize: 0 means the cache is disabled
deviceCredentials:
timeToLiveInMinutes: 1440
maxSize: 10000
timeToLiveInMinutes: "${CACHE_SPECS_DEVICE_CREDENTIALS_TTL:1440}"
maxSize: "${CACHE_SPECS_DEVICE_CREDENTIALS_MAX_SIZE:10000}"
devices:
timeToLiveInMinutes: 1440
maxSize: 10000
timeToLiveInMinutes: "${CACHE_SPECS_DEVICES_TTL:1440}"
maxSize: "${CACHE_SPECS_DEVICES_MAX_SIZE:10000}"
sessions:
timeToLiveInMinutes: 1440
maxSize: 10000
timeToLiveInMinutes: "${CACHE_SPECS_SESSIONS_TTL:1440}"
maxSize: "${CACHE_SPECS_SESSIONS_MAX_SIZE:10000}"
assets:
timeToLiveInMinutes: 1440
maxSize: 10000
timeToLiveInMinutes: "${CACHE_SPECS_ASSETS_TTL:1440}"
maxSize: "${CACHE_SPECS_ASSETS_MAX_SIZE:10000}"
entityViews:
timeToLiveInMinutes: 1440
maxSize: 10000
timeToLiveInMinutes: "${CACHE_SPECS_ENTITY_VIEWS_TTL:1440}"
maxSize: "${CACHE_SPECS_ENTITY_VIEWS_MAX_SIZE:10000}"
claimDevices:
timeToLiveInMinutes: 1440
maxSize: 1000
timeToLiveInMinutes: "${CACHE_SPECS_CLAIM_DEVICES_TTL:1440}"
maxSize: "${CACHE_SPECS_CLAIM_DEVICES_MAX_SIZE:1000}"
securitySettings:
timeToLiveInMinutes: 1440
maxSize: 10000
timeToLiveInMinutes: "${CACHE_SPECS_SECURITY_SETTINGS_TTL:1440}"
maxSize: "${CACHE_SPECS_SECURITY_SETTINGS_MAX_SIZE:10000}"
tenantProfiles:
timeToLiveInMinutes: 1440
maxSize: 10000
timeToLiveInMinutes: "${CACHE_SPECS_TENANT_PROFILES_TTL:1440}"
maxSize: "${CACHE_SPECS_TENANT_PROFILES_MAX_SIZE:10000}"
deviceProfiles:
timeToLiveInMinutes: 1440
maxSize: 10000
timeToLiveInMinutes: "${CACHE_SPECS_DEVICE_PROFILES_TTL:1440}"
maxSize: "${CACHE_SPECS_DEVICE_PROFILES_MAX_SIZE:10000}"
attributes:
timeToLiveInMinutes: 1440
maxSize: 100000
timeToLiveInMinutes: "${CACHE_SPECS_ATTRIBUTES_TTL:1440}"
maxSize: "${CACHE_SPECS_ATTRIBUTES_MAX_SIZE:100000}"
tokensOutdatageTime:
timeToLiveInMinutes: 20000
maxSize: 10000
timeToLiveInMinutes: "${CACHE_SPECS_TOKENS_OUTDATAGE_TIME_TTL:20000}"
maxSize: "${CACHE_SPECS_TOKENS_OUTDATAGE_TIME_MAX_SIZE:10000}"
otaPackages:
timeToLiveInMinutes: 60
maxSize: 10
timeToLiveInMinutes: "${CACHE_SPECS_OTA_PACKAGES_TTL:60}"
maxSize: "${CACHE_SPECS_OTA_PACKAGES_MAX_SIZE:10}"
otaPackagesData:
timeToLiveInMinutes: 60
maxSize: 10
timeToLiveInMinutes: "${CACHE_SPECS_OTA_PACKAGES_DATA_TTL:60}"
maxSize: "${CACHE_SPECS_OTA_PACKAGES_DATA_MAX_SIZE:10}"
edges:
timeToLiveInMinutes: 1440
maxSize: 10000
timeToLiveInMinutes: "${CACHE_SPECS_EDGES_TTL:1440}"
maxSize: "${CACHE_SPECS_EDGES_MAX_SIZE:10000}"
redis:
# standalone or cluster

257
application/src/test/java/org/thingsboard/server/actors/ActorSystemContextTest.java

@ -0,0 +1,257 @@
/**
* 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.actors;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.rule.engine.api.SmsService;
import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.cassandra.CassandraCluster;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.ClaimDevicesService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.nosql.CassandraBufferedRateReadExecutor;
import org.thingsboard.server.dao.nosql.CassandraBufferedRateWriteExecutor;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.rule.RuleNodeStateService;
import org.thingsboard.server.dao.tenant.TbTenantProfileCache;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.executors.ExternalCallExecutorService;
import org.thingsboard.server.service.executors.SharedEventLoopGroupService;
import org.thingsboard.server.service.mail.MailExecutorService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
import org.thingsboard.server.service.rpc.TbRpcService;
import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
import org.thingsboard.server.service.script.JsInvokeService;
import org.thingsboard.server.service.session.DeviceSessionCacheService;
import org.thingsboard.server.service.sms.SmsExecutorService;
import org.thingsboard.server.service.state.DeviceStateService;
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.service.transport.TbCoreToTransportService;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = ActorSystemContext.class)
@EnableConfigurationProperties
@TestPropertySource(properties = {
"cache.type=caffeine",
})
public class ActorSystemContextTest {
@Autowired
ActorSystemContext ctx;
@MockBean
private TbApiUsageStateService apiUsageStateService;
@MockBean
private TbApiUsageClient apiUsageClient;
@MockBean
private TbServiceInfoProvider serviceInfoProvider;
@MockBean
private ActorService actorService;
@MockBean
private ComponentDiscoveryService componentService;
@MockBean
private DataDecodingEncodingService encodingService;
@MockBean
private DeviceService deviceService;
@MockBean
private TbTenantProfileCache tenantProfileCache;
@MockBean
private TbDeviceProfileCache deviceProfileCache;
@MockBean
private AssetService assetService;
@MockBean
private DashboardService dashboardService;
@MockBean
private TenantService tenantService;
@MockBean
private TenantProfileService tenantProfileService;
@MockBean
private CustomerService customerService;
@MockBean
private UserService userService;
@MockBean
private RuleChainService ruleChainService;
@MockBean
private RuleNodeStateService ruleNodeStateService;
@MockBean
private PartitionService partitionService;
@MockBean
private TbClusterService clusterService;
@MockBean
private TimeseriesService tsService;
@MockBean
private AttributesService attributesService;
@MockBean
private EventService eventService;
@MockBean
private RelationService relationService;
@MockBean
private AuditLogService auditLogService;
@MockBean
private EntityViewService entityViewService;
@MockBean
private TelemetrySubscriptionService tsSubService;
@MockBean
private AlarmSubscriptionService alarmService;
@MockBean
private JsInvokeService jsSandbox;
@MockBean
private MailExecutorService mailExecutor;
@MockBean
private SmsExecutorService smsExecutor;
@MockBean
private DbCallbackExecutorService dbCallbackExecutor;
@MockBean
private ExternalCallExecutorService externalCallExecutorService;
@MockBean
private SharedEventLoopGroupService sharedEventLoopGroupService;
@MockBean
private MailService mailService;
@MockBean
private SmsService smsService;
@MockBean
private SmsSenderFactory smsSenderFactory;
@MockBean
private ClaimDevicesService claimDevicesService;
@MockBean
private JsInvokeStats jsInvokeStats;
@MockBean
private DeviceStateService deviceStateService;
@MockBean
private DeviceSessionCacheService deviceSessionCacheService;
@MockBean
private TbCoreToTransportService tbCoreToTransportService;
@MockBean
private TbRuleEngineDeviceRpcService tbRuleEngineDeviceRpcService;
@MockBean
private TbCoreDeviceRpcService tbCoreDeviceRpcService;
@MockBean
private EdgeService edgeService;
@MockBean
private EdgeEventService edgeEventService;
@MockBean
private EdgeRpcService edgeRpcService;
@MockBean
private ResourceService resourceService;
@MockBean
private OtaPackageService otaPackageService;
@MockBean
private TbRpcService tbRpcService;
@MockBean
private CassandraCluster cassandraCluster;
@MockBean
private CassandraBufferedRateReadExecutor cassandraBufferedRateReadExecutor;
@MockBean
private CassandraBufferedRateWriteExecutor cassandraBufferedRateWriteExecutor;
@MockBean
private RedisTemplate<String, Object> redisTemplate;
@Test
void givenCaffeineCache_whenInit_thenIsLocalCacheTrue() {
assertThat(ctx.getCacheType()).isEqualTo("caffeine");
assertThat(ctx.isLocalCacheType()).as("caffeine is the local cache type").isTrue();
}
}

4
application/src/test/java/org/thingsboard/server/cache/CaffeineCacheDefaultConfigurationTestSuite.java → application/src/test/java/org/thingsboard/server/cache/CaffeineCacheDefaultConfigurationTest.java

@ -29,11 +29,11 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = CaffeineCacheDefaultConfigurationTestSuite.class, loader = SpringBootContextLoader.class)
@ContextConfiguration(classes = CaffeineCacheDefaultConfigurationTest.class, loader = SpringBootContextLoader.class)
@ComponentScan({"org.thingsboard.server.cache"})
@EnableConfigurationProperties
@Slf4j
public class CaffeineCacheDefaultConfigurationTestSuite {
public class CaffeineCacheDefaultConfigurationTest {
@Autowired
CaffeineCacheConfiguration caffeineCacheConfiguration;

42
application/src/test/java/org/thingsboard/server/controller/AbstractInMemoryStorageTest.java

@ -0,0 +1,42 @@
/**
* 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.controller;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.thingsboard.server.queue.memory.InMemoryStorage;
@Slf4j
public abstract class AbstractInMemoryStorageTest {
@Before
public void setUpInMemoryStorage() {
log.info("set up InMemoryStorage");
cleanupInMemStorage();
}
@After
public void tearDownInMemoryStorage() {
log.info("tear down InMemoryStorage");
cleanupInMemStorage();
}
public static void cleanupInMemStorage() {
InMemoryStorage.getInstance().cleanup();
}
}

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

@ -22,7 +22,6 @@ import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
@ -59,18 +58,20 @@ import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
import org.thingsboard.server.dao.tenant.TenantProfileService;
import org.thingsboard.server.service.mail.TestMailService;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRequest;
import org.thingsboard.server.service.security.auth.rest.LoginRequest;
@ -81,6 +82,7 @@ import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
@ -93,7 +95,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
@Slf4j
public abstract class AbstractWebTest {
public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
protected ObjectMapper mapper = new ObjectMapper();
@ -132,6 +134,9 @@ public abstract class AbstractWebTest {
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private TenantProfileService tenantProfileService;
@Rule
public TestRule watcher = new TestWatcher() {
protected void starting(Description description) {
@ -161,8 +166,9 @@ public abstract class AbstractWebTest {
}
@Before
public void setup() throws Exception {
log.info("Executing setup");
public void setupWebTest() throws Exception {
log.info("Executing web test setup");
if (this.mockMvc == null) {
this.mockMvc = webAppContextSetup(webApplicationContext)
.apply(springSecurity()).build();
@ -197,16 +203,38 @@ public abstract class AbstractWebTest {
logout();
log.info("Executed setup");
log.info("Executed web test setup");
}
@After
public void teardown() throws Exception {
log.info("Executing teardown");
public void teardownWebTest() throws Exception {
log.info("Executing web test teardown");
loginSysAdmin();
doDelete("/api/tenant/" + tenantId.getId().toString())
.andExpect(status().isOk());
log.info("Executed teardown");
verifyNoTenantsLeft();
tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID);
log.info("Executed web test teardown");
}
void verifyNoTenantsLeft() throws Exception {
List<Tenant> loadedTenants = new ArrayList<>();
PageLink pageLink = new PageLink(10);
PageData<Tenant> pageData;
do {
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>() {
}, pageLink);
loadedTenants.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
assertThat(loadedTenants).as("All tenants expected to be deleted, but some tenants left in the database").isEmpty();
}
protected void loginSysAdmin() throws Exception {
@ -570,6 +598,7 @@ public abstract class AbstractWebTest {
protected Edge constructEdge(String name, String type) {
return constructEdge(tenantId, name, type);
}
protected Edge constructEdge(TenantId tenantId, String name, String type) {
Edge edge = new Edge();
edge.setTenantId(tenantId);

196
application/src/test/java/org/thingsboard/server/controller/BaseRpcControllerTest.java

@ -0,0 +1,196 @@
/**
* 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.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.test.web.servlet.MvcResult;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.rpc.Rpc;
import org.thingsboard.server.common.data.rpc.RpcStatus;
import org.thingsboard.server.common.data.security.Authority;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public abstract class BaseRpcControllerTest extends AbstractControllerTest {
private Tenant savedTenant;
private User tenantAdmin;
@Before
public void beforeTest() throws Exception {
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());
}
private Device createDefaultDevice() {
Device device = new Device();
device.setName("My device");
device.setType("default");
return device;
}
private ObjectNode createDefaultRpc() {
ObjectNode rpc = JacksonUtil.newObjectNode();
rpc.put("method", "setGpio");
ObjectNode params = JacksonUtil.newObjectNode();
params.put("pin", 7);
params.put("value", 1);
rpc.set("params", params);
rpc.put("persistent", true);
rpc.put("timeout", 5000);
return rpc;
}
private Rpc getRpcById(String rpcId) throws Exception {
return doGet("/api/rpc/persistent/" + rpcId, Rpc.class);
}
private MvcResult removeRpcById(String rpcId) throws Exception {
return doDelete("/api/rpc/persistent/" + rpcId).andReturn();
}
@Test
public void testSaveRpc() throws Exception {
Device device = createDefaultDevice();
Device savedDevice = doPost("/api/device", device, Device.class);
ObjectNode rpc = createDefaultRpc();
String result = doPostAsync(
"/api/rpc/oneway/" + savedDevice.getId().getId().toString(),
JacksonUtil.toString(rpc),
String.class,
status().isOk()
);
String rpcId = JacksonUtil.fromString(result, JsonNode.class)
.get("rpcId")
.asText();
Rpc savedRpc = getRpcById(rpcId);
Assert.assertNotNull(savedRpc);
Assert.assertEquals(savedDevice.getId(), savedRpc.getDeviceId());
}
@Test
public void testDeleteRpc() throws Exception {
Device device = createDefaultDevice();
Device savedDevice = doPost("/api/device", device, Device.class);
ObjectNode rpc = createDefaultRpc();
String result = doPostAsync(
"/api/rpc/oneway/" + savedDevice.getId().getId().toString(),
JacksonUtil.toString(rpc),
String.class,
status().isOk()
);
String rpcId = JacksonUtil.fromString(result, JsonNode.class)
.get("rpcId")
.asText();
Rpc savedRpc = getRpcById(rpcId);
MvcResult mvcResult = removeRpcById(savedRpc.getId().getId().toString());
MvcResult res = doGet("/api/rpc/persistent/" + rpcId)
.andExpect(status().isNotFound())
.andReturn();
JsonNode deleteResponse = JacksonUtil.fromString(res.getResponse().getContentAsString(), JsonNode.class);
Assert.assertEquals(404, deleteResponse.get("status").asInt());
String url = "/api/rpc/persistent/device/" + savedDevice.getUuidId().toString()
+ "?" + "page=0" + "&" +
"pageSize=" + Integer.MAX_VALUE + "&" +
"rpcStatus=" + RpcStatus.DELETED.name();
MvcResult byDeviceResult = doGet(url).andReturn();
JsonNode byDeviceResponse = JacksonUtil.fromString(byDeviceResult.getResponse().getContentAsString(), JsonNode.class);
Assert.assertEquals(500, byDeviceResponse.get("status").asInt());
}
@Test
public void testGetRpcsByDeviceId() throws Exception {
Device device = createDefaultDevice();
Device savedDevice = doPost("/api/device", device, Device.class);
ObjectNode rpc = createDefaultRpc();
String result = doPostAsync(
"/api/rpc/oneway/" + savedDevice.getId().getId().toString(),
JacksonUtil.toString(rpc),
String.class,
status().isOk()
);
String rpcId = JacksonUtil.fromString(result, JsonNode.class)
.get("rpcId")
.asText();
String url = "/api/rpc/persistent/device/" + savedDevice.getId().getId()
+ "?" + "page=0" + "&" +
"pageSize=" + Integer.MAX_VALUE + "&" +
"rpcStatus=" + RpcStatus.QUEUED;
MvcResult byDeviceResult = doGetAsync(url).andReturn();
List<Rpc> byDeviceRpcs = JacksonUtil.fromString(
byDeviceResult
.getResponse()
.getContentAsString(),
new TypeReference<PageData<Rpc>>() {}
).getData();
boolean found = byDeviceRpcs.stream().anyMatch(r ->
r.getUuidId().toString().equals(rpcId)
&& r.getDeviceId().equals(savedDevice.getId())
);
Assert.assertTrue(found);
}
}

7
application/src/test/java/org/thingsboard/server/controller/BaseTenantProfileControllerTest.java

@ -47,13 +47,6 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController
@Autowired
private TenantProfileService tenantProfileService;
@After
@Override
public void teardown() throws Exception {
super.teardown();
tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID);
}
@Test
public void testSaveTenantProfile() throws Exception {
loginSysAdmin();

174
application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java

@ -36,6 +36,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
@ -50,15 +51,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testSaveUser() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedTenant.getId());
user.setTenantId(tenantId);
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
@ -100,24 +96,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
loginSysAdmin();
doDelete("/api/user/" + savedUser.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testSaveUserWithViolationOfFiledValidation() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedTenant.getId());
user.setTenantId(tenantId);
user.setEmail(email);
user.setFirstName(RandomStringUtils.randomAlphabetic(300));
user.setLastName("Downs");
@ -130,14 +118,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
@Test
public void testUpdateUserFromDifferentTenant() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
User tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setTenantId(tenantId);
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
@ -147,24 +131,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doPost("/api/user", tenantAdmin, User.class, status().isForbidden());
deleteDifferentTenant();
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testResetPassword() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedTenant.getId());
user.setTenantId(tenantId);
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
@ -205,24 +181,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
loginSysAdmin();
doDelete("/api/user/" + savedUser.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testFindUserById() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedTenant.getId());
user.setTenantId(tenantId);
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
@ -231,24 +199,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
Assert.assertNotNull(foundUser);
Assert.assertEquals(savedUser, foundUser);
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testSaveUserWithSameEmail() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
String email = TENANT_ADMIN_EMAIL;
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedTenant.getId());
user.setTenantId(tenantId);
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
@ -256,24 +216,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doPost("/api/user", user)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("User with email '" + email + "' already present in database")));
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testSaveUserWithInvalidEmail() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
String email = "tenant_thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedTenant.getId());
user.setTenantId(tenantId);
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
@ -281,32 +233,21 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doPost("/api/user", user)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Invalid email address format '" + email + "'")));
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testSaveUserWithEmptyEmail() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedTenant.getId());
user.setTenantId(tenantId);
user.setFirstName("Joe");
user.setLastName("Downs");
doPost("/api/user", user)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("User email should be specified")));
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
@ -328,15 +269,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testDeleteUser() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
String email = "tenant2@thingsboard.org";
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedTenant.getId());
user.setTenantId(tenantId);
user.setEmail(email);
user.setFirstName("Joe");
user.setLastName("Downs");
@ -350,17 +286,15 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doGet("/api/user/" + savedUser.getId().getId().toString())
.andExpect(status().isNotFound());
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testFindTenantAdmins() throws Exception {
loginSysAdmin();
//here created a new tenant despite already created on AbstractWebTest and then delete the tenant properly on the last line
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
tenant.setTitle("My tenant with many admins");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
@ -380,7 +314,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
PageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<PageData<User>>() {
}, pageLink);
loadedTenantAdmins.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -390,14 +325,16 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
Collections.sort(tenantAdmins, idComparator);
Collections.sort(loadedTenantAdmins, idComparator);
Assert.assertEquals(tenantAdmins, loadedTenantAdmins);
assertThat(tenantAdmins).as("admins list size").hasSameSizeAs(loadedTenantAdmins);
assertThat(tenantAdmins).as("admins list content").isEqualTo(loadedTenantAdmins);
doDelete("/api/tenant/" + tenantId.getId().toString())
.andExpect(status().isOk());
doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
.andExpect(status().isOk());
pageLink = new PageLink(33);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertTrue(pageData.getData().isEmpty());
}
@ -407,13 +344,6 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
TenantId tenantId = savedTenant.getId();
String email1 = "testEmail1";
List<User> tenantAdminsEmail1 = new ArrayList<>();
@ -447,7 +377,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
PageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<PageData<User>>() {
}, pageLink);
loadedTenantAdminsEmail1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -463,7 +394,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(16, 0, email2);
do {
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<PageData<User>>() {
}, pageLink);
loadedTenantAdminsEmail2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -481,8 +413,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
}
pageLink = new PageLink(4, 0, email1);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -492,25 +425,17 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
}
pageLink = new PageLink(4, 0, email2);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenant/" + tenantId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testFindCustomerUsers() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
TenantId tenantId = savedTenant.getId();
User tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(tenantId);
@ -540,7 +465,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
PageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<PageData<User>>() {
}, pageLink);
loadedCustomerUsers.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -554,23 +480,12 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doDelete("/api/customer/" + customerId.getId().toString())
.andExpect(status().isOk());
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testFindCustomerUsersByEmail() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
TenantId tenantId = savedTenant.getId();
User tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(tenantId);
@ -619,7 +534,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
PageData<User> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<PageData<User>>() {
}, pageLink);
loadedCustomerUsersEmail1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -635,7 +551,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
pageLink = new PageLink(16, 0, email2);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
new TypeReference<PageData<User>>() {
}, pageLink);
loadedCustomerUsersEmail2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -653,8 +570,9 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
}
pageLink = new PageLink(4, 0, email1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
@ -664,18 +582,14 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
}
pageLink = new PageLink(4, 0, email2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/users?",
new TypeReference<PageData<User>>() {
}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
doDelete("/api/customer/" + customerId.getId().toString())
.andExpect(status().isOk());
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
}

15
application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java

@ -16,14 +16,10 @@
package org.thingsboard.server.controller;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomSqlUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
// "org.thingsboard.server.controller.sql.WebsocketApiSqlTest",
@ -31,17 +27,12 @@ import java.util.Arrays;
// "org.thingsboard.server.controller.sql.TbResourceControllerSqlTest",
// "org.thingsboard.server.controller.sql.DeviceProfileControllerSqlTest",
"org.thingsboard.server.controller.sql.*Test",
})
})
public class ControllerSqlTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"),
"sql/hsql/drop-all-tables.sql",
"sql-test.properties");
@BeforeClass
public static void cleanupInMemStorage(){
public static void cleanupInMemStorage() {
InMemoryStorage.getInstance().cleanup();
}
}

3
application/src/test/java/org/thingsboard/server/controller/sql/ComponentDescriptorControllerSqlTest.java

@ -18,9 +18,6 @@ package org.thingsboard.server.controller.sql;
import org.thingsboard.server.controller.BaseComponentDescriptorControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
/**
* Created by Valerii Sosliuk on 6/28/2017.
*/
@DaoSqlTest
public class ComponentDescriptorControllerSqlTest extends BaseComponentDescriptorControllerTest {
}

17
dao/src/test/java/org/thingsboard/server/dao/PostgreSqlDaoServiceTestSuite.java → application/src/test/java/org/thingsboard/server/controller/sql/RpcControllerTest.java

@ -13,18 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao;
package org.thingsboard.server.controller.sql;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.extensions.cpsuite.ClasspathSuite.ClassnameFilters;
import org.junit.runner.RunWith;
import org.thingsboard.server.controller.BaseRpcControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
@RunWith(ClasspathSuite.class)
@ClassnameFilters({
"org.thingsboard.server.dao.service.psql.*SqlTest",
"org.thingsboard.server.dao.service.attributes.psql.*SqlTest",
"org.thingsboard.server.dao.service.event.psql.*SqlTest",
"org.thingsboard.server.dao.service.timeseries.psql.*SqlTest"
})
public class PostgreSqlDaoServiceTestSuite {
@DaoSqlTest
public class RpcControllerTest extends BaseRpcControllerTest {
}

16
application/src/test/java/org/thingsboard/server/edge/EdgeSqlTestSuite.java

@ -16,26 +16,18 @@
package org.thingsboard.server.edge;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomSqlUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.edge.sql.*Test"})
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.edge.sql.*Test",
})
public class EdgeSqlTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"),
"sql/hsql/drop-all-tables.sql",
"sql-test.properties");
@BeforeClass
public static void cleanupInMemStorage(){
public static void cleanupInMemStorage() {
InMemoryStorage.getInstance().cleanup();
}
}

16
application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java

@ -16,28 +16,20 @@
package org.thingsboard.server.rules;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomSqlUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.rules.flow.sql.*Test",
"org.thingsboard.server.rules.lifecycle.sql.*Test"})
"org.thingsboard.server.rules.lifecycle.sql.*Test",
})
public class RuleEngineSqlTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"),
"sql/hsql/drop-all-tables.sql",
"sql-test.properties");
@BeforeClass
public static void cleanupInMemStorage(){
public static void cleanupInMemStorage() {
InMemoryStorage.getInstance().cleanup();
}
}

7
application/src/test/java/org/thingsboard/server/rules/lifecycle/AbstractRuleEngineLifecycleIntegrationTest.java

@ -42,11 +42,14 @@ import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
import org.thingsboard.server.common.msg.queue.TbMsgCallback;
import org.thingsboard.server.controller.AbstractRuleEngineControllerTest;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.awaitility.Awaitility.await;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
@ -132,6 +135,8 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
attributesService.save(device.getTenantId(), device.getId(), DataConstants.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new StringDataEntry("serverAttributeKey", "serverAttributeValue"), System.currentTimeMillis())));
await("total inMemory queue lag is empty").atMost(30, TimeUnit.SECONDS)
.until(() -> InMemoryStorage.getInstance().getLagTotal() == 0);
Thread.sleep(1000);
TbMsgCallback tbMsgCallback = Mockito.mock(TbMsgCallback.class);
@ -139,7 +144,7 @@ public abstract class AbstractRuleEngineLifecycleIntegrationTest extends Abstrac
QueueToRuleEngineMsg qMsg = new QueueToRuleEngineMsg(savedTenant.getId(), tbMsg, null, null);
// Pushing Message to the system
actorSystem.tell(qMsg);
Mockito.verify(tbMsgCallback, Mockito.timeout(3000)).onSuccess();
Mockito.verify(tbMsgCallback, Mockito.timeout(10000)).onSuccess();
PageData<Event> eventsPage = getDebugEvents(savedTenant.getId(), ruleChain.getFirstRuleNodeId(), 1000);

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

@ -16,28 +16,19 @@
package org.thingsboard.server.service;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomSqlUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.service.resource.sql.*Test",
})
})
public class ServiceSqlTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"),
"sql/hsql/drop-all-tables.sql",
"sql-test.properties");
@BeforeClass
public static void cleanupInMemStorage(){
public static void cleanupInMemStorage() {
InMemoryStorage.getInstance().cleanup();
}
}

16
application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java

@ -16,29 +16,21 @@
package org.thingsboard.server.system;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomSqlUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
/**
* Created by Valerii Sosliuk on 6/27/2017.
*/
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.system.sql.*SqlTest"})
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.system.sql.*SqlTest",
})
public class SystemSqlTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"),
"sql/hsql/drop-all-tables.sql",
"sql-test.properties");
@BeforeClass
public static void cleanupInMemStorage(){
public static void cleanupInMemStorage() {
InMemoryStorage.getInstance().cleanup();
}

13
application/src/test/java/org/thingsboard/server/transport/TransportNoSqlTestSuite.java

@ -21,22 +21,16 @@ import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomCassandraCQLUnit;
import org.thingsboard.server.dao.CustomSqlUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.transport.*.telemetry.timeseries.nosql.*Test"})
"org.thingsboard.server.transport.*.telemetry.timeseries.nosql.*Test",
})
public class TransportNoSqlTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"),
"sql/hsql/drop-all-tables.sql",
"nosql-test.properties");
@ClassRule
public static CustomCassandraCQLUnit cassandraUnit =
new CustomCassandraCQLUnit(
@ -47,7 +41,8 @@ public class TransportNoSqlTestSuite {
"cassandra-test.yaml", 30000l);
@BeforeClass
public static void cleanupInMemStorage(){
public static void cleanupInMemStorage() {
InMemoryStorage.getInstance().cleanup();
}
}

13
application/src/test/java/org/thingsboard/server/transport/TransportSqlTestSuite.java

@ -16,14 +16,10 @@
package org.thingsboard.server.transport;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.thingsboard.server.dao.CustomSqlUnit;
import org.thingsboard.server.queue.memory.InMemoryStorage;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.transport.*.rpc.sql.*Test",
@ -38,14 +34,9 @@ import java.util.Arrays;
})
public class TransportSqlTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"),
"sql/hsql/drop-all-tables.sql",
"sql-test.properties");
@BeforeClass
public static void cleanupInMemStorage(){
public static void cleanupInMemStorage() {
InMemoryStorage.getInstance().cleanup();
}
}

12
application/src/test/java/org/thingsboard/server/transport/coap/attributes/request/AbstractCoapAttributesRequestProtoIntegrationTest.java

@ -24,7 +24,7 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.CoapDeviceType;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
@ -70,15 +70,15 @@ public abstract class AbstractCoapAttributesRequestProtoIntegrationTest extends
" }\n" +
"}";
@After
public void afterTest() throws Exception {
processAfterTest();
@Before
@Override
public void beforeTest() throws Exception {
processBeforeTest("Test Request attribute values from the server proto", CoapDeviceType.DEFAULT,
TransportPayloadType.PROTOBUF, null, ATTRIBUTES_SCHEMA_STR, null, null, null, null, DeviceProfileProvisionType.DISABLED);
}
@Test
public void testRequestAttributesValuesFromTheServer() throws Exception {
super.processBeforeTest("Test Request attribute values from the server proto", CoapDeviceType.DEFAULT,
TransportPayloadType.PROTOBUF, null, ATTRIBUTES_SCHEMA_STR, null, null, null, null, DeviceProfileProvisionType.DISABLED);
processTestRequestAttributesValuesFromTheServer();
}

11
application/src/test/java/org/thingsboard/server/transport/coap/telemetry/attributes/AbstractCoapAttributesProtoIntegrationTest.java

@ -20,7 +20,7 @@ import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage;
import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.CoapDeviceType;
import org.thingsboard.server.common.data.TransportPayloadType;
@ -39,14 +39,14 @@ import static org.junit.Assert.assertTrue;
@Slf4j
public abstract class AbstractCoapAttributesProtoIntegrationTest extends AbstractCoapAttributesIntegrationTest {
@After
public void afterTest() throws Exception {
processAfterTest();
@Before
@Override
public void beforeTest() throws Exception {
processBeforeTest("Test Post Attributes device Proto", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF);
}
@Test
public void testPushAttributes() throws Exception {
super.processBeforeTest("Test Post Attributes device Proto", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF);
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration);
CoapDeviceProfileTransportConfiguration coapTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration;
@ -90,7 +90,6 @@ public abstract class AbstractCoapAttributesProtoIntegrationTest extends Abstrac
@Test
public void testPushAttributesWithExplicitPresenceProtoKeys() throws Exception {
super.processBeforeTest("Test Post Attributes device Proto", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF);
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration);
CoapDeviceProfileTransportConfiguration coapTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration;

20
application/src/test/java/org/thingsboard/server/transport/coap/telemetry/timeseries/AbstractCoapTimeseriesProtoIntegrationTest.java

@ -20,7 +20,7 @@ import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage;
import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.CoapDeviceType;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
@ -33,7 +33,6 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import java.util.Arrays;
import java.util.Collections;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@ -41,14 +40,15 @@ import static org.junit.Assert.assertTrue;
@Slf4j
public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends AbstractCoapTimeseriesIntegrationTest {
@After
public void afterTest() throws Exception {
processAfterTest();
@Before
@Override
public void beforeTest() throws Exception {
//do nothing, processBeforeTest will be invoked in particular test methods with different parameters
}
@Test
public void testPushTelemetry() throws Exception {
super.processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF);
processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF);
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration);
CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration;
@ -117,7 +117,7 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac
" }\n" +
" }\n" +
"}";
super.processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration);
CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration;
@ -167,12 +167,12 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac
.setField(postTelemetryMsgDescriptor.findFieldByName("values"), valuesMsg)
.build();
processTestPostTelemetry(postTelemetryMsg.toByteArray(), Arrays.asList("key1", "key2", "key3", "key4", "key5"), true, false);
processTestPostTelemetry(postTelemetryMsg.toByteArray(), Arrays.asList("key1", "key2", "key3", "key4", "key5"), true, false);
}
@Test
public void testPushTelemetryWithExplicitPresenceProtoKeys() throws Exception {
super.processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF);
processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF);
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration);
CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration;
@ -239,7 +239,7 @@ public abstract class AbstractCoapTimeseriesProtoIntegrationTest extends Abstrac
" }\n" +
" }\n" +
"}";
super.processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
processBeforeTest("Test Post Telemetry device proto payload", CoapDeviceType.DEFAULT, TransportPayloadType.PROTOBUF, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED);
DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration();
assertTrue(transportConfiguration instanceof CoapDeviceProfileTransportConfiguration);
CoapDeviceProfileTransportConfiguration coapDeviceProfileTransportConfiguration = (CoapDeviceProfileTransportConfiguration) transportConfiguration;

23
application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/attributes/AbstractMqttAttributesProtoIntegrationTest.java

@ -20,7 +20,7 @@ import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage;
import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
@ -42,27 +42,28 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac
private static final String POST_DATA_ATTRIBUTES_TOPIC = "proto/attributes";
@After
public void afterTest() throws Exception {
processAfterTest();
@Before
@Override
public void beforeTest() throws Exception {
//do nothing, processBeforeTest will be invoked in particular test methods with different parameters
}
@Test
public void testPushAttributes() throws Exception {
super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
DynamicMessage postAttributesMsg = getDefaultDynamicMessage();
processAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), false);
}
@Test
public void testPushAttributesWithEnabledJsonBackwardCompatibility() throws Exception {
super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC, true, false);
processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC, true, false);
processJsonPayloadAttributesTest(POST_DATA_ATTRIBUTES_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), PAYLOAD_VALUES_STR.getBytes());
}
@Test
public void testPushAttributesWithExplicitPresenceProtoKeys() throws Exception {
super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
DynamicSchema attributesSchema = getDynamicSchema();
DynamicMessage.Builder nestedJsonObjectBuilder = attributesSchema.newMessageBuilder("PostAttributes.JsonObject.NestedJsonObject");
@ -95,27 +96,27 @@ public abstract class AbstractMqttAttributesProtoIntegrationTest extends Abstrac
@Test
public void testPushAttributesOnShortTopic() throws Exception {
super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
DynamicMessage postAttributesMsg = getDefaultDynamicMessage();
processAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_SHORT_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), false);
}
@Test
public void testPushAttributesOnShortJsonTopic() throws Exception {
super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
processJsonPayloadAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_SHORT_JSON_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), PAYLOAD_VALUES_STR.getBytes());
}
@Test
public void testPushAttributesOnShortProtoTopic() throws Exception {
super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, POST_DATA_ATTRIBUTES_TOPIC);
DynamicMessage postAttributesMsg = getDefaultDynamicMessage();
processAttributesTest(MqttTopics.DEVICE_ATTRIBUTES_SHORT_PROTO_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postAttributesMsg.toByteArray(), false);
}
@Test
public void testPushAttributesGateway() throws Exception {
super.processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, null);
processBeforeTest("Test Post Attributes device", "Test Post Attributes gateway", TransportPayloadType.PROTOBUF, null, null);
TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributesMsgProtoBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder();
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
String deviceName1 = "Device A";

31
application/src/test/java/org/thingsboard/server/transport/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java

@ -21,15 +21,15 @@ import com.google.protobuf.DynamicMessage;
import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfileProvisionType;
import org.thingsboard.server.common.data.TransportPayloadType;
import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.gen.transport.TransportApiProtos;
import org.thingsboard.server.gen.transport.TransportProtos;
@ -45,21 +45,22 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
private static final String POST_DATA_TELEMETRY_TOPIC = "proto/telemetry";
@After
public void afterTest() throws Exception {
processAfterTest();
@Before
@Override
public void beforeTest() throws Exception {
//do nothing, processBeforeTest will be invoked in particular test methods with different parameters
}
@Test
public void testPushTelemetry() throws Exception {
super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
DynamicMessage postTelemetryMsg = getDefaultDynamicMessage();
processTelemetryTest(POST_DATA_TELEMETRY_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postTelemetryMsg.toByteArray(), false, false);
}
@Test
public void testPushTelemetryWithEnabledJsonBackwardCompatibility() throws Exception {
super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, true, false);
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, true, false);
processJsonPayloadTelemetryTest(POST_DATA_TELEMETRY_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), PAYLOAD_VALUES_STR.getBytes(), false);
}
@ -90,7 +91,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
" }\n" +
" }\n" +
"}";
super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED, false, false);
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED, false, false);
DynamicSchema telemetrySchema = getDynamicSchema(schemaStr);
DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject");
@ -135,7 +136,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
@Test
public void testPushTelemetryWithExplicitPresenceProtoKeys() throws Exception {
super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
DynamicSchema telemetrySchema = getDynamicSchema(DEVICE_TELEMETRY_PROTO_SCHEMA);
DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject");
@ -192,7 +193,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
" }\n" +
" }\n" +
"}";
super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED, false, false);
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, schemaStr, null, null, null, null, null, DeviceProfileProvisionType.DISABLED, false, false);
DynamicSchema telemetrySchema = getDynamicSchema(schemaStr);
DynamicMessage.Builder nestedJsonObjectBuilder = telemetrySchema.newMessageBuilder("PostTelemetry.JsonObject.NestedJsonObject");
@ -232,27 +233,27 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
@Test
public void testPushTelemetryOnShortTopic() throws Exception {
super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
DynamicMessage postTelemetryMsg = getDefaultDynamicMessage();
processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_SHORT_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postTelemetryMsg.toByteArray(), false, false);
}
@Test
public void testPushTelemetryOnShortJsonTopic() throws Exception {
super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
processJsonPayloadTelemetryTest(MqttTopics.DEVICE_TELEMETRY_SHORT_JSON_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), PAYLOAD_VALUES_STR.getBytes(), false);
}
@Test
public void testPushTelemetryOnShortProtoTopic() throws Exception {
super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null);
DynamicMessage postTelemetryMsg = getDefaultDynamicMessage();
processTelemetryTest(MqttTopics.DEVICE_TELEMETRY_SHORT_PROTO_TOPIC, Arrays.asList("key1", "key2", "key3", "key4", "key5"), postTelemetryMsg.toByteArray(), false, false);
}
@Test
public void testPushTelemetryGateway() throws Exception {
super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, null, null, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED, false, false);
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, null, null, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED, false, false);
TransportApiProtos.GatewayTelemetryMsg.Builder gatewayTelemetryMsgProtoBuilder = TransportApiProtos.GatewayTelemetryMsg.newBuilder();
List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4", "key5");
String deviceName1 = "Device A";
@ -266,7 +267,7 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
@Test
public void testGatewayConnect() throws Exception {
super.processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED, false, false);
processBeforeTest("Test Post Telemetry device proto payload", "Test Post Telemetry gateway proto payload", TransportPayloadType.PROTOBUF, POST_DATA_TELEMETRY_TOPIC, null, null, null, null, null, null, null, DeviceProfileProvisionType.DISABLED, false, false);
String deviceName = "Device A";
TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName);
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);

2
common/dao-api/src/main/java/org/thingsboard/server/dao/timeseries/TimeseriesService.java

@ -41,6 +41,8 @@ public interface TimeseriesService {
ListenableFuture<Integer> save(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntry, long ttl);
ListenableFuture<Integer> saveWithoutLatest(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntry, long ttl);
ListenableFuture<List<Void>> saveLatest(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntry);
ListenableFuture<List<Void>> remove(TenantId tenantId, EntityId entityId, List<DeleteTsKvQuery> queries);

2
common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseReadTsKvQuery.java

@ -16,8 +16,10 @@
package org.thingsboard.server.common.data.kv;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class BaseReadTsKvQuery extends BaseTsKvQuery implements ReadTsKvQuery {
private final long interval;

2
common/data/src/main/java/org/thingsboard/server/common/data/rpc/RpcStatus.java

@ -16,5 +16,5 @@
package org.thingsboard.server.common.data.rpc;
public enum RpcStatus {
QUEUED, SENT, DELIVERED, SUCCESSFUL, TIMEOUT, EXPIRED, FAILED
QUEUED, SENT, DELIVERED, SUCCESSFUL, TIMEOUT, EXPIRED, FAILED, DELETED
}

2
common/data/src/main/java/org/thingsboard/server/common/data/security/model/UserPasswordPolicy.java

@ -35,6 +35,8 @@ public class UserPasswordPolicy implements Serializable {
private Integer minimumDigits;
@ApiModelProperty(position = 1, value = "Minimum number of special in the password." )
private Integer minimumSpecialCharacters;
@ApiModelProperty(position = 1, value = "Allow whitespaces")
private Boolean allowWhitespaces = true;
@ApiModelProperty(position = 1, value = "Password expiration period (days). Force expiration of the password." )
private Integer passwordExpirationPeriodDays;

4
common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryStorage.java

@ -42,6 +42,10 @@ public final class InMemoryStorage {
});
}
public int getLagTotal() {
return storage.values().stream().map(BlockingQueue::size).reduce(0, Integer::sum);
}
public static InMemoryStorage getInstance() {
if (instance == null) {
synchronized (InMemoryStorage.class) {

54
common/queue/src/test/java/org/thingsboard/server/queue/memory/InMemoryStorageTest.java

@ -0,0 +1,54 @@
/**
* 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.queue.memory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.queue.TbQueueMsg;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
public class InMemoryStorageTest {
InMemoryStorage storage = InMemoryStorage.getInstance();
@Before
public void setUp() {
storage.cleanup();
}
@After
public void tearDown() {
storage.cleanup();
}
@Test
public void givenStorage_whenGetLagTotal_thenReturnInteger() throws InterruptedException {
assertThat(storage.getLagTotal()).isEqualTo(0);
storage.put("main", mock(TbQueueMsg.class));
assertThat(storage.getLagTotal()).isEqualTo(1);
storage.put("main", mock(TbQueueMsg.class));
assertThat(storage.getLagTotal()).isEqualTo(2);
storage.put("hp", mock(TbQueueMsg.class));
assertThat(storage.getLagTotal()).isEqualTo(3);
storage.get("main");
assertThat(storage.getLagTotal()).isEqualTo(1);
storage.cleanup();
assertThat(storage.getLagTotal()).isEqualTo(0);
}
}

12
dao/pom.xml

@ -201,21 +201,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
@ -256,7 +246,7 @@
<configuration>
<excludes>
<exclude>**/sql/*Test.java</exclude>
<exclude>**/sql/*/*Test.java</exclude>
<exclude>**/sql/*/*DaoTest.java</exclude>
<exclude>**/psql/*Test.java</exclude>
<exclude>**/nosql/*Test.java</exclude>
</excludes>

2
dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvInsertRepository.java

@ -56,7 +56,7 @@ public abstract class AttributeKvInsertRepository {
@Autowired
private TransactionTemplate transactionTemplate;
@Value("${sql.remove_null_chars}")
@Value("${sql.remove_null_chars:true}")
private boolean removeNullChars;
protected void saveOrUpdate(List<AttributeKvEntity> entities) {

4
dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityDataAdapter.java

@ -79,11 +79,11 @@ public class EntityDataAdapter {
return entityData;
}
private static String convertValue(Object value) {
static String convertValue(Object value) {
if (value != null) {
String strVal = value.toString();
// check number
if (NumberUtils.isNumber(strVal)) {
if (strVal.length() > 0 && NumberUtils.isParsable(strVal)) {
try {
long longVal = Long.parseLong(strVal);
return Long.toString(longVal);

3
dao/src/main/java/org/thingsboard/server/dao/sql/relation/JpaRelationDao.java

@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.PageRequest;
import org.springframework.dao.DataAccessException;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.EntityType;
@ -161,7 +162,7 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
if (relationExistsBeforeDelete) {
try {
relationRepository.deleteById(key);
} catch (ConcurrencyFailureException e) {
} catch (DataAccessException e) {
log.debug("[{}] Concurrency exception while deleting relation", key, e);
}
}

2
dao/src/main/java/org/thingsboard/server/dao/sqlts/insert/AbstractInsertRepository.java

@ -29,7 +29,7 @@ public abstract class AbstractInsertRepository {
private static final ThreadLocal<Pattern> PATTERN_THREAD_LOCAL = ThreadLocal.withInitial(() -> Pattern.compile(String.valueOf(Character.MIN_VALUE)));
private static final String EMPTY_STR = "";
@Value("${sql.remove_null_chars}")
@Value("${sql.remove_null_chars:true}")
private boolean removeNullChars;
@Autowired

29
dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java

@ -56,6 +56,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
public class BaseTimeseriesService implements TimeseriesService {
private static final int INSERTS_PER_ENTRY = 3;
private static final int INSERTS_PER_ENTRY_WITHOUT_LATEST = 2;
private static final int DELETES_PER_ENTRY = INSERTS_PER_ENTRY;
public static final Function<List<Integer>, Integer> SUM_ALL_INTEGERS = new Function<List<Integer>, Integer>() {
@Override
@ -144,12 +145,26 @@ public class BaseTimeseriesService implements TimeseriesService {
@Override
public ListenableFuture<Integer> save(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntries, long ttl) {
List<ListenableFuture<Integer>> futures = Lists.newArrayListWithExpectedSize(tsKvEntries.size() * INSERTS_PER_ENTRY);
return doSave(tenantId, entityId, tsKvEntries, ttl, true);
}
@Override
public ListenableFuture<Integer> saveWithoutLatest(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntries, long ttl) {
return doSave(tenantId, entityId, tsKvEntries, ttl, false);
}
private ListenableFuture<Integer> doSave(TenantId tenantId, EntityId entityId, List<TsKvEntry> tsKvEntries, long ttl, boolean saveLatest) {
int inserts = saveLatest ? INSERTS_PER_ENTRY : INSERTS_PER_ENTRY_WITHOUT_LATEST;
List<ListenableFuture<Integer>> futures = Lists.newArrayListWithExpectedSize(tsKvEntries.size() * inserts);
for (TsKvEntry tsKvEntry : tsKvEntries) {
if (tsKvEntry == null) {
throw new IncorrectParameterException("Key value entry can't be null");
}
saveAndRegisterFutures(tenantId, futures, entityId, tsKvEntry, ttl);
if (saveLatest) {
saveAndRegisterFutures(tenantId, futures, entityId, tsKvEntry, ttl);
} else {
saveWithoutLatestAndRegisterFutures(tenantId, futures, entityId, tsKvEntry, ttl);
}
}
return Futures.transform(Futures.allAsList(futures), SUM_ALL_INTEGERS, MoreExecutors.directExecutor());
}
@ -167,11 +182,19 @@ public class BaseTimeseriesService implements TimeseriesService {
}
private void saveAndRegisterFutures(TenantId tenantId, List<ListenableFuture<Integer>> futures, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
doSaveAndRegisterFuturesFor(tenantId, futures, entityId, tsKvEntry, ttl);
futures.add(Futures.transform(timeseriesLatestDao.saveLatest(tenantId, entityId, tsKvEntry), v -> 0, MoreExecutors.directExecutor()));
}
private void saveWithoutLatestAndRegisterFutures(TenantId tenantId, List<ListenableFuture<Integer>> futures, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
doSaveAndRegisterFuturesFor(tenantId, futures, entityId, tsKvEntry, ttl);
}
private void doSaveAndRegisterFuturesFor(TenantId tenantId, List<ListenableFuture<Integer>> futures, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
throw new IncorrectParameterException("Telemetry data can't be stored for entity view. Read only");
}
futures.add(timeseriesDao.savePartition(tenantId, entityId, tsKvEntry.getTs(), tsKvEntry.getKey()));
futures.add(Futures.transform(timeseriesLatestDao.saveLatest(tenantId, entityId, tsKvEntry), v -> 0, MoreExecutors.directExecutor()));
futures.add(timeseriesDao.save(tenantId, entityId, tsKvEntry, ttl));
}

2
dao/src/main/java/org/thingsboard/server/dao/timeseries/NoSqlTsPartitionDate.java

@ -66,6 +66,6 @@ public enum NoSqlTsPartitionDate {
}
}
}
return Optional.of(partition);
return Optional.ofNullable(partition);
}
}

3
dao/src/test/java/org/thingsboard/server/dao/CustomSqlUnit.java

@ -32,8 +32,11 @@ import java.util.Properties;
/**
* Created by Valerii Sosliuk on 6/24/2017.
*
* Deprecated. Use PostgreSqlInitializer class instead
*/
@Slf4j
@Deprecated
public class CustomSqlUnit extends ExternalResource {
private final List<String> sqlFiles;

26
dao/src/test/java/org/thingsboard/server/dao/JpaDaoTestSuite.java

@ -15,38 +15,14 @@
*/
package org.thingsboard.server.dao;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.extensions.cpsuite.ClasspathSuite.ClassnameFilters;
import org.junit.runner.RunWith;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClassnameFilters({
"org.thingsboard.server.dao.sql.*THIS_MUST_BE_FIXED_Test"
"org.thingsboard.server.dao.sql.*THIS_MUST_BE_FIXED_Test",
})
public class JpaDaoTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"),
"sql/hsql/drop-all-tables.sql",
"sql-test.properties"
);
// @ClassRule
// public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
// Arrays.asList("sql/schema-ts-psql.sql", "sql/schema-entities.sql", "sql/system-data.sql"),
// "sql/psql/drop-all-tables.sql",
// "sql-test.properties"
// );
// @ClassRule
// public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
// Arrays.asList("sql/schema-timescale.sql", "sql/schema-timescale-idx.sql", "sql/schema-entities.sql", "sql/system-data.sql"),
// "sql/timescale/drop-all-tables.sql",
// "sql-test.properties"
// );
}

9
dao/src/test/java/org/thingsboard/server/dao/NoSqlDaoServiceTestSuite.java

@ -25,17 +25,10 @@ import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClassnameFilters({
"org.thingsboard.server.dao.service.nosql.*ServiceNoSqlTest"
"org.thingsboard.server.dao.service.nosql.*ServiceNoSqlTest",
})
public class NoSqlDaoServiceTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"),
"sql/hsql/drop-all-tables.sql",
"nosql-test.properties"
);
@ClassRule
public static CustomCassandraCQLUnit cassandraUnit =
new CustomCassandraCQLUnit(

1
dao/src/test/java/org/thingsboard/server/dao/PostgreSqlInitializer.java

@ -32,6 +32,7 @@ public class PostgreSqlInitializer {
"sql/schema-ts-psql.sql",
"sql/schema-entities.sql",
"sql/schema-entities-idx.sql",
"sql/schema-entities-idx-psql-addon.sql",
"sql/system-data.sql",
"sql/system-test-psql.sql");
private static final String dropAllTablesSqlFile = "sql/psql/drop-all-tables.sql";

36
dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java

@ -15,48 +15,16 @@
*/
package org.thingsboard.server.dao;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.extensions.cpsuite.ClasspathSuite.ClassnameFilters;
import org.junit.runner.RunWith;
import java.util.Arrays;
@RunWith(ClasspathSuite.class)
@ClassnameFilters({
"org.thingsboard.server.dao.service.sql.*SqlTest",
"org.thingsboard.server.dao.service.attributes.sql.*SqlTest",
"org.thingsboard.server.dao.service.event.sql.*SqlTest",
"org.thingsboard.server.dao.service.timeseries.sql.*SqlTest"
"org.thingsboard.server.dao.service.sql.*SqlTest",
"org.thingsboard.server.dao.service.timeseries.sql.*SqlTest",
})
public class SqlDaoServiceTestSuite {
@ClassRule
public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
Arrays.asList("sql/schema-types-hsql.sql", "sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql"
, "sql/system-data.sql"
, "sql/system-test.sql"
),
"sql/hsql/drop-all-tables.sql",
"sql-test.properties"
);
// @ClassRule
// public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
// Arrays.asList("sql/schema-ts-psql.sql"
// , "sql/schema-entities.sql", "sql/schema-entities-idx.sql"
// , "sql/system-data.sql", "sql/system-test.sql"
// ),
// "sql/psql/drop-all-tables.sql",
// "sql-test.properties"
// );
// @ClassRule
// public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
// Arrays.asList("sql/schema-timescale.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"),
// "sql/timescale/drop-all-tables.sql",
// "sql-test.properties"
// );
}

8
dao/src/test/java/org/thingsboard/server/dao/service/psql/EntityServicePostgreSqlTest.java → dao/src/test/java/org/thingsboard/server/dao/service/sql/EntityServiceSqlTest.java

@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.service.psql;
package org.thingsboard.server.dao.service.sql;
import org.thingsboard.server.dao.service.BaseEntityServiceTest;
import org.thingsboard.server.dao.service.DaoPostgreSqlTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
@DaoPostgreSqlTest
public class EntityServicePostgreSqlTest extends BaseEntityServiceTest {
@DaoSqlTest
public class EntityServiceSqlTest extends BaseEntityServiceTest {
}

98
dao/src/test/java/org/thingsboard/server/dao/service/timeseries/BaseTimeseriesServiceTest.java

@ -17,7 +17,10 @@ package org.thingsboard.server.dao.service.timeseries;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import lombok.extern.slf4j.Slf4j;
import org.junit.*;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.DeviceId;
@ -42,6 +45,8 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -52,6 +57,7 @@ import static org.junit.Assert.assertNotNull;
@Slf4j
public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
static final int MAX_TIMEOUT = 30;
private static final String STRING_KEY = "stringKey";
private static final String LONG_KEY = "longKey";
@ -93,8 +99,8 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
testLatestTsAndVerify(deviceId);
}
private void testLatestTsAndVerify(EntityId entityId) throws ExecutionException, InterruptedException {
List<TsKvEntry> tsList = tsService.findAllLatest(tenantId, entityId).get();
private void testLatestTsAndVerify(EntityId entityId) throws ExecutionException, InterruptedException, TimeoutException {
List<TsKvEntry> tsList = tsService.findAllLatest(tenantId, entityId).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertNotNull(tsList);
assertEquals(4, tsList.size());
@ -134,11 +140,24 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
saveEntries(deviceId, TS - 1);
saveEntries(deviceId, TS);
List<TsKvEntry> entries = tsService.findLatest(tenantId, deviceId, Collections.singleton(STRING_KEY)).get();
List<TsKvEntry> entries = tsService.findLatest(tenantId, deviceId, Collections.singleton(STRING_KEY)).get(MAX_TIMEOUT, TimeUnit.SECONDS);
Assert.assertEquals(1, entries.size());
Assert.assertEquals(toTsEntry(TS, stringKvEntry), entries.get(0));
}
@Test
public void testFindLatestWithoutLatestUpdate() throws Exception {
DeviceId deviceId = new DeviceId(Uuids.timeBased());
saveEntries(deviceId, TS - 2);
saveEntries(deviceId, TS - 1);
saveEntriesWithoutLatest(deviceId, TS);
List<TsKvEntry> entries = tsService.findLatest(tenantId, deviceId, Collections.singleton(STRING_KEY)).get(MAX_TIMEOUT, TimeUnit.SECONDS);
Assert.assertEquals(1, entries.size());
Assert.assertEquals(toTsEntry(TS - 1, stringKvEntry), entries.get(0));
}
@Test
public void testFindByQueryAscOrder() throws Exception {
DeviceId deviceId = new DeviceId(Uuids.timeBased());
@ -150,7 +169,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
List<ReadTsKvQuery> queries = new ArrayList<>();
queries.add(new BaseReadTsKvQuery(STRING_KEY, TS - 3, TS, 0, 1000, Aggregation.NONE, "ASC"));
List<TsKvEntry> entries = tsService.findAll(tenantId, deviceId, queries).get();
List<TsKvEntry> entries = tsService.findAll(tenantId, deviceId, queries).get(MAX_TIMEOUT, TimeUnit.SECONDS);
Assert.assertEquals(3, entries.size());
Assert.assertEquals(toTsEntry(TS - 3, stringKvEntry), entries.get(0));
Assert.assertEquals(toTsEntry(TS - 2, stringKvEntry), entries.get(1));
@ -158,7 +177,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
EntityView entityView = saveAndCreateEntityView(deviceId, Arrays.asList(STRING_KEY));
entries = tsService.findAll(tenantId, entityView.getId(), queries).get();
entries = tsService.findAll(tenantId, entityView.getId(), queries).get(MAX_TIMEOUT, TimeUnit.SECONDS);
Assert.assertEquals(3, entries.size());
Assert.assertEquals(toTsEntry(TS - 3, stringKvEntry), entries.get(0));
Assert.assertEquals(toTsEntry(TS - 2, stringKvEntry), entries.get(1));
@ -176,7 +195,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
List<ReadTsKvQuery> queries = new ArrayList<>();
queries.add(new BaseReadTsKvQuery(STRING_KEY, TS - 3, TS, 0, 1000, Aggregation.NONE, "DESC"));
List<TsKvEntry> entries = tsService.findAll(tenantId, deviceId, queries).get();
List<TsKvEntry> entries = tsService.findAll(tenantId, deviceId, queries).get(MAX_TIMEOUT, TimeUnit.SECONDS);
Assert.assertEquals(3, entries.size());
Assert.assertEquals(toTsEntry(TS - 1, stringKvEntry), entries.get(0));
Assert.assertEquals(toTsEntry(TS - 2, stringKvEntry), entries.get(1));
@ -184,7 +203,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
EntityView entityView = saveAndCreateEntityView(deviceId, Arrays.asList(STRING_KEY));
entries = tsService.findAll(tenantId, entityView.getId(), queries).get();
entries = tsService.findAll(tenantId, entityView.getId(), queries).get(MAX_TIMEOUT, TimeUnit.SECONDS);
Assert.assertEquals(3, entries.size());
Assert.assertEquals(toTsEntry(TS - 1, stringKvEntry), entries.get(0));
Assert.assertEquals(toTsEntry(TS - 2, stringKvEntry), entries.get(1));
@ -201,13 +220,13 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
saveEntries(deviceId, 40000);
tsService.remove(tenantId, deviceId, Collections.singletonList(
new BaseDeleteTsKvQuery(STRING_KEY, 25000, 45000, true))).get();
new BaseDeleteTsKvQuery(STRING_KEY, 25000, 45000, true))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
List<TsKvEntry> list = tsService.findAll(tenantId, deviceId, Collections.singletonList(
new BaseReadTsKvQuery(STRING_KEY, 5000, 45000, 10000, 10, Aggregation.NONE))).get();
new BaseReadTsKvQuery(STRING_KEY, 5000, 45000, 10000, 10, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
Assert.assertEquals(2, list.size());
List<TsKvEntry> latest = tsService.findLatest(tenantId, deviceId, Collections.singletonList(STRING_KEY)).get();
List<TsKvEntry> latest = tsService.findLatest(tenantId, deviceId, Collections.singletonList(STRING_KEY)).get(MAX_TIMEOUT, TimeUnit.SECONDS);
Assert.assertEquals(20000, latest.get(0).getTs());
}
@ -226,7 +245,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
entries.add(save(deviceId, 55000, 600));
List<TsKvEntry> list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.NONE))).get();
60000, 20000, 3, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(55000, list.get(0).getTs());
assertEquals(java.util.Optional.of(600L), list.get(0).getLongValue());
@ -238,7 +257,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(400L), list.get(2).getLongValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.AVG))).get();
60000, 20000, 3, Aggregation.AVG))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
assertEquals(java.util.Optional.of(150.0), list.get(0).getDoubleValue());
@ -250,7 +269,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(550.0), list.get(2).getDoubleValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.SUM))).get();
60000, 20000, 3, Aggregation.SUM))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
@ -263,7 +282,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(1100L), list.get(2).getLongValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.MIN))).get();
60000, 20000, 3, Aggregation.MIN))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
@ -276,7 +295,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(500L), list.get(2).getLongValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.MAX))).get();
60000, 20000, 3, Aggregation.MAX))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
@ -289,7 +308,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(600L), list.get(2).getLongValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.COUNT))).get();
60000, 20000, 3, Aggregation.COUNT))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
@ -310,7 +329,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
entries.add(save(deviceId, 115000, "C2"));
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 60000,
120000, 20000, 3, Aggregation.NONE))).get();
120000, 20000, 3, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(115000, list.get(0).getTs());
assertEquals(java.util.Optional.of("C2"), list.get(0).getStrValue());
@ -323,7 +342,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 60000,
120000, 20000, 3, Aggregation.MIN))).get();
120000, 20000, 3, Aggregation.MIN))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(70000, list.get(0).getTs());
@ -336,7 +355,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of("C1"), list.get(2).getStrValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 60000,
120000, 20000, 3, Aggregation.MAX))).get();
120000, 20000, 3, Aggregation.MAX))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(70000, list.get(0).getTs());
@ -349,7 +368,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of("C2"), list.get(2).getStrValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 60000,
120000, 20000, 3, Aggregation.COUNT))).get();
120000, 20000, 3, Aggregation.COUNT))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(70000, list.get(0).getTs());
@ -377,7 +396,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
entries.add(save(deviceId, 55000, 600.0));
List<TsKvEntry> list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.NONE))).get();
60000, 20000, 3, Aggregation.NONE))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(55000, list.get(0).getTs());
assertEquals(java.util.Optional.of(600.0), list.get(0).getDoubleValue());
@ -389,7 +408,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(400.0), list.get(2).getDoubleValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.AVG))).get();
60000, 20000, 3, Aggregation.AVG))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
assertEquals(java.util.Optional.of(150.0), list.get(0).getDoubleValue());
@ -401,7 +420,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(550.0), list.get(2).getDoubleValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.SUM))).get();
60000, 20000, 3, Aggregation.SUM))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
@ -414,7 +433,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(1100.0), list.get(2).getDoubleValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.MIN))).get();
60000, 20000, 3, Aggregation.MIN))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
@ -427,7 +446,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(500.0), list.get(2).getDoubleValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.MAX))).get();
60000, 20000, 3, Aggregation.MAX))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
@ -440,7 +459,7 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
assertEquals(java.util.Optional.of(600.0), list.get(2).getDoubleValue());
list = tsService.findAll(tenantId, deviceId, Collections.singletonList(new BaseReadTsKvQuery(LONG_KEY, 0,
60000, 20000, 3, Aggregation.COUNT))).get();
60000, 20000, 3, Aggregation.COUNT))).get(MAX_TIMEOUT, TimeUnit.SECONDS);
assertEquals(3, list.size());
assertEquals(10000, list.get(0).getTs());
@ -455,28 +474,37 @@ public abstract class BaseTimeseriesServiceTest extends AbstractServiceTest {
private TsKvEntry save(DeviceId deviceId, long ts, long value) throws Exception {
TsKvEntry entry = new BasicTsKvEntry(ts, new LongDataEntry(LONG_KEY, value));
tsService.save(tenantId, deviceId, entry).get();
tsService.save(tenantId, deviceId, entry).get(MAX_TIMEOUT, TimeUnit.SECONDS);
return entry;
}
private TsKvEntry save(DeviceId deviceId, long ts, double value) throws Exception {
TsKvEntry entry = new BasicTsKvEntry(ts, new DoubleDataEntry(LONG_KEY, value));
tsService.save(tenantId, deviceId, entry).get();
tsService.save(tenantId, deviceId, entry).get(MAX_TIMEOUT, TimeUnit.SECONDS);
return entry;
}
private TsKvEntry save(DeviceId deviceId, long ts, String value) throws Exception {
TsKvEntry entry = new BasicTsKvEntry(ts, new StringDataEntry(LONG_KEY, value));
tsService.save(tenantId, deviceId, entry).get();
tsService.save(tenantId, deviceId, entry).get(MAX_TIMEOUT, TimeUnit.SECONDS);
return entry;
}
private void saveEntries(DeviceId deviceId, long ts) throws ExecutionException, InterruptedException {
tsService.save(tenantId, deviceId, toTsEntry(ts, stringKvEntry)).get();
tsService.save(tenantId, deviceId, toTsEntry(ts, longKvEntry)).get();
tsService.save(tenantId, deviceId, toTsEntry(ts, doubleKvEntry)).get();
tsService.save(tenantId, deviceId, toTsEntry(ts, booleanKvEntry)).get();
private void saveEntries(DeviceId deviceId, long ts) throws ExecutionException, InterruptedException, TimeoutException {
tsService.save(tenantId, deviceId, toTsEntry(ts, stringKvEntry)).get(MAX_TIMEOUT, TimeUnit.SECONDS);
tsService.save(tenantId, deviceId, toTsEntry(ts, longKvEntry)).get(MAX_TIMEOUT, TimeUnit.SECONDS);
tsService.save(tenantId, deviceId, toTsEntry(ts, doubleKvEntry)).get(MAX_TIMEOUT, TimeUnit.SECONDS);
tsService.save(tenantId, deviceId, toTsEntry(ts, booleanKvEntry)).get(MAX_TIMEOUT, TimeUnit.SECONDS);
}
private void saveEntriesWithoutLatest(DeviceId deviceId, long ts) throws ExecutionException, InterruptedException, TimeoutException {
List<TsKvEntry> tsKvEntry = List.of(
toTsEntry(ts, stringKvEntry),
toTsEntry(ts, longKvEntry),
toTsEntry(ts, doubleKvEntry),
toTsEntry(ts, booleanKvEntry));
tsService.saveWithoutLatest(tenantId, deviceId, tsKvEntry, 0).get(MAX_TIMEOUT, TimeUnit.SECONDS);
}
private static TsKvEntry toTsEntry(long ts, KvEntry entry) {

24
dao/src/test/java/org/thingsboard/server/dao/service/DaoPostgreSqlTest.java → dao/src/test/java/org/thingsboard/server/dao/sql/query/EntityDataAdapterTest.java

@ -13,21 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.service;
package org.thingsboard.server.dao.sql.query;
import org.springframework.test.context.TestPropertySource;
import org.junit.Test;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static org.assertj.core.api.Assertions.assertThat;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@TestPropertySource(locations = {"classpath:application-test.properties", "classpath:psql-test.properties"})
public @interface DaoPostgreSqlTest {
public class EntityDataAdapterTest {
@Test
public void testConvertValue() {
assertThat(EntityDataAdapter.convertValue("500")).isEqualTo("500");
assertThat(EntityDataAdapter.convertValue("500D")).isEqualTo("500D"); //do not convert to Double !!!
}
}

12
dao/src/test/resources/nosql-test.properties

@ -9,11 +9,11 @@ spring.jpa.properties.hibernate.order_by.default_null_ordering=last
spring.jpa.properties.hibernate.jdbc.log.warnings=false
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.url=jdbc:hsqldb:file:/tmp/testDb;sql.enforce_size=false
spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.url=jdbc:tc:postgresql:12.8:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb
spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.datasource.hikari.maximumPoolSize = 50

47
dao/src/test/resources/psql-test.properties

@ -1,47 +0,0 @@
database.ts.type=sql
database.ts_latest.type=sql
sql.ts_inserts_executor_type=fixed
sql.ts_inserts_fixed_thread_pool_size=200
sql.ts_key_value_partitioning=MONTHS
#
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.properties.hibernate.order_by.default_null_ordering=last
spring.jpa.properties.hibernate.jdbc.log.warnings=false
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.url=jdbc:tc:postgresql:12.8:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb
spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver
#org.postgresql.Driver
spring.datasource.hikari.maximumPoolSize=50
service.type=monolith
#database.ts.type=timescale
#database.ts.type=sql
#database.entities.type=sql
#
#sql.ts_inserts_executor_type=fixed
#sql.ts_inserts_fixed_thread_pool_size=200
#sql.ts_key_value_partitioning=MONTHS
#
#spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
#spring.jpa.show-sql=false
#spring.jpa.hibernate.ddl-auto=none
#spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
#
#spring.datasource.username=postgres
#spring.datasource.password=postgres
#spring.datasource.url=jdbc:postgresql://localhost:5432/sqltest
#spring.datasource.driverClassName=org.postgresql.Driver
#spring.datasource.hikari.maximumPoolSize = 50
queue.core.pack-processing-timeout=3000
queue.rule-engine.pack-processing-timeout=3000
queue.rule-engine.queues[0].name=Main
queue.rule-engine.queues[0].topic=tb_rule_engine.main
queue.rule-engine.queues[0].poll-interval=25
queue.rule-engine.queues[0].partitions=3
queue.rule-engine.queues[0].pack-processing-timeout=3000
queue.rule-engine.queues[0].processing-strategy.type=SKIP_ALL_FAILURES
queue.rule-engine.queues[0].submit-strategy.type=BURST
sql.log_entity_queries=true

12
dao/src/test/resources/sql-test.properties

@ -10,13 +10,13 @@ spring.jpa.properties.hibernate.order_by.default_null_ordering=last
spring.jpa.properties.hibernate.jdbc.log.warnings=false
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.database-platform=org.hibernate.dialect.HSQLDialect
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.url=jdbc:hsqldb:file:target/tmp/testDb;sql.enforce_size=false;sql.syntax_pgs=true
spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.url=jdbc:tc:postgresql:12.8:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb
spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.datasource.hikari.maximumPoolSize = 50
service.type=monolith

2
pom.xml

@ -47,7 +47,7 @@
<jedis.version>3.3.0</jedis.version>
<jjwt.version>0.7.0</jjwt.version>
<slf4j.version>1.7.32</slf4j.version>
<log4j.version>2.15.0</log4j.version>
<log4j.version>2.16.0</log4j.version>
<logback.version>1.2.6</logback.version>
<rat.version>0.10</rat.version>
<cassandra.version>4.10.0</cassandra.version>

2
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineTelemetryService.java

@ -34,6 +34,8 @@ public interface RuleEngineTelemetryService {
void saveAndNotify(TenantId tenantId, CustomerId id, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback);
void saveWithoutLatestAndNotify(TenantId tenantId, CustomerId id, EntityId entityId, List<TsKvEntry> ts, long ttl, FutureCallback<Void> callback);
void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, FutureCallback<Void> callback);
void saveAndNotify(TenantId tenantId, EntityId entityId, String scope, List<AttributeKvEntry> attributes, boolean notifyDevice, FutureCallback<Void> callback);

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

@ -23,9 +23,8 @@ import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
@ -46,7 +45,9 @@ import java.util.concurrent.TimeUnit;
name = "save timeseries",
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",
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.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeTimeseriesConfig",
icon = "file_upload"
@ -94,7 +95,11 @@ public class TbMsgTimeseriesNode implements TbNode {
if (ttl == 0L) {
ttl = tenantProfileDefaultStorageTtl;
}
ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), msg.getCustomerId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg));
if (config.isSkipLatestPersistence()) {
ctx.getTelemetryService().saveWithoutLatestAndNotify(ctx.getTenantId(), msg.getCustomerId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg));
} else {
ctx.getTelemetryService().saveAndNotify(ctx.getTenantId(), msg.getCustomerId(), msg.getOriginator(), tsKvEntryList, ttl, new TelemetryNodeCallback(ctx, msg));
}
}
public static long getTs(TbMsg msg) {
@ -103,7 +108,7 @@ public class TbMsgTimeseriesNode implements TbNode {
if (!StringUtils.isEmpty(tsStr)) {
try {
ts = Long.parseLong(tsStr);
} catch (NumberFormatException e) {
} catch (NumberFormatException ignored) {
}
} else {
ts = msg.getTs();

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

@ -22,11 +22,13 @@ import org.thingsboard.rule.engine.api.NodeConfiguration;
public class TbMsgTimeseriesNodeConfiguration implements NodeConfiguration<TbMsgTimeseriesNodeConfiguration> {
private long defaultTTL;
private boolean skipLatestPersistence;
@Override
public TbMsgTimeseriesNodeConfiguration defaultConfiguration() {
TbMsgTimeseriesNodeConfiguration configuration = new TbMsgTimeseriesNodeConfiguration();
configuration.setDefaultTTL(0L);
configuration.setSkipLatestPersistence(false);
return configuration;
}
}

4265
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

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

@ -40,6 +40,49 @@ zk:
# Name of the directory in zookeeper 'filesystem'
zk_dir: "${ZOOKEEPER_NODES_DIR:/thingsboard}"
cache:
type: "${CACHE_TYPE:redis}"
redis:
# standalone or cluster
connection:
type: "${REDIS_CONNECTION_TYPE:standalone}"
standalone:
host: "${REDIS_HOST:localhost}"
port: "${REDIS_PORT:6379}"
useDefaultClientConfig: "${REDIS_USE_DEFAULT_CLIENT_CONFIG:true}"
# this value may be used only if you used not default ClientConfig
clientName: "${REDIS_CLIENT_NAME:standalone}"
# this value may be used only if you used not default ClientConfig
connectTimeout: "${REDIS_CLIENT_CONNECT_TIMEOUT:30000}"
# this value may be used only if you used not default ClientConfig
readTimeout: "${REDIS_CLIENT_READ_TIMEOUT:60000}"
# this value may be used only if you used not default ClientConfig
usePoolConfig: "${REDIS_CLIENT_USE_POOL_CONFIG:false}"
cluster:
# Comma-separated list of "host:port" pairs to bootstrap from.
nodes: "${REDIS_NODES:}"
# Maximum number of redirects to follow when executing commands across the cluster.
max-redirects: "${REDIS_MAX_REDIRECTS:12}"
useDefaultPoolConfig: "${REDIS_USE_DEFAULT_POOL_CONFIG:true}"
# db index
db: "${REDIS_DB:0}"
# db password
password: "${REDIS_PASSWORD:}"
# pool config
pool_config:
maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}"
maxIdle: "${REDIS_POOL_CONFIG_MAX_IDLE:128}"
minIdle: "${REDIS_POOL_CONFIG_MIN_IDLE:16}"
testOnBorrow: "${REDIS_POOL_CONFIG_TEST_ON_BORROW:true}"
testOnReturn: "${REDIS_POOL_CONFIG_TEST_ON_RETURN:true}"
testWhileIdle: "${REDIS_POOL_CONFIG_TEST_WHILE_IDLE:true}"
minEvictableMs: "${REDIS_POOL_CONFIG_MIN_EVICTABLE_MS:60000}"
evictionRunsMs: "${REDIS_POOL_CONFIG_EVICTION_RUNS_MS:30000}"
maxWaitMills: "${REDIS_POOL_CONFIG_MAX_WAIT_MS:60000}"
numberTestsPerEvictionRun: "${REDIS_POOL_CONFIG_NUMBER_TESTS_PER_EVICTION_RUN:3}"
blockWhenExhausted: "${REDIS_POOL_CONFIG_BLOCK_WHEN_EXHAUSTED:true}"
transport:
snmp:
enabled: "${SNMP_ENABLED:true}"

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

@ -23,6 +23,7 @@ import * as AngularFlexLayoutFlex from '@angular/flex-layout/flex';
import * as AngularFlexLayoutGrid from '@angular/flex-layout/grid';
import * as AngularFlexLayoutExtended from '@angular/flex-layout/extended';
import * as AngularPlatformBrowser from '@angular/platform-browser';
import * as AngularPlatformBrowserAnimations from '@angular/platform-browser/animations';
import * as AngularRouter from '@angular/router';
import * as AngularCdkCoercion from '@angular/cdk/coercion';
import * as AngularCdkCollections from '@angular/cdk/collections';
@ -30,6 +31,8 @@ import * as AngularCdkKeycodes from '@angular/cdk/keycodes';
import * as AngularCdkLayout from '@angular/cdk/layout';
import * as AngularCdkOverlay from '@angular/cdk/overlay';
import * as AngularCdkPortal from '@angular/cdk/portal';
import * as AngularCdkBidi from '@angular/cdk/bidi';
import * as AngularCdkPlatform from '@angular/cdk/platform';
import * as AngularMaterialAutocomplete from '@angular/material/autocomplete';
import * as AngularMaterialBadge from '@angular/material/badge';
import * as AngularMaterialBottomSheet from '@angular/material/bottom-sheet';
@ -72,7 +75,9 @@ import * as NgrxStore from '@ngrx/store';
import * as RxJs from 'rxjs';
import * as RxJsOperators from 'rxjs/operators';
import * as TranslateCore from '@ngx-translate/core';
import * as MatDateTimePicker from '@mat-datetimepicker/core';
import * as _moment from 'moment';
import * as tslib from 'tslib';
import * as TbCore from '@core/public-api';
import * as TbShared from '@shared/public-api';
@ -301,6 +306,7 @@ class ModulesMap implements IModulesMap {
'@angular/flex-layout/grid': AngularFlexLayoutGrid,
'@angular/flex-layout/extended': AngularFlexLayoutExtended,
'@angular/platform-browser': AngularPlatformBrowser,
'@angular/platform-browser/animations': AngularPlatformBrowserAnimations,
'@angular/router': AngularRouter,
'@angular/cdk/coercion': AngularCdkCoercion,
'@angular/cdk/collections': AngularCdkCollections,
@ -308,6 +314,8 @@ class ModulesMap implements IModulesMap {
'@angular/cdk/layout': AngularCdkLayout,
'@angular/cdk/overlay': AngularCdkOverlay,
'@angular/cdk/portal': AngularCdkPortal,
'@angular/cdk/bidi': AngularCdkBidi,
'@angular/cdk/platform': AngularCdkPlatform,
'@angular/cdk/drag-drop': DragDropModule,
'@angular/material/autocomplete': AngularMaterialAutocomplete,
'@angular/material/badge': AngularMaterialBadge,
@ -348,7 +356,9 @@ class ModulesMap implements IModulesMap {
rxjs: RxJs,
'rxjs/operators': RxJsOperators,
'@ngx-translate/core': TranslateCore,
'@mat-datetimepicker/core': MatDateTimePicker,
moment: _moment,
tslib,
'@core/public-api': TbCore,
'@shared/public-api': TbShared,

4
ui-ngx/src/app/modules/home/components/device/device-credentials.component.html

@ -39,10 +39,10 @@
</ng-template>
<ng-template [ngSwitchCase]="deviceCredentialsType.X509_CERTIFICATE">
<mat-form-field class="mat-block">
<mat-label translate>device.rsa-key</mat-label>
<mat-label translate>device.certificate-pem-format</mat-label>
<textarea matInput formControlName="credentialsValue" cols="15" rows="5" required></textarea>
<mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">
{{ 'device.rsa-key-required' | translate }}
{{ 'device.certificate-pem-format-required' | translate }}
</mat-error>
</mat-form-field>
</ng-template>

4
ui-ngx/src/app/modules/home/components/widget/trip-animation/trip-animation.component.ts

@ -49,7 +49,7 @@ import {
} from '@home/components/widget/lib/maps/common-maps-utils';
import { JsonSettingsSchema, WidgetConfig } from '@shared/models/widget.models';
import moment from 'moment';
import { isDefined, isUndefined } from '@core/utils';
import { deepClone, isDefined, isUndefined } from '@core/utils';
import { ResizeObserver } from '@juggle/resize-observer';
import { MapWidgetInterface } from '@home/components/widget/lib/maps/map-widget.interface';
@ -100,7 +100,7 @@ export class TripAnimationComponent implements OnInit, AfterViewInit, OnDestroy
addGroupInfo(schema, 'Path Settings');
addToSchema(schema, addCondition(pointSchema, 'model.showPoints === true', ['showPoints']));
addGroupInfo(schema, 'Path Points Settings');
const mapPolygonSchemaWithoutEdit = mapPolygonSchema;
const mapPolygonSchemaWithoutEdit = deepClone(mapPolygonSchema);
delete mapPolygonSchemaWithoutEdit.schema.properties.editablePolygon;
mapPolygonSchemaWithoutEdit.form.splice(mapPolygonSchemaWithoutEdit.form.indexOf('editablePolygon'), 1);
addToSchema(schema, addCondition(mapPolygonSchemaWithoutEdit, 'model.showPolygon === true', ['showPolygon']));

3
ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html

@ -139,6 +139,9 @@
{{ 'admin.password-reuse-frequency-days-range' | translate }}
</mat-error>
</mat-form-field>
<mat-checkbox formControlName = "allowWhitespaces" >
<mat-label translate>admin.allow-whitespace</mat-label>
</mat-checkbox>
</section>
</mat-expansion-panel>
</mat-accordion>

3
ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts

@ -63,7 +63,8 @@ export class SecuritySettingsComponent extends PageComponent implements OnInit,
minimumDigits: [null, Validators.min(0)],
minimumSpecialCharacters: [null, Validators.min(0)],
passwordExpirationPeriodDays: [null, Validators.min(0)],
passwordReuseFrequencyDays: [null, Validators.min(0)]
passwordReuseFrequencyDays: [null, Validators.min(0)],
allowWhitespaces: [true]
}
)
});

3
ui-ngx/src/app/shared/components/json-form/json-form.component.html

@ -15,8 +15,9 @@
limitations under the License.
-->
<div class="tb-json-form" style="background: #fff;" tb-fullscreen [fullscreenElement]="targetFullscreenElement"
<div class="tb-json-form" style="background: #fff;" tb-fullscreen [fullscreenElement]="reactFullscreen"
[fullscreen]="isFullscreen"
(fullscreenChanged)="onFullscreenChanged($event)">
<div #reactRoot></div>
<div class="tb-json-form" #reactFullscreen></div>
</div>

11
ui-ngx/src/app/shared/components/json-form/json-form.component.ts

@ -73,6 +73,9 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
@ViewChild('reactRoot', {static: true})
reactRootElmRef: ElementRef<HTMLElement>;
@ViewChild('reactFullscreen', {static: true})
reactFullscreenElmRef: ElementRef<HTMLElement>;
private readonlyValue: boolean;
get readonly(): boolean {
return this.readonlyValue;
@ -106,8 +109,7 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
isModelValid = true;
isFullscreen = false;
targetFullscreenElement: HTMLElement;
fullscreenFinishFn: () => void;
fullscreenFinishFn: (el: Element) => void;
private propagateChange = null;
private propagateChangePending = false;
@ -233,8 +235,7 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
});
}
private onToggleFullscreen(element: HTMLElement, fullscreenFinishFn?: () => void) {
this.targetFullscreenElement = element;
private onToggleFullscreen(fullscreenFinishFn?: (el: Element) => void) {
this.isFullscreen = !this.isFullscreen;
this.fullscreenFinishFn = fullscreenFinishFn;
this.cd.markForCheck();
@ -244,7 +245,7 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato
this.formProps.isFullscreen = fullscreen;
this.renderReactSchemaForm(false);
if (this.fullscreenFinishFn) {
this.fullscreenFinishFn();
this.fullscreenFinishFn(this.reactFullscreenElmRef.nativeElement);
this.fullscreenFinishFn = null;
}
}

109
ui-ngx/src/app/shared/components/json-form/react/json-form-ace-editor.tsx

@ -14,6 +14,7 @@
* limitations under the License.
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import ThingsboardBaseComponent from './json-form-base-component';
import reactCSS from 'reactcss';
import Button from '@material-ui/core/Button';
@ -42,6 +43,7 @@ interface ThingsboardAceEditorProps extends JsonFormFieldProps {
interface ThingsboardAceEditorState extends JsonFormFieldState {
isFull: boolean;
fullscreenContainerElement: Element;
helpVisible: boolean;
helpReady: boolean;
focused: boolean;
@ -49,7 +51,6 @@ interface ThingsboardAceEditorState extends JsonFormFieldState {
class ThingsboardAceEditor extends React.Component<ThingsboardAceEditorProps, ThingsboardAceEditorState> {
hostElement: HTMLElement;
private aceEditor: IEditorProps;
constructor(props) {
@ -64,6 +65,7 @@ class ThingsboardAceEditor extends React.Component<ThingsboardAceEditorProps, Th
const value = props.value ? props.value + '' : '';
this.state = {
isFull: false,
fullscreenContainerElement: null,
helpVisible: false,
helpReady: true,
value,
@ -131,12 +133,8 @@ class ThingsboardAceEditor extends React.Component<ThingsboardAceEditorProps, Th
}
onToggleFull() {
this.setState({ isFull: !this.state.isFull });
this.props.onToggleFullscreen(this.hostElement, () => {
if (this.aceEditor) {
this.aceEditor.resize();
this.aceEditor.renderer.updateFull();
}
this.props.onToggleFullscreen((el) => {
this.setState({ isFull: !this.state.isFull, fullscreenContainerElement: el });
});
}
@ -177,56 +175,61 @@ class ThingsboardAceEditor extends React.Component<ThingsboardAceEditorProps, Th
if (this.state.isFull) {
containerClass += ' fullscreen-form-field';
}
return (
<div>
<div className='tb-json-form' ref={c => (this.hostElement = c)}>
<div className={containerClass}>
<label className={labelClass}>{this.props.form.title}</label>
<div className='json-form-ace-editor'>
<div className='title-panel'>
<label>{this.props.mode}</label>
{ this.props.onTidy ? <Button style={ styles.tidyButtonStyle }
className='tidy-button' onClick={this.onTidy}>Tidy</Button> : null }
{ this.props.form.helpId ? <div style={ {position: 'relative', display: 'inline-block', marginLeft: '5px'} }>
<IconButton color='primary'
className='help-button' onClick={this.onHelp}>
{this.state.helpVisible ? <Help /> : <HelpOutline /> }
</IconButton>
{ this.state.helpVisible && !this.state.helpReady ?
<div className='tb-absolute-fill help-button-loading'>
<CircularProgress size={18} thickness={4}/>
</div> : null }</div> : null }
<Button style={ styles.tidyButtonStyle }
className='tidy-button' onClick={this.onToggleFull}>
{this.state.isFull ?
'Exit fullscreen' : 'Fullscreen'}
</Button>
</div>
<React.Suspense fallback={<div>Loading...</div>}>
<ReactAce mode={this.props.mode}
theme={'textmate'}
height={this.state.isFull ? '100%' : '150px'}
width={this.state.isFull ? '100%' : '300px'}
onChange={this.onValueChanged}
onFocus={this.onFocus}
onBlur={this.onBlur}
onLoad={this.onLoad}
name={this.props.form.title}
value={this.state.value}
readOnly={this.props.form.readonly}
editorProps={{$blockScrolling: Infinity}}
enableBasicAutocompletion={true}
enableSnippets={true}
enableLiveAutocompletion={true}
style={style}/>
</React.Suspense>
</div>
<div className='json-form-error'
style={{opacity: this.props.valid ? '0' : '1'}}>{this.props.error}</div>
const formDom = (
<div className={containerClass}>
<label className={labelClass}>{this.props.form.title}</label>
<div className='json-form-ace-editor'>
<div className='title-panel'>
<label>{this.props.mode}</label>
{ this.props.onTidy ? <Button style={ styles.tidyButtonStyle }
className='tidy-button' onClick={this.onTidy}>Tidy</Button> : null }
{ this.props.form.helpId ? <div style={ {position: 'relative', display: 'inline-block', marginLeft: '5px'} }>
<IconButton color='primary'
className='help-button' onClick={this.onHelp}>
{this.state.helpVisible ? <Help /> : <HelpOutline /> }
</IconButton>
{ this.state.helpVisible && !this.state.helpReady ?
<div className='tb-absolute-fill help-button-loading'>
<CircularProgress size={18} thickness={4}/>
</div> : null }</div> : null }
<Button style={ styles.tidyButtonStyle }
className='tidy-button' onClick={this.onToggleFull}>
{this.state.isFull ?
'Exit fullscreen' : 'Fullscreen'}
</Button>
</div>
<React.Suspense fallback={<div>Loading...</div>}>
<ReactAce mode={this.props.mode}
theme={'textmate'}
height={this.state.isFull ? '100%' : '150px'}
width={this.state.isFull ? '100%' : '300px'}
onChange={this.onValueChanged}
onFocus={this.onFocus}
onBlur={this.onBlur}
onLoad={this.onLoad}
name={this.props.form.title}
value={this.state.value}
readOnly={this.props.form.readonly}
editorProps={{$blockScrolling: Infinity}}
enableBasicAutocompletion={true}
enableSnippets={true}
enableLiveAutocompletion={true}
style={style}/>
</React.Suspense>
</div>
<div className='json-form-error'
style={{opacity: this.props.valid ? '0' : '1'}}>{this.props.error}</div>
</div>
);
if (this.state.isFull) {
return ReactDOM.createPortal(formDom, this.state.fullscreenContainerElement);
} else {
return (
<div>
{formDom}
</div>
);
}
}
}

4
ui-ngx/src/app/shared/components/json-form/react/json-form-schema-form.tsx

@ -106,8 +106,8 @@ class ThingsboardSchemaForm extends React.Component<JsonFormProps, any> {
this.props.onIconClick(key, val, iconSelectedFn);
}
onToggleFullscreen(element: HTMLElement, fullscreenFinishFn?: () => void) {
this.props.onToggleFullscreen(element, fullscreenFinishFn);
onToggleFullscreen(fullscreenFinishFn?: (el: Element) => void) {
this.props.onToggleFullscreen(fullscreenFinishFn);
}
onHelpClick(event: MouseEvent, helpId: string, helpVisibleFn: (visible: boolean) => void, helpReadyFn: (ready: boolean) => void) {

2
ui-ngx/src/app/shared/components/json-form/react/json-form.models.ts

@ -48,7 +48,7 @@ export type OnColorClickFn = (key: (string | number)[], val: tinycolor.ColorForm
colorSelectedFn: (color: tinycolor.ColorFormats.RGBA) => void) => void;
export type OnIconClickFn = (key: (string | number)[], val: string,
iconSelectedFn: (icon: string) => void) => void;
export type onToggleFullscreenFn = (element: HTMLElement, fullscreenFinishFn?: () => void) => void;
export type onToggleFullscreenFn = (fullscreenFinishFn?: (el: Element) => void) => void;
export type onHelpClickFn = (event: MouseEvent, helpId: string, helpVisibleFn: (visible: boolean) => void,
helpReadyFn: (ready: boolean) => void) => void;

1
ui-ngx/src/app/shared/models/settings.models.ts

@ -55,6 +55,7 @@ export interface UserPasswordPolicy {
minimumDigits: number;
minimumSpecialCharacters: number;
passwordExpirationPeriodDays: number;
allowWhitespaces: boolean;
}
export interface SecuritySettings {

2
ui-ngx/src/assets/locale/locale.constant-cs_CZ.json

@ -954,8 +954,6 @@
"access-token": "Přístupový token",
"access-token-required": "Přístupový token je povinný.",
"access-token-invalid": "Délka přístupového tokenu musí být od 1 do 32 znaků.",
"rsa-key": "RSA veřejný klíč",
"rsa-key-required": "RSA veřejný klíč je povinný.",
"lwm2m-security-config": {
"identity": "Identita klienta",
"identity-required": "Identita klienta je povinná.",

2
ui-ngx/src/assets/locale/locale.constant-de_DE.json

@ -699,8 +699,6 @@
"access-token": "Zugangs-Token",
"access-token-required": "Zugangs-Token ist erforderlich.",
"access-token-invalid": "Die Länge des Zugangs-Tokens muss zwischen 1 und 32 Zeichen betragen.",
"rsa-key": "RSA öffentlicher Schlüssel",
"rsa-key-required": "RSA öffentlicher Schlüssel ist erforderlich.",
"secret": "Geheimnis",
"secret-required": "Geheimnis ist erforderlich.",
"device-type": "Gerätetyp",

2
ui-ngx/src/assets/locale/locale.constant-el_GR.json

@ -799,8 +799,6 @@
"access-token": "Διακριτικό πρόσβασης",
"access-token-required": "Απαιτείται διακριτικό πρόσβασης.",
"access-token-invalid": "Access token length must be from 1 to 32 characters.",
"rsa-key": "RSA public key",
"rsa-key-required": "Απαιτείται RSA public key.",
"secret": "Secret",
"secret-required": "Απαιτείται secret.",
"device-type": "Τύπος συσκευής",

5
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -152,6 +152,7 @@
"password-expiration-period-days-range": "Password expiration period in days can't be negative",
"password-reuse-frequency-days": "Password reuse frequency in days",
"password-reuse-frequency-days-range": "Password reuse frequency in days can't be negative",
"allow-whitespace": "Allow whitespace",
"general-policy": "General policy",
"max-failed-login-attempts": "Maximum number of failed login attempts, before account is locked",
"minimum-max-failed-login-attempts-range": "Maximum number of failed login attempts can't be negative",
@ -980,8 +981,8 @@
"access-token": "Access token",
"access-token-required": "Access token is required.",
"access-token-invalid": "Access token length must be from 1 to 32 characters.",
"rsa-key": "RSA public key",
"rsa-key-required": "RSA public key is required.",
"certificate-pem-format": "Certificate in PEM format",
"certificate-pem-format-required": "Certificate is required.",
"lwm2m-security-config": {
"identity": "Client Identity",
"identity-required": "Client Identity is required.",

2
ui-ngx/src/assets/locale/locale.constant-es_ES.json

@ -894,8 +894,6 @@
"access-token": "Tóken de acceso",
"access-token-required": "Access token requerido.",
"access-token-invalid": "Access token debe tener entre 1 a 32 caracteres.",
"rsa-key": "Clave pública RSA",
"rsa-key-required": "Clave pública RSA requerida.",
"client-id": "ID Cliente",
"client-id-pattern": "Contiene carácter inválido.",
"user-name": "Nombre Usuario",

2
ui-ngx/src/assets/locale/locale.constant-fa_IR.json

@ -641,8 +641,6 @@
"access-token": "شناسه دسترسي",
"access-token-required": ".شناسه دسترسي مورد نياز است",
"access-token-invalid": ".طول شناسه دسترسي بايد از 1 تا 32 حرف باشد",
"rsa-key": "RSA کليد عمومي",
"rsa-key-required": ".مورد نياز است RSA کليد عمومي",
"secret": "محرمانه",
"secret-required": ".شناسه محرمانه مورد نياز است",
"device-type": "نوع دستگاه",

2
ui-ngx/src/assets/locale/locale.constant-fr_FR.json

@ -714,8 +714,6 @@
"no-keys-found": "Aucune clé trouvée",
"public": "Public",
"remove-alias": "Supprimer l'alias du dispositif",
"rsa-key": "Clé publique RSA",
"rsa-key-required": "La clé publique RSA est requise.",
"secret": "Secret",
"secret-required": "Code secret est requis.",
"select-device": "Selectionner un dispositif",

2
ui-ngx/src/assets/locale/locale.constant-it_IT.json

@ -666,8 +666,6 @@
"access-token": "Token di accesso",
"access-token-required": "Token di accesso obbligatorio.",
"access-token-invalid": "Il token di accesso deve avere una lunghezza compresa tra 1 e 32 caratteri.",
"rsa-key": "Chiave pubblica RSA",
"rsa-key-required": "Chiave pubblica RSA obbligatoria.",
"secret": "Secret",
"secret-required": "Secret obbligatorio.",
"device-type": "Tipo dispositivo",

2
ui-ngx/src/assets/locale/locale.constant-ja_JA.json

@ -624,8 +624,6 @@
"access-token": "アクセストークン",
"access-token-required": "アクセストークンが必要です。",
"access-token-invalid": "アクセストークンの長さは、1〜32文字でなければなりません。",
"rsa-key": "RSA公開鍵",
"rsa-key-required": "RSA公開鍵が必要です。",
"secret": "秘密",
"secret-required": "秘密が必要です。",
"device-type": "デバイスタイプ",

2
ui-ngx/src/assets/locale/locale.constant-ka_GE.json

@ -684,8 +684,6 @@
"access-token": "ტოკენი",
"access-token-required": "ტოკენი სავალდებულოა",
"access-token-invalid": "ტოკენის სიგრძე უნდა იყოს 1 დან 32 სიმბოლომდე",
"rsa-key": "ღია გასაღები RSA",
"rsa-key-required": "ღია გასაღები RSA აუცილებელია",
"secret": "საიდუმლო",
"secret-required": "საიდუმლო აუცილებელია",
"device-type": "მოწყობილობის ტიპი",

2
ui-ngx/src/assets/locale/locale.constant-ko_KR.json

@ -870,8 +870,6 @@
"access-token": "억세스 토큰",
"access-token-required": "액세스 토큰을 입력하세요.",
"access-token-invalid": "액세스 토큰 길이는 1 - 32 자 여야합니다.",
"rsa-key": "RSA public key",
"rsa-key-required": "RSA public key 를 입력하세요.",
"client-id": "클라이언트 ID",
"client-id-pattern": "잘못된 문자가 포함되어 있습니다.",
"user-name": "사용자 이름",

2
ui-ngx/src/assets/locale/locale.constant-lv_LV.json

@ -642,8 +642,6 @@
"access-token": "Piekļuves tokens",
"access-token-required": "Piekļuves tokens ir nepieciešams.",
"access-token-invalid": "Piekļuves tokena garumam ir jābūt no 1 līdz 32 rakstzīmēm.",
"rsa-key": "RSA publiskā atslēga",
"rsa-key-required": "RSA publiskā atslēga ir nepieciešama.",
"secret": "Noslēpums",
"secret-required": "Noslēpums ir nepieciešams.",
"device-type": "Iekārtas tips",

2
ui-ngx/src/assets/locale/locale.constant-pt_BR.json

@ -716,8 +716,6 @@
"access-token": "Token de acesso",
"access-token-required": "O token de acesso é obrigatório.",
"access-token-invalid": "O token de acesso deve ter de 1 a 32 caracteres.",
"rsa-key": "Chave pública RSA",
"rsa-key-required": "A chave pública RSA é obrigatória.",
"secret": "Segredo",
"secret-required": "O segredo é obrigatório.",
"device-type": "Tipo de dispositivo",

2
ui-ngx/src/assets/locale/locale.constant-ro_RO.json

@ -675,8 +675,6 @@
"access-token": "Token Acces",
"access-token-required": "Tokenul de acces este necesar",
"access-token-invalid": "Dimensiunea tokenului de acces trebuie să fie de 1-32 caractere",
"rsa-key": "Cheie RSA publică",
"rsa-key-required": "Cheia RSA publică key este necesară",
"secret": "Cod Secret",
"secret-required": "Codul secret este necesar",
"device-type": "Tip Dispozitiv",

2
ui-ngx/src/assets/locale/locale.constant-ru_RU.json

@ -678,8 +678,6 @@
"access-token": "Токен",
"access-token-required": "Токен обязателен.",
"access-token-invalid": "Длина токена должна быть от 1 до 32 символов.",
"rsa-key": "Открытый ключ RSA",
"rsa-key-required": "Открытый ключ RSA обязателен.",
"secret": "Секрет",
"secret-required": "Секрет обязателен.",
"device-type": "Тип устройства",

2
ui-ngx/src/assets/locale/locale.constant-sl_SI.json

@ -870,8 +870,6 @@
"access-token": "Dostopni žeton",
"access-token-required": "Zahtevan je žeton za dostop.",
"access-token-invalid": "Dolžina žetona za dostop mora biti od 1 do 32 znakov.",
"rsa-key": "Javni ključ RSA",
"rsa-key-required": "Potreben je javni ključ RSA.",
"client-id": "Client ID",
"client-id-pattern": "Contains invalid character.",
"user-name": "User Name",

2
ui-ngx/src/assets/locale/locale.constant-tr_TR.json

@ -957,8 +957,6 @@
"access-token": "Erişim şifresi",
"access-token-required": "Erişim şifresi gerekli.",
"access-token-invalid": "Erişim şifresi uzunluğu 1 ile 32 karakter arasında olmalıdır.",
"rsa-key": "RSA açık anahtarı",
"rsa-key-required": "RSA açık anahtarı gerekli.",
"lwm2m-security-config": {
"identity": "İstemci Kimliği",
"identity-required": "İstemci Kimliği gerekli.",

2
ui-ngx/src/assets/locale/locale.constant-uk_UA.json

@ -795,8 +795,6 @@
"access-token": "Маркер доступу",
"access-token-required": "Необхідно вказати маркер доступу.",
"access-token-invalid": "Маркер доступу має бути від 1 до 32 символів.",
"rsa-key": "Публічний ключ RSA",
"rsa-key-required": "Необхідно вказати публічний ключ RSA.",
"secret": "Секрет",
"secret-required": "Необхідно вказати секрет.",
"device-type": "Тип пристрою",

2
ui-ngx/src/assets/locale/locale.constant-zh_CN.json

@ -1081,8 +1081,6 @@
"password": "密码",
"public": "公开",
"remove-alias": "删除设备别名",
"rsa-key": "RSA公钥",
"rsa-key-required": "RSA公钥必填",
"search": "查找设备",
"secret": "密钥",
"secret-required": "密钥必填",

2
ui-ngx/src/assets/locale/locale.constant-zh_TW.json

@ -625,8 +625,6 @@
"access-token": "存取令牌",
"access-token-required": "需要存取令牌",
"access-token-invalid": "存取令牌長度必須為1到32個字符。",
"rsa-key": "RSA公鑰",
"rsa-key-required": "需要RSA公鑰",
"secret": "密鑰",
"secret-required": "密鑰必填",
"device-type": "設備類型",

Loading…
Cancel
Save