Browse Source

Implement fields validation

pull/4841/head
AndrewVolosytnykhThingsboard 5 years ago
parent
commit
e3d0da90be
  1. 3
      application/src/main/java/org/thingsboard/server/controller/AdminController.java
  2. 2
      application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
  3. 141
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  4. 12
      common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java
  5. 8
      common/data/src/main/java/org/thingsboard/server/common/data/Customer.java
  6. 2
      common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
  7. 4
      common/data/src/main/java/org/thingsboard/server/common/data/Device.java
  8. 2
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java
  9. 3
      common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java
  10. 9
      common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java
  11. 2
      common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java
  12. 2
      common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java
  13. 5
      common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java
  14. 2
      common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java
  15. 4
      common/data/src/main/java/org/thingsboard/server/common/data/User.java
  16. 2
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
  17. 5
      common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java
  18. 4
      common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java
  19. 3
      common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicKvEntry.java
  20. 1
      common/data/src/main/java/org/thingsboard/server/common/data/kv/StringDataEntry.java
  21. 6
      common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java
  22. 2
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java
  23. 3
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java
  24. 38
      common/data/src/main/java/org/thingsboard/server/common/data/validation/Length.java
  25. 4
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java
  26. 63
      dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java
  27. 45
      dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java
  28. 41
      dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java
  29. 3
      ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html
  30. 2
      ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.ts
  31. 8
      ui-ngx/src/app/modules/home/components/entity/contact-based.component.ts
  32. 3
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html
  33. 2
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts
  34. 3
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.html
  35. 2
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts
  36. 3
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html
  37. 2
      ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.ts
  38. 6
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html
  39. 4
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts
  40. 3
      ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html
  41. 23
      ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.ts
  42. 6
      ui-ngx/src/app/modules/home/pages/asset/asset.component.html
  43. 6
      ui-ngx/src/app/modules/home/pages/asset/asset.component.ts
  44. 3
      ui-ngx/src/app/modules/home/pages/customer/customer.component.html
  45. 2
      ui-ngx/src/app/modules/home/pages/customer/customer.component.ts
  46. 3
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html
  47. 2
      ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts
  48. 6
      ui-ngx/src/app/modules/home/pages/device/device.component.html
  49. 4
      ui-ngx/src/app/modules/home/pages/device/device.component.ts
  50. 9
      ui-ngx/src/app/modules/home/pages/edge/edge.component.html
  51. 6
      ui-ngx/src/app/modules/home/pages/edge/edge.component.ts
  52. 3
      ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.html
  53. 4
      ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.ts
  54. 12
      ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html
  55. 3
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.html
  56. 2
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.ts
  57. 3
      ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html
  58. 2
      ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts
  59. 3
      ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html
  60. 2
      ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.ts
  61. 9
      ui-ngx/src/app/shared/components/contact.component.html
  62. 3
      ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.html
  63. 15
      ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts
  64. 3
      ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.html
  65. 2
      ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.ts
  66. 30
      ui-ngx/src/assets/locale/locale.constant-en_US.json

3
application/src/main/java/org/thingsboard/server/controller/AdminController.java

@ -26,12 +26,12 @@ import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.rule.engine.api.SmsService;
import org.thingsboard.server.common.data.sms.config.TestSmsRequest;
import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.UpdateMessage; import org.thingsboard.server.common.data.UpdateMessage;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.model.SecuritySettings; import org.thingsboard.server.common.data.security.model.SecuritySettings;
import org.thingsboard.server.common.data.sms.config.TestSmsRequest;
import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
@ -88,6 +88,7 @@ public class AdminController extends BaseController {
} else if (adminSettings.getKey().equals("sms")) { } else if (adminSettings.getKey().equals("sms")) {
smsService.updateSmsConfiguration(); smsService.updateSmsConfiguration();
} }
return adminSettings; return adminSettings;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);

2
application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java

@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo; import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery; import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.service.ConstraintValidator;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Operation;
@ -63,6 +64,7 @@ public class EntityRelationController extends BaseController {
if (relation.getTypeGroup() == null) { if (relation.getTypeGroup() == null) {
relation.setTypeGroup(RelationTypeGroup.COMMON); relation.setTypeGroup(RelationTypeGroup.COMMON);
} }
ConstraintValidator.validateFields(relation);
relationService.saveRelation(getTenantId(), relation); relationService.saveRelation(getTenantId(), relation);
logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(), logEntityAction(relation.getFrom(), null, getCurrentUser().getCustomerId(),

141
application/src/main/java/org/thingsboard/server/controller/TelemetryController.java

@ -48,7 +48,6 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
@ -73,6 +72,7 @@ import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.dao.service.ConstraintValidator;
import org.thingsboard.server.dao.timeseries.TimeseriesService; import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.AccessValidator;
@ -138,7 +138,11 @@ public class TelemetryController extends BaseController {
@ResponseBody @ResponseBody
public DeferredResult<ResponseEntity> getAttributeKeys( public DeferredResult<ResponseEntity> getAttributeKeys(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); try {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback);
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -147,8 +151,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> getAttributeKeysByScope( public DeferredResult<ResponseEntity> getAttributeKeysByScope(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr
, @PathVariable("scope") String scope) throws ThingsboardException { , @PathVariable("scope") String scope) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, try {
(result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope));
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -157,9 +165,13 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> getAttributes( public DeferredResult<ResponseEntity> getAttributes(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser(); try {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, SecurityUser user = getCurrentUser();
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr));
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -169,9 +181,13 @@ public class TelemetryController extends BaseController {
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope, @PathVariable("scope") String scope,
@RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser(); try {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, SecurityUser user = getCurrentUser();
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr));
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -179,8 +195,12 @@ public class TelemetryController extends BaseController {
@ResponseBody @ResponseBody
public DeferredResult<ResponseEntity> getTimeseriesKeys( public DeferredResult<ResponseEntity> getTimeseriesKeys(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException { @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, try {
(result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor()));
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -190,10 +210,14 @@ public class TelemetryController extends BaseController {
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@RequestParam(name = "keys", required = false) String keysStr, @RequestParam(name = "keys", required = false) String keysStr,
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
SecurityUser user = getCurrentUser(); try {
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes));
} catch (Exception e) {
throw handleException(e);
}
} }
@ -211,15 +235,19 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "agg", defaultValue = "NONE") String aggStr, @RequestParam(name = "agg", defaultValue = "NONE") String aggStr,
@RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy,
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, try {
(result, tenantId, entityId) -> { return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
// If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted (result, tenantId, entityId) -> {
Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted
List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy)) Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr);
.collect(Collectors.toList()); List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy))
.collect(Collectors.toList());
Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor());
}); Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor());
});
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -227,8 +255,12 @@ public class TelemetryController extends BaseController {
@ResponseBody @ResponseBody
public DeferredResult<ResponseEntity> saveDeviceAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope, public DeferredResult<ResponseEntity> saveDeviceAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope,
@RequestBody JsonNode request) throws ThingsboardException { @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); try {
return saveAttributes(getTenantId(), entityId, scope, request); EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -237,8 +269,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityAttributesV1(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, public DeferredResult<ResponseEntity> saveEntityAttributesV1(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope, @PathVariable("scope") String scope,
@RequestBody JsonNode request) throws ThingsboardException { @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); try {
return saveAttributes(getTenantId(), entityId, scope, request); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -247,8 +283,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityAttributesV2(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, public DeferredResult<ResponseEntity> saveEntityAttributesV2(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope, @PathVariable("scope") String scope,
@RequestBody JsonNode request) throws ThingsboardException { @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); try {
return saveAttributes(getTenantId(), entityId, scope, request); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -257,8 +297,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityTelemetry(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, public DeferredResult<ResponseEntity> saveEntityTelemetry(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope, @PathVariable("scope") String scope,
@RequestBody String requestBody) throws ThingsboardException { @RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); try {
return saveTelemetry(getTenantId(), entityId, requestBody, 0L); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveTelemetry(getTenantId(), entityId, requestBody, 0L);
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -267,8 +311,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityTelemetryWithTTL(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, public DeferredResult<ResponseEntity> saveEntityTelemetryWithTTL(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope, @PathVariable("ttl") Long ttl, @PathVariable("scope") String scope, @PathVariable("ttl") Long ttl,
@RequestBody String requestBody) throws ThingsboardException { @RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); try {
return saveTelemetry(getTenantId(), entityId, requestBody, ttl); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveTelemetry(getTenantId(), entityId, requestBody, ttl);
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -280,8 +328,12 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "startTs", required = false) Long startTs, @RequestParam(name = "startTs", required = false) Long startTs,
@RequestParam(name = "endTs", required = false) Long endTs, @RequestParam(name = "endTs", required = false) Long endTs,
@RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); try {
return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted);
} catch (Exception e) {
throw handleException(e);
}
} }
private DeferredResult<ResponseEntity> deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, private DeferredResult<ResponseEntity> deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys,
@ -335,8 +387,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr, public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr,
@PathVariable("scope") String scope, @PathVariable("scope") String scope,
@RequestParam(name = "keys") String keysStr) throws ThingsboardException { @RequestParam(name = "keys") String keysStr) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); try {
return deleteAttributes(entityId, scope, keysStr); EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return deleteAttributes(entityId, scope, keysStr);
} catch (Exception e) {
throw handleException(e);
}
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -345,8 +401,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr, public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope, @PathVariable("scope") String scope,
@RequestParam(name = "keys") String keysStr) throws ThingsboardException { @RequestParam(name = "keys") String keysStr) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); try {
return deleteAttributes(entityId, scope, keysStr); EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return deleteAttributes(entityId, scope, keysStr);
} catch (Exception e) {
throw handleException(e);
}
} }
private DeferredResult<ResponseEntity> deleteAttributes(EntityId entityIdSrc, String scope, String keysStr) throws ThingsboardException { private DeferredResult<ResponseEntity> deleteAttributes(EntityId entityIdSrc, String scope, String keysStr) throws ThingsboardException {
@ -392,6 +452,7 @@ public class TelemetryController extends BaseController {
} }
if (json.isObject()) { if (json.isObject()) {
List<AttributeKvEntry> attributes = extractRequestAttributes(json); List<AttributeKvEntry> attributes = extractRequestAttributes(json);
attributes.forEach(ConstraintValidator::validateFields);
if (attributes.isEmpty()) { if (attributes.isEmpty()) {
return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST);
} }

12
common/data/src/main/java/org/thingsboard/server/common/data/ContactBased.java

@ -17,30 +17,36 @@ package org.thingsboard.server.common.data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public abstract class ContactBased<I extends UUIDBased> extends SearchTextBasedWithAdditionalInfo<I> implements HasName { public abstract class ContactBased<I extends UUIDBased> extends SearchTextBasedWithAdditionalInfo<I> implements HasName {
private static final long serialVersionUID = 5047448057830660988L; private static final long serialVersionUID = 5047448057830660988L;
@Length(fieldName = "country")
@NoXss @NoXss
protected String country; protected String country;
@Length(fieldName = "state")
@NoXss @NoXss
protected String state; protected String state;
@Length(fieldName = "city")
@NoXss @NoXss
protected String city; protected String city;
@NoXss @NoXss
protected String address; protected String address;
@NoXss @NoXss
protected String address2; protected String address2;
@Length(fieldName = "zip or postal code")
@NoXss @NoXss
protected String zip; protected String zip;
@Length(fieldName = "phone")
@NoXss @NoXss
protected String phone; protected String phone;
@NoXss @NoXss
protected String email; protected String email;
public ContactBased() { public ContactBased() {
super(); super();
} }
@ -48,7 +54,7 @@ public abstract class ContactBased<I extends UUIDBased> extends SearchTextBasedW
public ContactBased(I id) { public ContactBased(I id) {
super(id); super(id);
} }
public ContactBased(ContactBased<I> contact) { public ContactBased(ContactBased<I> contact) {
super(contact); super(contact);
this.country = contact.getCountry(); this.country = contact.getCountry();

8
common/data/src/main/java/org/thingsboard/server/common/data/Customer.java

@ -20,13 +20,15 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonProperty.Access; import com.fasterxml.jackson.annotation.JsonProperty.Access;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
public class Customer extends ContactBased<CustomerId> implements HasTenantId { public class Customer extends ContactBased<CustomerId> implements HasTenantId {
private static final long serialVersionUID = -1599722990298929275L; private static final long serialVersionUID = -1599722990298929275L;
@NoXss @NoXss
@Length(fieldName = "title")
private String title; private String title;
private TenantId tenantId; private TenantId tenantId;
@ -37,7 +39,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId {
public Customer(CustomerId id) { public Customer(CustomerId id) {
super(id); super(id);
} }
public Customer(Customer customer) { public Customer(Customer customer) {
super(customer); super(customer);
this.tenantId = customer.getTenantId(); this.tenantId = customer.getTenantId();
@ -51,7 +53,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId {
public void setTenantId(TenantId tenantId) { public void setTenantId(TenantId tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }
public String getTitle() { public String getTitle() {
return title; return title;
} }

2
common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java

@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
import javax.validation.Valid; import javax.validation.Valid;
@ -29,6 +30,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
private TenantId tenantId; private TenantId tenantId;
@NoXss @NoXss
@Length(fieldName = "title")
private String title; private String title;
private String image; private String image;
@Valid @Valid

4
common/data/src/main/java/org/thingsboard/server/common/data/Device.java

@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -39,10 +40,13 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
private TenantId tenantId; private TenantId tenantId;
private CustomerId customerId; private CustomerId customerId;
@NoXss @NoXss
@Length(fieldName = "name")
private String name; private String name;
@NoXss @NoXss
@Length(fieldName = "type")
private String type; private String type;
@NoXss @NoXss
@Length(fieldName = "label")
private String label; private String label;
private DeviceProfileId deviceProfileId; private DeviceProfileId deviceProfileId;
private transient DeviceData deviceData; private transient DeviceData deviceData;

2
common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
import javax.validation.Valid; import javax.validation.Valid;
@ -41,6 +42,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
private TenantId tenantId; private TenantId tenantId;
@NoXss @NoXss
@Length(fieldName = "name")
private String name; private String name;
@NoXss @NoXss
private String description; private String description;

3
common/data/src/main/java/org/thingsboard/server/common/data/EntityView.java

@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.objects.TelemetryEntityView; import org.thingsboard.server.common.data.objects.TelemetryEntityView;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
/** /**
@ -41,8 +42,10 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
private TenantId tenantId; private TenantId tenantId;
private CustomerId customerId; private CustomerId customerId;
@NoXss @NoXss
@Length(fieldName = "name")
private String name; private String name;
@NoXss @NoXss
@Length(fieldName = "type")
private String type; private String type;
private TelemetryEntityView keys; private TelemetryEntityView keys;
private long startTimeMs; private long startTimeMs;

9
common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java

@ -19,11 +19,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.validation.Length;
@Slf4j @Slf4j
@Data @Data
@ -35,10 +36,14 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage
private TenantId tenantId; private TenantId tenantId;
private DeviceProfileId deviceProfileId; private DeviceProfileId deviceProfileId;
private OtaPackageType type; private OtaPackageType type;
@Length(fieldName = "title")
private String title; private String title;
@Length(fieldName = "version")
private String version; private String version;
@Length(fieldName = "url")
private String url; private String url;
private boolean hasData; private boolean hasData;
@Length(fieldName = "file name")
private String fileName; private String fileName;
private String contentType; private String contentType;
private ChecksumAlgorithm checksumAlgorithm; private ChecksumAlgorithm checksumAlgorithm;

2
common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java

@ -19,6 +19,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
@Slf4j @Slf4j
@ -29,6 +30,7 @@ public class TbResource extends TbResourceInfo {
private static final long serialVersionUID = 7379609705527272306L; private static final long serialVersionUID = 7379609705527272306L;
@NoXss @NoXss
@Length(fieldName = "file name")
private String fileName; private String fileName;
private String data; private String data;

2
common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java

@ -21,6 +21,7 @@ import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
@Slf4j @Slf4j
@ -32,6 +33,7 @@ public class TbResourceInfo extends SearchTextBased<TbResourceId> implements Has
private TenantId tenantId; private TenantId tenantId;
@NoXss @NoXss
@Length(fieldName = "title")
private String title; private String title;
private ResourceType resourceType; private ResourceType resourceType;
private String resourceKey; private String resourceKey;

5
common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java

@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ -27,8 +28,10 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId {
private static final long serialVersionUID = 8057243243859922101L; private static final long serialVersionUID = 8057243243859922101L;
@Length(fieldName = "title")
@NoXss @NoXss
private String title; private String title;
@Length(fieldName = "region")
@NoXss @NoXss
private String region; private String region;
private TenantProfileId tenantProfileId; private TenantProfileId tenantProfileId;
@ -40,7 +43,7 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId {
public Tenant(TenantId id) { public Tenant(TenantId id) {
super(id); super(id);
} }
public Tenant(Tenant tenant) { public Tenant(Tenant tenant) {
super(tenant); super(tenant);
this.title = tenant.getTitle(); this.title = tenant.getTitle();

2
common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java

@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; import org.thingsboard.server.common.data.tenant.profile.TenantProfileData;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -37,6 +38,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn
public class TenantProfile extends SearchTextBased<TenantProfileId> implements HasName { public class TenantProfile extends SearchTextBased<TenantProfileId> implements HasName {
@NoXss @NoXss
@Length(fieldName = "name")
private String name; private String name;
@NoXss @NoXss
private String description; private String description;

4
common/data/src/main/java/org/thingsboard/server/common/data/User.java

@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ -36,8 +36,10 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H
private String email; private String email;
private Authority authority; private Authority authority;
@NoXss @NoXss
@Length(fieldName = "firs name")
private String firstName; private String firstName;
@NoXss @NoXss
@Length(fieldName = "last name")
private String lastName; private String lastName;
public User() { public User() {

2
common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java

@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import java.util.List; import java.util.List;
@ -41,6 +42,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
private TenantId tenantId; private TenantId tenantId;
private CustomerId customerId; private CustomerId customerId;
@Length(fieldName = "type")
private String type; private String type;
private EntityId originator; private EntityId originator;
private AlarmSeverity severity; private AlarmSeverity severity;

5
common/data/src/main/java/org/thingsboard/server/common/data/edge/Edge.java

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.server.common.data.edge; package org.thingsboard.server.common.data.edge;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -28,6 +27,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString @ToString
@ -40,8 +40,11 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
private TenantId tenantId; private TenantId tenantId;
private CustomerId customerId; private CustomerId customerId;
private RuleChainId rootRuleChainId; private RuleChainId rootRuleChainId;
@Length(fieldName = "name")
private String name; private String name;
@Length(fieldName = "type")
private String type; private String type;
@Length(fieldName = "label")
private String label; private String label;
private String routingKey; private String routingKey;
private String secret; private String secret;

4
common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java

@ -15,8 +15,7 @@
*/ */
package org.thingsboard.server.common.data.kv; package org.thingsboard.server.common.data.kv;
import com.fasterxml.jackson.databind.JsonNode; import javax.validation.Valid;
import java.util.Optional; import java.util.Optional;
/** /**
@ -25,6 +24,7 @@ import java.util.Optional;
public class BaseAttributeKvEntry implements AttributeKvEntry { public class BaseAttributeKvEntry implements AttributeKvEntry {
private final long lastUpdateTs; private final long lastUpdateTs;
@Valid
private final KvEntry kv; private final KvEntry kv;
public BaseAttributeKvEntry(KvEntry kv, long lastUpdateTs) { public BaseAttributeKvEntry(KvEntry kv, long lastUpdateTs) {

3
common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicKvEntry.java

@ -15,11 +15,14 @@
*/ */
package org.thingsboard.server.common.data.kv; package org.thingsboard.server.common.data.kv;
import org.thingsboard.server.common.data.validation.Length;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
public abstract class BasicKvEntry implements KvEntry { public abstract class BasicKvEntry implements KvEntry {
@Length(fieldName = "attribute key")
private final String key; private final String key;
protected BasicKvEntry(String key) { protected BasicKvEntry(String key) {

1
common/data/src/main/java/org/thingsboard/server/common/data/kv/StringDataEntry.java

@ -21,6 +21,7 @@ import java.util.Optional;
public class StringDataEntry extends BasicKvEntry { public class StringDataEntry extends BasicKvEntry {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final String value; private final String value;
public StringDataEntry(String key, String value) { public StringDataEntry(String key, String value) {

6
common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java

@ -16,15 +16,12 @@
package org.thingsboard.server.common.data.relation; package org.thingsboard.server.common.data.relation;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.validation.Length;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
@Slf4j @Slf4j
@ -38,6 +35,7 @@ public class EntityRelation implements Serializable {
private EntityId from; private EntityId from;
private EntityId to; private EntityId to;
@Length(fieldName = "type")
private String type; private String type;
private RelationTypeGroup typeGroup; private RelationTypeGroup typeGroup;
private transient JsonNode additionalInfo; private transient JsonNode additionalInfo;

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

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
@Data @Data
@ -37,6 +38,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
private TenantId tenantId; private TenantId tenantId;
@NoXss @NoXss
@Length(fieldName = "name")
private String name; private String name;
private RuleChainType type; private RuleChainType type;
private RuleNodeId firstRuleNodeId; private RuleNodeId firstRuleNodeId;

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

@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.validation.Length;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ -33,7 +34,9 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo<RuleNodeId> impl
private static final long serialVersionUID = -5656679015121235465L; private static final long serialVersionUID = -5656679015121235465L;
private RuleChainId ruleChainId; private RuleChainId ruleChainId;
@Length(fieldName = "type")
private String type; private String type;
@Length(fieldName = "name")
private String name; private String name;
private boolean debugMode; private boolean debugMode;
private transient JsonNode configuration; private transient JsonNode configuration;

38
common/data/src/main/java/org/thingsboard/server/common/data/validation/Length.java

@ -0,0 +1,38 @@
/**
* 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.common.data.validation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Constraint(validatedBy = {})
public @interface Length {
String message() default "length of {fieldName} should be equals or less than {max}";
String fieldName();
int max() default 255;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

4
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java

@ -19,10 +19,9 @@ import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.SearchTextBased; import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.id.WidgetsBundleId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.common.data.validation.NoXss;
import java.util.Arrays;
public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements HasTenantId { public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements HasTenantId {
private static final long serialVersionUID = -7627368878362410489L; private static final long serialVersionUID = -7627368878362410489L;
@ -31,6 +30,7 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H
@NoXss @NoXss
private String alias; private String alias;
@NoXss @NoXss
@Length(fieldName = "title")
private String title; private String title;
private String image; private String image;
@NoXss @NoXss

63
dao/src/main/java/org/thingsboard/server/dao/service/ConstraintValidator.java

@ -0,0 +1,63 @@
/**
* 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.dao.service;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.HibernateValidatorConfiguration;
import org.hibernate.validator.cfg.ConstraintMapping;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
public class ConstraintValidator {
private static Validator fieldsValidator;
static {
initializeValidators();
}
public static void validateFields(Object data) {
Set<ConstraintViolation<Object>> constraintsViolations = fieldsValidator.validate(data);
List<String> validationErrors = constraintsViolations.stream()
.map(ConstraintViolation::getMessage)
.distinct()
.collect(Collectors.toList());
if (!validationErrors.isEmpty()) {
throw new ValidationException("Validation error: " + String.join(", ", validationErrors));
}
}
private static void initializeValidators() {
HibernateValidatorConfiguration validatorConfiguration = Validation.byProvider(HibernateValidator.class).configure();
ConstraintMapping constraintMapping = validatorConfiguration.createConstraintMapping();
constraintMapping.constraintDefinition(NoXss.class).validatedBy(NoXssValidator.class);
constraintMapping.constraintDefinition(Length.class).validatedBy(StringLengthValidator.class);
validatorConfiguration.addMapping(constraintMapping);
fieldsValidator = validatorConfiguration.buildValidatorFactory().getValidator();
}
}

45
dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java

@ -17,51 +17,32 @@ package org.thingsboard.server.dao.service;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.HibernateValidatorConfiguration;
import org.hibernate.validator.cfg.ConstraintMapping;
import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.common.data.validation.NoXss;
import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.dao.TenantEntityDao;
import org.thingsboard.server.dao.TenantEntityWithDataDao; import org.thingsboard.server.dao.TenantEntityWithDataDao;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Slf4j @Slf4j
public abstract class DataValidator<D extends BaseData<?>> { public abstract class DataValidator<D extends BaseData<?>> {
private static final Pattern EMAIL_PATTERN = private static final Pattern EMAIL_PATTERN =
Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE); Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE);
private static Validator fieldsValidator;
static {
initializeFieldsValidator();
}
public void validate(D data, Function<D, TenantId> tenantIdFunction) { public void validate(D data, Function<D, TenantId> tenantIdFunction) {
try { try {
if (data == null) { if (data == null) {
throw new DataValidationException("Data object can't be null!"); throw new DataValidationException("Data object can't be null!");
} }
List<String> validationErrors = validateFields(data); ConstraintValidator.validateFields(data);
if (!validationErrors.isEmpty()) {
throw new IllegalArgumentException("Validation error: " + String.join(", ", validationErrors));
}
TenantId tenantId = tenantIdFunction.apply(data); TenantId tenantId = tenantIdFunction.apply(data);
validateDataImpl(tenantId, data); validateDataImpl(tenantId, data);
@ -104,14 +85,6 @@ public abstract class DataValidator<D extends BaseData<?>> {
return emailMatcher.matches(); return emailMatcher.matches();
} }
private List<String> validateFields(D data) {
Set<ConstraintViolation<D>> constraintsViolations = fieldsValidator.validate(data);
return constraintsViolations.stream()
.map(ConstraintViolation::getMessage)
.distinct()
.collect(Collectors.toList());
}
protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, protected void validateNumberOfEntitiesPerTenant(TenantId tenantId,
TenantEntityDao tenantEntityDao, TenantEntityDao tenantEntityDao,
long maxEntities, long maxEntities,
@ -126,10 +99,10 @@ public abstract class DataValidator<D extends BaseData<?>> {
} }
protected void validateMaxSumDataSizePerTenant(TenantId tenantId, protected void validateMaxSumDataSizePerTenant(TenantId tenantId,
TenantEntityWithDataDao dataDao, TenantEntityWithDataDao dataDao,
long maxSumDataSize, long maxSumDataSize,
long currentDataSize, long currentDataSize,
EntityType entityType) { EntityType entityType) {
if (maxSumDataSize > 0) { if (maxSumDataSize > 0) {
if (dataDao.sumDataSizeByTenantId(tenantId) + currentDataSize > maxSumDataSize) { if (dataDao.sumDataSizeByTenantId(tenantId) + currentDataSize > maxSumDataSize) {
throw new DataValidationException(String.format("Failed to create the %s, files size limit is exhausted %d bytes!", throw new DataValidationException(String.format("Failed to create the %s, files size limit is exhausted %d bytes!",
@ -156,12 +129,4 @@ public abstract class DataValidator<D extends BaseData<?>> {
} }
} }
private static void initializeFieldsValidator() {
HibernateValidatorConfiguration validatorConfiguration = Validation.byProvider(HibernateValidator.class).configure();
ConstraintMapping constraintMapping = validatorConfiguration.createConstraintMapping();
constraintMapping.constraintDefinition(NoXss.class).validatedBy(NoXssValidator.class);
validatorConfiguration.addMapping(constraintMapping);
fieldsValidator = validatorConfiguration.buildValidatorFactory().getValidator();
}
} }

41
dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java

@ -0,0 +1,41 @@
/**
* 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.dao.service;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.validation.Length;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
@Slf4j
public class StringLengthValidator implements ConstraintValidator<Length, String> {
private int max;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StringUtils.isEmpty(value)) {
return true;
}
return value.trim().length() <= max;
}
@Override
public void initialize(Length constraintAnnotation) {
this.max = constraintAnnotation.max();
}
}

3
ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.html

@ -36,6 +36,9 @@
<mat-error *ngIf="attributeFormGroup.get('key').hasError('required')"> <mat-error *ngIf="attributeFormGroup.get('key').hasError('required')">
{{ 'attribute.key-required' | translate }} {{ 'attribute.key-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="attributeFormGroup.get('key').hasError('maxlength')">
{{ 'attribute.key-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-value-input <tb-value-input
formControlName="value" formControlName="value"

2
ui-ngx/src/app/modules/home/components/attribute/add-attribute-dialog.component.ts

@ -56,7 +56,7 @@ export class AddAttributeDialogComponent extends DialogComponent<AddAttributeDia
ngOnInit(): void { ngOnInit(): void {
this.attributeFormGroup = this.fb.group({ this.attributeFormGroup = this.fb.group({
key: ['', [Validators.required]], key: ['', [Validators.required, Validators.maxLength(255)]],
value: [null, [Validators.required]] value: [null, [Validators.required]]
}); });
} }

8
ui-ngx/src/app/modules/home/components/entity/contact-based.component.ts

@ -36,15 +36,15 @@ export abstract class ContactBasedComponent<T extends ContactBased<HasId>> exten
buildForm(entity: T): FormGroup { buildForm(entity: T): FormGroup {
const entityForm = this.buildEntityForm(entity); const entityForm = this.buildEntityForm(entity);
entityForm.addControl('country', this.fb.control(entity ? entity.country : '', [])); entityForm.addControl('country', this.fb.control(entity ? entity.country : '', [Validators.maxLength(255)]));
entityForm.addControl('city', this.fb.control(entity ? entity.city : '', [])); entityForm.addControl('city', this.fb.control(entity ? entity.city : '', [Validators.maxLength(255)]));
entityForm.addControl('state', this.fb.control(entity ? entity.state : '', [])); entityForm.addControl('state', this.fb.control(entity ? entity.state : '', [Validators.maxLength(255)]));
entityForm.addControl('zip', this.fb.control(entity ? entity.zip : '', entityForm.addControl('zip', this.fb.control(entity ? entity.zip : '',
this.zipValidators(entity ? entity.country : '') this.zipValidators(entity ? entity.country : '')
)); ));
entityForm.addControl('address', this.fb.control(entity ? entity.address : '', [])); entityForm.addControl('address', this.fb.control(entity ? entity.address : '', []));
entityForm.addControl('address2', this.fb.control(entity ? entity.address2 : '', [])); entityForm.addControl('address2', this.fb.control(entity ? entity.address2 : '', []));
entityForm.addControl('phone', this.fb.control(entity ? entity.phone : '', [])); entityForm.addControl('phone', this.fb.control(entity ? entity.phone : '', [Validators.maxLength(255)]));
entityForm.addControl('email', this.fb.control(entity ? entity.email : '', [Validators.email])); entityForm.addControl('email', this.fb.control(entity ? entity.email : '', [Validators.email]));
return entityForm; return entityForm;
} }

3
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html

@ -41,6 +41,9 @@
<mat-error *ngIf="deviceProfileDetailsFormGroup.get('name').hasError('required')"> <mat-error *ngIf="deviceProfileDetailsFormGroup.get('name').hasError('required')">
{{ 'device-profile.name-required' | translate }} {{ 'device-profile.name-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="deviceProfileDetailsFormGroup.get('name').hasError('maxlength')">
{{ 'device-profile.name-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-rule-chain-autocomplete <tb-rule-chain-autocomplete
labelText="device-profile.default-rule-chain" labelText="device-profile.default-rule-chain"

2
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts

@ -105,7 +105,7 @@ export class AddDeviceProfileDialogComponent extends
super(store, router, dialogRef); super(store, router, dialogRef);
this.deviceProfileDetailsFormGroup = this.fb.group( this.deviceProfileDetailsFormGroup = this.fb.group(
{ {
name: [data.deviceProfileName, [Validators.required]], name: [data.deviceProfileName, [Validators.required, Validators.maxLength(255)]],
type: [DeviceProfileType.DEFAULT, [Validators.required]], type: [DeviceProfileType.DEFAULT, [Validators.required]],
image: [null, []], image: [null, []],
defaultRuleChainId: [null, []], defaultRuleChainId: [null, []],

3
ui-ngx/src/app/modules/home/components/profile/device-profile.component.html

@ -54,6 +54,9 @@
<mat-error *ngIf="entityForm.get('name').hasError('required')"> <mat-error *ngIf="entityForm.get('name').hasError('required')">
{{ 'device-profile.name-required' | translate }} {{ 'device-profile.name-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'device-profile.name-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-rule-chain-autocomplete <tb-rule-chain-autocomplete
labelText="device-profile.default-rule-chain" labelText="device-profile.default-rule-chain"

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

@ -102,7 +102,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
}; };
const form = this.fb.group( const form = this.fb.group(
{ {
name: [entity ? entity.name : '', [Validators.required]], name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
type: [entity ? entity.type : null, [Validators.required]], type: [entity ? entity.type : null, [Validators.required]],
image: [entity ? entity.image : null], image: [entity ? entity.image : null],
transportType: [entity ? entity.transportType : null, [Validators.required]], transportType: [entity ? entity.transportType : null, [Validators.required]],

3
ui-ngx/src/app/modules/home/components/profile/tenant-profile.component.html

@ -48,6 +48,9 @@
<mat-error *ngIf="entityForm.get('name').hasError('required')"> <mat-error *ngIf="entityForm.get('name').hasError('required')">
{{ 'tenant-profile.name-required' | translate }} {{ 'tenant-profile.name-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'tenant-profile.name-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<div fxLayout="column"> <div fxLayout="column">
<mat-checkbox class="hinted-checkbox" formControlName="isolatedTbCore"> <mat-checkbox class="hinted-checkbox" formControlName="isolatedTbCore">

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

@ -58,7 +58,7 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> {
buildForm(entity: TenantProfile): FormGroup { buildForm(entity: TenantProfile): FormGroup {
return this.fb.group( return this.fb.group(
{ {
name: [entity ? entity.name : '', [Validators.required]], name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
isolatedTbCore: [entity ? entity.isolatedTbCore : false, []], isolatedTbCore: [entity ? entity.isolatedTbCore : false, []],
isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []],
profileData: [entity && !this.isAdd ? entity.profileData : { profileData: [entity && !this.isAdd ? entity.profileData : {

6
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html

@ -44,10 +44,16 @@
<mat-error *ngIf="deviceWizardFormGroup.get('name').hasError('required')"> <mat-error *ngIf="deviceWizardFormGroup.get('name').hasError('required')">
{{ 'device.name-required' | translate }} {{ 'device.name-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="deviceWizardFormGroup.get('name').hasError('maxlength')">
{{ 'device.name-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>device.label</mat-label> <mat-label translate>device.label</mat-label>
<input matInput formControlName="label"> <input matInput formControlName="label">
<mat-error *ngIf="deviceWizardFormGroup.get('label').hasError('maxlength')">
{{ 'device.label-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<div fxLayout="row" fxLayoutGap="16px"> <div fxLayout="row" fxLayoutGap="16px">
<mat-radio-group fxLayout="column" formControlName="addProfileType" fxLayoutAlign="space-around"> <mat-radio-group fxLayout="column" formControlName="addProfileType" fxLayoutAlign="space-around">

4
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts

@ -105,8 +105,8 @@ export class DeviceWizardDialogComponent extends
private fb: FormBuilder) { private fb: FormBuilder) {
super(store, router, dialogRef); super(store, router, dialogRef);
this.deviceWizardFormGroup = this.fb.group({ this.deviceWizardFormGroup = this.fb.group({
name: ['', Validators.required], name: ['', [Validators.required, Validators.maxLength(255)]],
label: [''], label: ['', Validators.maxLength(255)],
gateway: [false], gateway: [false],
overwriteActivityTime: [false], overwriteActivityTime: [false],
addProfileType: [0], addProfileType: [0],

3
ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.html

@ -56,6 +56,9 @@
<mat-error *ngIf="entityForm.get('title').hasError('required')"> <mat-error *ngIf="entityForm.get('title').hasError('required')">
{{ 'resource.title-required' | translate }} {{ 'resource.title-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'resource.title-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-file-input *ngIf="isAdd" <tb-file-input *ngIf="isAdd"
formControlName="data" formControlName="data"

23
ui-ngx/src/app/modules/home/pages/admin/resource/resources-library.component.ts

@ -29,7 +29,7 @@ import {
ResourceTypeMIMETypes, ResourceTypeMIMETypes,
ResourceTypeTranslationMap ResourceTypeTranslationMap
} from '@shared/models/resource.models'; } from '@shared/models/resource.models';
import { pairwise, startWith, takeUntil } from 'rxjs/operators'; import {filter, pairwise, startWith, takeUntil} from 'rxjs/operators';
import { ActionNotificationShow } from "@core/notification/notification.actions"; import { ActionNotificationShow } from "@core/notification/notification.actions";
@Component({ @Component({
@ -56,16 +56,14 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
super.ngOnInit(); super.ngOnInit();
this.entityForm.get('resourceType').valueChanges.pipe( this.entityForm.get('resourceType').valueChanges.pipe(
startWith(ResourceType.LWM2M_MODEL), startWith(ResourceType.LWM2M_MODEL),
pairwise(), filter(() => this.isAdd),
takeUntil(this.destroy$) takeUntil(this.destroy$)
).subscribe(([previousType, type]) => { ).subscribe((type) => {
if (previousType === this.resourceType.LWM2M_MODEL) {
this.entityForm.get('title').setValidators(Validators.required);
this.entityForm.get('title').updateValueAndValidity({emitEvent: false});
}
if (type === this.resourceType.LWM2M_MODEL) { if (type === this.resourceType.LWM2M_MODEL) {
this.entityForm.get('title').clearValidators(); this.entityForm.get('title').disable({emitEvent: false});
this.entityForm.get('title').updateValueAndValidity({emitEvent: false}); this.entityForm.patchValue({title: ''}, {emitEvent: false});
} else {
this.entityForm.get('title').enable({emitEvent: false})
} }
this.entityForm.patchValue({ this.entityForm.patchValue({
data: null, data: null,
@ -91,11 +89,8 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
buildForm(entity: Resource): FormGroup { buildForm(entity: Resource): FormGroup {
const form = this.fb.group( const form = this.fb.group(
{ {
title: [entity ? entity.title : '', []], title: [entity ? entity.title : "", [Validators.required, Validators.maxLength(255)]],
resourceType: [{ resourceType: [entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, [Validators.required]],
value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL,
disabled: !this.isAdd
}, [Validators.required]],
fileName: [entity ? entity.fileName : null, [Validators.required]], fileName: [entity ? entity.fileName : null, [Validators.required]],
} }
); );

6
ui-ngx/src/app/modules/home/pages/asset/asset.component.html

@ -76,6 +76,9 @@
<mat-error *ngIf="entityForm.get('name').hasError('required')"> <mat-error *ngIf="entityForm.get('name').hasError('required')">
{{ 'asset.name-required' | translate }} {{ 'asset.name-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'asset.name-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-entity-subtype-autocomplete <tb-entity-subtype-autocomplete
formControlName="type" formControlName="type"
@ -86,6 +89,9 @@
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>asset.label</mat-label> <mat-label translate>asset.label</mat-label>
<input matInput formControlName="label"> <input matInput formControlName="label">
<mat-error *ngIf="entityForm.get('label').hasError('maxlength')">
{{ 'asset.label-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<div formGroupName="additionalInfo"> <div formGroupName="additionalInfo">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">

6
ui-ngx/src/app/modules/home/pages/asset/asset.component.ts

@ -65,9 +65,9 @@ export class AssetComponent extends EntityComponent<AssetInfo> {
buildForm(entity: AssetInfo): FormGroup { buildForm(entity: AssetInfo): FormGroup {
return this.fb.group( return this.fb.group(
{ {
name: [entity ? entity.name : '', [Validators.required]], name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
type: [entity ? entity.type : null, [Validators.required]], type: [entity ? entity.type : null, [Validators.required, Validators.maxLength(255)]],
label: [entity ? entity.label : ''], label: [entity ? entity.label : '', Validators.maxLength(255)],
additionalInfo: this.fb.group( additionalInfo: this.fb.group(
{ {
description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''],

3
ui-ngx/src/app/modules/home/pages/customer/customer.component.html

@ -73,6 +73,9 @@
<mat-error *ngIf="entityForm.get('title').hasError('required')"> <mat-error *ngIf="entityForm.get('title').hasError('required')">
{{ 'customer.title-required' | translate }} {{ 'customer.title-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'customer.title-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<div formGroupName="additionalInfo" fxLayout="column"> <div formGroupName="additionalInfo" fxLayout="column">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">

2
ui-ngx/src/app/modules/home/pages/customer/customer.component.ts

@ -57,7 +57,7 @@ export class CustomerComponent extends ContactBasedComponent<Customer> {
buildEntityForm(entity: Customer): FormGroup { buildEntityForm(entity: Customer): FormGroup {
return this.fb.group( return this.fb.group(
{ {
title: [entity ? entity.title : '', [Validators.required]], title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
additionalInfo: this.fb.group( additionalInfo: this.fb.group(
{ {
description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''],

3
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.html

@ -104,6 +104,9 @@
<mat-error *ngIf="entityForm.get('title').hasError('required')"> <mat-error *ngIf="entityForm.get('title').hasError('required')">
{{ 'dashboard.title-required' | translate }} {{ 'dashboard.title-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'dashboard.title-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-image-input fxFlex <tb-image-input fxFlex
label="{{'dashboard.image' | translate}}" label="{{'dashboard.image' | translate}}"

2
ui-ngx/src/app/modules/home/pages/dashboard/dashboard-form.component.ts

@ -79,7 +79,7 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> {
this.updateFields(entity); this.updateFields(entity);
return this.fb.group( return this.fb.group(
{ {
title: [entity ? entity.title : '', [Validators.required]], title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
image: [entity ? entity.image : null], image: [entity ? entity.image : null],
configuration: this.fb.group( configuration: this.fb.group(
{ {

6
ui-ngx/src/app/modules/home/pages/device/device.component.html

@ -89,6 +89,9 @@
<mat-error *ngIf="entityForm.get('name').hasError('required')"> <mat-error *ngIf="entityForm.get('name').hasError('required')">
{{ 'device.name-required' | translate }} {{ 'device.name-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'device.name-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-device-profile-autocomplete <tb-device-profile-autocomplete
[selectDefaultProfile]="isAdd" [selectDefaultProfile]="isAdd"
@ -100,6 +103,9 @@
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>device.label</mat-label> <mat-label translate>device.label</mat-label>
<input matInput formControlName="label"> <input matInput formControlName="label">
<mat-error *ngIf="entityForm.get('label').hasError('maxlength')">
{{ 'device.label-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-ota-package-autocomplete <tb-ota-package-autocomplete
[useFullEntityId]="true" [useFullEntityId]="true"

4
ui-ngx/src/app/modules/home/pages/device/device.component.ts

@ -81,11 +81,11 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
buildForm(entity: DeviceInfo): FormGroup { buildForm(entity: DeviceInfo): FormGroup {
const form = this.fb.group( const form = this.fb.group(
{ {
name: [entity ? entity.name : '', [Validators.required]], name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
deviceProfileId: [entity ? entity.deviceProfileId : null, [Validators.required]], deviceProfileId: [entity ? entity.deviceProfileId : null, [Validators.required]],
firmwareId: [entity ? entity.firmwareId : null], firmwareId: [entity ? entity.firmwareId : null],
softwareId: [entity ? entity.softwareId : null], softwareId: [entity ? entity.softwareId : null],
label: [entity ? entity.label : ''], label: [entity ? entity.label : '', [Validators.maxLength(255)]],
deviceData: [entity ? entity.deviceData : null, [Validators.required]], deviceData: [entity ? entity.deviceData : null, [Validators.required]],
additionalInfo: this.fb.group( additionalInfo: this.fb.group(
{ {

9
ui-ngx/src/app/modules/home/pages/edge/edge.component.html

@ -126,6 +126,9 @@
<mat-error *ngIf="entityForm.get('name').hasError('required')"> <mat-error *ngIf="entityForm.get('name').hasError('required')">
{{ 'edge.name-required' | translate }} {{ 'edge.name-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'edge.name-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-entity-subtype-autocomplete <tb-entity-subtype-autocomplete
formControlName="type" formControlName="type"
@ -140,6 +143,9 @@
<mat-error *ngIf="entityForm.get('edgeLicenseKey').hasError('required')"> <mat-error *ngIf="entityForm.get('edgeLicenseKey').hasError('required')">
{{ 'edge.edge-license-key-required' | translate }} {{ 'edge.edge-license-key-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('type').hasError('maxlength')">
{{ 'edge.type-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div [fxShow]="edgeScope !== 'customer_user'"> <div [fxShow]="edgeScope !== 'customer_user'">
@ -179,6 +185,9 @@
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>edge.label</mat-label> <mat-label translate>edge.label</mat-label>
<input matInput formControlName="label"> <input matInput formControlName="label">
<mat-error *ngIf="entityForm.get('label').hasError('maxlength')">
{{ 'edge.label-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<div formGroupName="additionalInfo" fxLayout="column"> <div formGroupName="additionalInfo" fxLayout="column">
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">

6
ui-ngx/src/app/modules/home/pages/edge/edge.component.ts

@ -69,9 +69,9 @@ export class EdgeComponent extends EntityComponent<EdgeInfo> {
buildForm(entity: EdgeInfo): FormGroup { buildForm(entity: EdgeInfo): FormGroup {
const form = this.fb.group( const form = this.fb.group(
{ {
name: [entity ? entity.name : '', [Validators.required]], name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
type: [entity?.type ? entity.type : 'default', [Validators.required]], type: [entity?.type ? entity.type : 'default', [Validators.required, Validators.maxLength(255)]],
label: [entity ? entity.label : ''], label: [entity ? entity.label : '', Validators.maxLength(255)],
cloudEndpoint: [null, [Validators.required]], cloudEndpoint: [null, [Validators.required]],
edgeLicenseKey: ['', [Validators.required]], edgeLicenseKey: ['', [Validators.required]],
routingKey: this.fb.control({value: entity ? entity.routingKey : null, disabled: true}), routingKey: this.fb.control({value: entity ? entity.routingKey : null, disabled: true}),

3
ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.html

@ -76,6 +76,9 @@
<mat-error *ngIf="entityForm.get('name').hasError('required')"> <mat-error *ngIf="entityForm.get('name').hasError('required')">
{{ 'entity-view.name-required' | translate }} {{ 'entity-view.name-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'entity-view.name-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-entity-subtype-autocomplete <tb-entity-subtype-autocomplete
formControlName="type" formControlName="type"

4
ui-ngx/src/app/modules/home/pages/entity-view/entity-view.component.ts

@ -80,8 +80,8 @@ export class EntityViewComponent extends EntityComponent<EntityViewInfo> {
buildForm(entity: EntityViewInfo): FormGroup { buildForm(entity: EntityViewInfo): FormGroup {
return this.fb.group( return this.fb.group(
{ {
name: [entity ? entity.name : '', [Validators.required]], name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
type: [entity ? entity.type : null, [Validators.required]], type: [entity ? entity.type : null, Validators.required],
entityId: [entity ? entity.entityId : null, [Validators.required]], entityId: [entity ? entity.entityId : null, [Validators.required]],
startTimeMs: [entity ? entity.startTimeMs : null], startTimeMs: [entity ? entity.startTimeMs : null],
endTimeMs: [entity ? entity.endTimeMs : null], endTimeMs: [entity ? entity.endTimeMs : null],

12
ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html

@ -65,6 +65,9 @@
<mat-error *ngIf="entityForm.get('title').hasError('required')"> <mat-error *ngIf="entityForm.get('title').hasError('required')">
{{ 'ota-update.title-required' | translate }} {{ 'ota-update.title-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'ota-update.title-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block" fxFlex> <mat-form-field class="mat-block" fxFlex>
<mat-label translate>ota-update.version</mat-label> <mat-label translate>ota-update.version</mat-label>
@ -72,6 +75,9 @@
<mat-error *ngIf="entityForm.get('version').hasError('required')"> <mat-error *ngIf="entityForm.get('version').hasError('required')">
{{ 'ota-update.version-required' | translate }} {{ 'ota-update.version-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('version').hasError('maxlength')">
{{ 'ota-update.version-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<tb-device-profile-autocomplete <tb-device-profile-autocomplete
@ -111,7 +117,8 @@
</mat-checkbox> </mat-checkbox>
</section> </section>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayoutGap.sm="8px" <div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayoutGap.sm="8px"
fxLayout.xs="column" fxLayout.md="column" *ngIf="!(isAdd && this.entityForm.get('generateChecksum').value)"> fxLayout.xs="column" fxLayout.md="column"
*ngIf="!(isAdd && this.entityForm.get('generateChecksum').value)">
<mat-form-field class="mat-block" fxFlex="33"> <mat-form-field class="mat-block" fxFlex="33">
<mat-label translate>ota-update.checksum-algorithm</mat-label> <mat-label translate>ota-update.checksum-algorithm</mat-label>
<mat-select formControlName="checksumAlgorithm"> <mat-select formControlName="checksumAlgorithm">
@ -149,7 +156,8 @@
<input matInput formControlName="url" <input matInput formControlName="url"
type="text" type="text"
[required]="entityForm.get('isURL').value"> [required]="entityForm.get('isURL').value">
<mat-error *ngIf="entityForm.get('url').hasError('required') || entityForm.get('url').hasError('pattern')" translate> <mat-error *ngIf="entityForm.get('url').hasError('required') || entityForm.get('url').hasError('pattern')"
translate>
ota-update.direct-url-required ota-update.direct-url-required
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>

3
ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.html

@ -84,6 +84,9 @@
<mat-error *ngIf="entityForm.get('name').hasError('required')"> <mat-error *ngIf="entityForm.get('name').hasError('required')">
{{ 'rulechain.name-required' | translate }} {{ 'rulechain.name-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'rulechain.name-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<mat-checkbox fxFlex formControlName="debugMode" style="padding-bottom: 16px;"> <mat-checkbox fxFlex formControlName="debugMode" style="padding-bottom: 16px;">
{{ 'rulechain.debug-mode' | translate }} {{ 'rulechain.debug-mode' | translate }}

2
ui-ngx/src/app/modules/home/pages/rulechain/rulechain.component.ts

@ -57,7 +57,7 @@ export class RuleChainComponent extends EntityComponent<RuleChain> {
buildForm(entity: RuleChain): FormGroup { buildForm(entity: RuleChain): FormGroup {
return this.fb.group( return this.fb.group(
{ {
name: [entity ? entity.name : '', [Validators.required]], name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
debugMode: [entity ? entity.debugMode : false], debugMode: [entity ? entity.debugMode : false],
additionalInfo: this.fb.group( additionalInfo: this.fb.group(
{ {

3
ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html

@ -48,6 +48,9 @@
<mat-error *ngIf="entityForm.get('title').hasError('required')"> <mat-error *ngIf="entityForm.get('title').hasError('required')">
{{ 'tenant.title-required' | translate }} {{ 'tenant.title-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'tenant.title-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-tenant-profile-autocomplete <tb-tenant-profile-autocomplete
[selectDefaultProfile]="isAdd" [selectDefaultProfile]="isAdd"

2
ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts

@ -51,7 +51,7 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> {
buildEntityForm(entity: TenantInfo): FormGroup { buildEntityForm(entity: TenantInfo): FormGroup {
return this.fb.group( return this.fb.group(
{ {
title: [entity ? entity.title : '', [Validators.required]], title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
tenantProfileId: [entity ? entity.tenantProfileId : null, [Validators.required]], tenantProfileId: [entity ? entity.tenantProfileId : null, [Validators.required]],
additionalInfo: this.fb.group( additionalInfo: this.fb.group(
{ {

3
ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.html

@ -44,6 +44,9 @@
<mat-error *ngIf="entityForm.get('title').hasError('required')"> <mat-error *ngIf="entityForm.get('title').hasError('required')">
{{ 'widgets-bundle.title-required' | translate }} {{ 'widgets-bundle.title-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'widgets-bundle.title-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<tb-image-input fxFlex <tb-image-input fxFlex
label="{{'widgets-bundle.image-preview' | translate}}" label="{{'widgets-bundle.image-preview' | translate}}"

2
ui-ngx/src/app/modules/home/pages/widget/widgets-bundle.component.ts

@ -47,7 +47,7 @@ export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> {
buildForm(entity: WidgetsBundle): FormGroup { buildForm(entity: WidgetsBundle): FormGroup {
return this.fb.group( return this.fb.group(
{ {
title: [entity ? entity.title : '', [Validators.required]], title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
image: [entity ? entity.image : ''], image: [entity ? entity.image : ''],
description: [entity ? entity.description : '', Validators.maxLength(255)] description: [entity ? entity.description : '', Validators.maxLength(255)]
} }

9
ui-ngx/src/app/shared/components/contact.component.html

@ -28,10 +28,16 @@
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>contact.city</mat-label> <mat-label translate>contact.city</mat-label>
<input matInput formControlName="city"> <input matInput formControlName="city">
<mat-error *ngIf="parentForm.get('city').hasError('maxlength')">
{{ 'contact.city-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>contact.state</mat-label> <mat-label translate>contact.state</mat-label>
<input matInput formControlName="state"> <input matInput formControlName="state">
<mat-error *ngIf="parentForm.get('state').hasError('maxlength')">
{{ 'contact.state-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>contact.postal-code</mat-label> <mat-label translate>contact.postal-code</mat-label>
@ -52,6 +58,9 @@
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>contact.phone</mat-label> <mat-label translate>contact.phone</mat-label>
<input matInput formControlName="phone"> <input matInput formControlName="phone">
<mat-error *ngIf="parentForm.get('phone').hasError('maxlength')">
{{ 'contact.phone-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>contact.email</mat-label> <mat-label translate>contact.email</mat-label>

3
ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.html

@ -40,4 +40,7 @@
<mat-error *ngIf="subTypeFormGroup.get('subType').hasError('required')"> <mat-error *ngIf="subTypeFormGroup.get('subType').hasError('required')">
{{ entitySubtypeRequiredText | translate }} {{ entitySubtypeRequiredText | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="subTypeFormGroup.get('subType').hasError('maxlength')">
{{ entitySubtypeMaxLength | translate }}
</mat-error>
</mat-form-field> </mat-form-field>

15
ui-ngx/src/app/shared/components/entity/entity-subtype-autocomplete.component.ts

@ -15,7 +15,7 @@
/// ///
import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms';
import { Observable, of, Subscription, throwError } from 'rxjs'; import { Observable, of, Subscription, throwError } from 'rxjs';
import { import {
catchError, catchError,
@ -58,9 +58,11 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
entityType: EntityType; entityType: EntityType;
private requiredValue: boolean; private requiredValue: boolean;
get required(): boolean { get required(): boolean {
return this.requiredValue; return this.requiredValue;
} }
@Input() @Input()
set required(value: boolean) { set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value); this.requiredValue = coerceBooleanProperty(value);
@ -74,6 +76,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
selectEntitySubtypeText: string; selectEntitySubtypeText: string;
entitySubtypeText: string; entitySubtypeText: string;
entitySubtypeRequiredText: string; entitySubtypeRequiredText: string;
entitySubtypeMaxLength: string;
filteredSubTypes: Observable<Array<string>>; filteredSubTypes: Observable<Array<string>>;
@ -96,7 +99,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
private entityViewService: EntityViewService, private entityViewService: EntityViewService,
private fb: FormBuilder) { private fb: FormBuilder) {
this.subTypeFormGroup = this.fb.group({ this.subTypeFormGroup = this.fb.group({
subType: [null] subType: [null, Validators.maxLength(255)]
}); });
} }
@ -114,6 +117,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
this.selectEntitySubtypeText = 'asset.select-asset-type'; this.selectEntitySubtypeText = 'asset.select-asset-type';
this.entitySubtypeText = 'asset.asset-type'; this.entitySubtypeText = 'asset.asset-type';
this.entitySubtypeRequiredText = 'asset.asset-type-required'; this.entitySubtypeRequiredText = 'asset.asset-type-required';
this.entitySubtypeMaxLength = 'asset.asset-type-max-length';
this.broadcastSubscription = this.broadcast.on('assetSaved', () => { this.broadcastSubscription = this.broadcast.on('assetSaved', () => {
this.subTypes = null; this.subTypes = null;
}); });
@ -122,6 +126,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
this.selectEntitySubtypeText = 'device.select-device-type'; this.selectEntitySubtypeText = 'device.select-device-type';
this.entitySubtypeText = 'device.device-type'; this.entitySubtypeText = 'device.device-type';
this.entitySubtypeRequiredText = 'device.device-type-required'; this.entitySubtypeRequiredText = 'device.device-type-required';
this.entitySubtypeMaxLength = 'device.device-type-max-length';
this.broadcastSubscription = this.broadcast.on('deviceSaved', () => { this.broadcastSubscription = this.broadcast.on('deviceSaved', () => {
this.subTypes = null; this.subTypes = null;
}); });
@ -130,6 +135,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
this.selectEntitySubtypeText = 'edge.select-edge-type'; this.selectEntitySubtypeText = 'edge.select-edge-type';
this.entitySubtypeText = 'edge.edge-type'; this.entitySubtypeText = 'edge.edge-type';
this.entitySubtypeRequiredText = 'edge.edge-type-required'; this.entitySubtypeRequiredText = 'edge.edge-type-required';
this.entitySubtypeMaxLength = 'edge.type-max-length';
this.broadcastSubscription = this.broadcast.on('edgeSaved', () => { this.broadcastSubscription = this.broadcast.on('edgeSaved', () => {
this.subTypes = null; this.subTypes = null;
}); });
@ -138,6 +144,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
this.selectEntitySubtypeText = 'entity-view.select-entity-view-type'; this.selectEntitySubtypeText = 'entity-view.select-entity-view-type';
this.entitySubtypeText = 'entity-view.entity-view-type'; this.entitySubtypeText = 'entity-view.entity-view-type';
this.entitySubtypeRequiredText = 'entity-view.entity-view-type-required'; this.entitySubtypeRequiredText = 'entity-view.entity-view-type-required';
this.entitySubtypeMaxLength = 'entity-view.type-max-length'
this.broadcastSubscription = this.broadcast.on('entityViewSaved', () => { this.broadcastSubscription = this.broadcast.on('entityViewSaved', () => {
this.subTypes = null; this.subTypes = null;
}); });
@ -149,7 +156,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
debounceTime(150), debounceTime(150),
distinctUntilChanged(), distinctUntilChanged(),
tap(value => { tap(value => {
this.updateView(value); this.updateView(value);
}), }),
// startWith<string | EntitySubtype>(''), // startWith<string | EntitySubtype>(''),
map(value => value ? value : ''), map(value => value ? value : ''),
@ -203,7 +210,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
fetchSubTypes(searchText?: string, strictMatch: boolean = false): Observable<Array<string>> { fetchSubTypes(searchText?: string, strictMatch: boolean = false): Observable<Array<string>> {
this.searchText = searchText; this.searchText = searchText;
return this.getSubTypes().pipe( return this.getSubTypes().pipe(
map(subTypes => subTypes.filter( subType => { map(subTypes => subTypes.filter(subType => {
if (strictMatch) { if (strictMatch) {
return searchText ? subType === searchText : false; return searchText ? subType === searchText : false;
} else { } else {

3
ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.html

@ -39,4 +39,7 @@
<mat-error *ngIf="relationTypeFormGroup.get('relationType').hasError('required')"> <mat-error *ngIf="relationTypeFormGroup.get('relationType').hasError('required')">
{{ 'relation.relation-type-required' | translate }} {{ 'relation.relation-type-required' | translate }}
</mat-error> </mat-error>
<mat-error *ngIf="relationTypeFormGroup.get('relationType').hasError('maxlength')">
{{ 'relation.relation-type-max-length' | translate }}
</mat-error>
</mat-form-field> </mat-form-field>

2
ui-ngx/src/app/shared/components/relation/relation-type-autocomplete.component.ts

@ -68,7 +68,7 @@ export class RelationTypeAutocompleteComponent implements ControlValueAccessor,
public translate: TranslateService, public translate: TranslateService,
private fb: FormBuilder) { private fb: FormBuilder) {
this.relationTypeFormGroup = this.fb.group({ this.relationTypeFormGroup = this.fb.group({
relationType: [null, this.required ? [Validators.required] : []] relationType: [null, this.required ? [Validators.required, Validators.maxLength(255)] : [Validators.maxLength(255)]]
}); });
} }

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

@ -369,6 +369,7 @@
"management": "Asset management", "management": "Asset management",
"view-assets": "View Assets", "view-assets": "View Assets",
"add": "Add Asset", "add": "Add Asset",
"asset-type-max-length": "Asset type should be less than 256",
"assign-to-customer": "Assign to customer", "assign-to-customer": "Assign to customer",
"assign-asset-to-customer": "Assign Asset(s) To Customer", "assign-asset-to-customer": "Assign Asset(s) To Customer",
"assign-asset-to-customer-text": "Please select the assets to assign to the customer", "assign-asset-to-customer-text": "Please select the assets to assign to the customer",
@ -391,6 +392,8 @@
"asset-types": "Asset types", "asset-types": "Asset types",
"name": "Name", "name": "Name",
"name-required": "Name is required.", "name-required": "Name is required.",
"name-max-length": "Name should be less than 256",
"label-max-length": "Label should be less than 256",
"description": "Description", "description": "Description",
"type": "Type", "type": "Type",
"type-required": "Type is required.", "type-required": "Type is required.",
@ -449,6 +452,7 @@
"scope-shared": "Shared attributes", "scope-shared": "Shared attributes",
"add": "Add attribute", "add": "Add attribute",
"key": "Key", "key": "Key",
"key-max-length": "Key should be less than 256",
"last-update-time": "Last update time", "last-update-time": "Last update time",
"key-required": "Attribute key is required.", "key-required": "Attribute key is required.",
"value": "Value", "value": "Value",
@ -590,7 +594,10 @@
"address2": "Address 2", "address2": "Address 2",
"phone": "Phone", "phone": "Phone",
"email": "Email", "email": "Email",
"no-address": "No address" "no-address": "No address",
"state-max-length": "State length should be less than 256",
"phone-max-length": "Phone number should be less than 256",
"city-max-length": "Specified city should be less than 256"
}, },
"common": { "common": {
"username": "Username", "username": "Username",
@ -646,6 +653,7 @@
"manage-dashboards": "Manage dashboards", "manage-dashboards": "Manage dashboards",
"title": "Title", "title": "Title",
"title-required": "Title is required.", "title-required": "Title is required.",
"title-max-length": "Title should be less than 256",
"description": "Description", "description": "Description",
"details": "Details", "details": "Details",
"events": "Events", "events": "Events",
@ -700,6 +708,7 @@
"select-widget-subtitle": "List of available widget types", "select-widget-subtitle": "List of available widget types",
"delete": "Delete dashboard", "delete": "Delete dashboard",
"title-required": "Title is required.", "title-required": "Title is required.",
"title-max-length": "Title should be less than 256",
"description": "Description", "description": "Description",
"details": "Details", "details": "Details",
"dashboard-details": "Dashboard details", "dashboard-details": "Dashboard details",
@ -886,6 +895,7 @@
"management": "Device management", "management": "Device management",
"view-devices": "View Devices", "view-devices": "View Devices",
"device-alias": "Device alias", "device-alias": "Device alias",
"device-type-max-length": "Device type should be less than 256",
"aliases": "Device aliases", "aliases": "Device aliases",
"no-alias-matching": "'{{alias}}' not found.", "no-alias-matching": "'{{alias}}' not found.",
"no-aliases-found": "No aliases found.", "no-aliases-found": "No aliases found.",
@ -998,6 +1008,8 @@
"device-types": "Device types", "device-types": "Device types",
"name": "Name", "name": "Name",
"name-required": "Name is required.", "name-required": "Name is required.",
"name-max-length": "Name should be less than 256",
"label-max-length": "Label should be less than 256",
"description": "Description", "description": "Description",
"label": "Label", "label": "Label",
"events": "Events", "events": "Events",
@ -1050,6 +1062,7 @@
"set-default": "Make device profile default", "set-default": "Make device profile default",
"delete": "Delete device profile", "delete": "Delete device profile",
"copyId": "Copy device profile Id", "copyId": "Copy device profile Id",
"name-max-length": "Name should be less than 256",
"new-device-profile-name": "Device profile name", "new-device-profile-name": "Device profile name",
"new-device-profile-name-required": "Device profile name is required.", "new-device-profile-name-required": "Device profile name is required.",
"name": "Name", "name": "Name",
@ -1401,6 +1414,9 @@
"edge": "Edge", "edge": "Edge",
"edge-instances": "Edge instances", "edge-instances": "Edge instances",
"edge-file": "Edge file", "edge-file": "Edge file",
"name-max-length": "Name should be less than 256",
"label-max-length": "Label should be less than 256",
"type-max-length": "Type should be less than 256",
"management": "Edge management", "management": "Edge management",
"no-edges-matching": "No edges matching '{{entity}}' were found.", "no-edges-matching": "No edges matching '{{entity}}' were found.",
"rulechain-templates": "Rule chain templates", "rulechain-templates": "Rule chain templates",
@ -1741,6 +1757,8 @@
"created-time": "Created time", "created-time": "Created time",
"name": "Name", "name": "Name",
"name-required": "Name is required.", "name-required": "Name is required.",
"name-max-length": "Name should be less than 256",
"type-max-length": "Entity view type should be less than 256",
"description": "Description", "description": "Description",
"events": "Events", "events": "Events",
"details": "Details", "details": "Details",
@ -2335,12 +2353,14 @@
"selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected", "selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected",
"title": "Title", "title": "Title",
"title-required": "Title is required.", "title-required": "Title is required.",
"title-max-length": "Title should be less than 256",
"types": { "types": {
"firmware": "Firmware", "firmware": "Firmware",
"software": "Software" "software": "Software"
}, },
"version": "Version", "version": "Version",
"version-required": "Version is required.", "version-required": "Version is required.",
"version-max-length": "Version should be less than 256",
"warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type."
}, },
"position": { "position": {
@ -2379,6 +2399,7 @@
"delete": "Delete relation", "delete": "Delete relation",
"relation-type": "Relation type", "relation-type": "Relation type",
"relation-type-required": "Relation type is required.", "relation-type-required": "Relation type is required.",
"relation-type-max-length": "Relation type should be less than 256",
"any-relation-type": "Any type", "any-relation-type": "Any type",
"add": "Add relation", "add": "Add relation",
"edit": "Edit relation", "edit": "Edit relation",
@ -2423,7 +2444,8 @@
"selected-resources": "{ count, plural, 1 {1 resource} other {# resources} } selected", "selected-resources": "{ count, plural, 1 {1 resource} other {# resources} } selected",
"system": "System", "system": "System",
"title": "Title", "title": "Title",
"title-required": "Title is required." "title-required": "Title is required.",
"title-max-length": "Title should be less than 256"
}, },
"rulechain": { "rulechain": {
"rulechain": "Rule chain", "rulechain": "Rule chain",
@ -2432,6 +2454,7 @@
"delete": "Delete rule chain", "delete": "Delete rule chain",
"name": "Name", "name": "Name",
"name-required": "Name is required.", "name-required": "Name is required.",
"name-max-length": "Name should be less than 256",
"description": "Description", "description": "Description",
"add": "Add Rule Chain", "add": "Add Rule Chain",
"set-root": "Make rule chain root", "set-root": "Make rule chain root",
@ -2573,6 +2596,7 @@
"add-tenant-text": "Add new tenant", "add-tenant-text": "Add new tenant",
"no-tenants-text": "No tenants found", "no-tenants-text": "No tenants found",
"tenant-details": "Tenant details", "tenant-details": "Tenant details",
"title-max-length": "Title should be less than 256",
"delete-tenant-title": "Are you sure you want to delete the tenant '{{tenantTitle}}'?", "delete-tenant-title": "Are you sure you want to delete the tenant '{{tenantTitle}}'?",
"delete-tenant-text": "Be careful, after the confirmation the tenant and all related data will become unrecoverable.", "delete-tenant-text": "Be careful, after the confirmation the tenant and all related data will become unrecoverable.",
"delete-tenants-title": "Are you sure you want to delete { count, plural, 1 {1 tenant} other {# tenants} }?", "delete-tenants-title": "Are you sure you want to delete { count, plural, 1 {1 tenant} other {# tenants} }?",
@ -2602,6 +2626,7 @@
"edit": "Edit tenant profile", "edit": "Edit tenant profile",
"tenant-profile-details": "Tenant profile details", "tenant-profile-details": "Tenant profile details",
"no-tenant-profiles-text": "No tenant profiles found", "no-tenant-profiles-text": "No tenant profiles found",
"name-max-length": "Name should be less than 256",
"search": "Search tenant profiles", "search": "Search tenant profiles",
"selected-tenant-profiles": "{ count, plural, 1 {1 tenant profile} other {# tenant profiles} } selected", "selected-tenant-profiles": "{ count, plural, 1 {1 tenant profile} other {# tenant profiles} } selected",
"no-tenant-profiles-matching": "No tenant profile matching '{{entity}}' were found.", "no-tenant-profiles-matching": "No tenant profile matching '{{entity}}' were found.",
@ -2925,6 +2950,7 @@
"delete": "Delete widgets bundle", "delete": "Delete widgets bundle",
"title": "Title", "title": "Title",
"title-required": "Title is required.", "title-required": "Title is required.",
"title-max-length": "Title should be less than 256",
"description": "Description", "description": "Description",
"image-preview": "Image preview", "image-preview": "Image preview",
"add-widgets-bundle-text": "Add new widgets bundle", "add-widgets-bundle-text": "Add new widgets bundle",

Loading…
Cancel
Save