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.thingsboard.rule.engine.api.MailService;
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.UpdateMessage;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
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.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
@ -88,6 +88,7 @@ public class AdminController extends BaseController {
} else if (adminSettings.getKey().equals("sms")) {
smsService.updateSmsConfiguration();
}
return adminSettings;
} catch (Exception 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.EntityRelationsQuery;
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.service.security.permission.Operation;
@ -63,6 +64,7 @@ public class EntityRelationController extends BaseController {
if (relation.getTypeGroup() == null) {
relation.setTypeGroup(RelationTypeGroup.COMMON);
}
ConstraintValidator.validateFields(relation);
relationService.saveRelation(getTenantId(), relation);
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.audit.ActionType;
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.EntityId;
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.tenant.profile.DefaultTenantProfileConfiguration;
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.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.AccessValidator;
@ -138,7 +138,11 @@ public class TelemetryController extends BaseController {
@ResponseBody
public DeferredResult<ResponseEntity> getAttributeKeys(
@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')")
@ -147,8 +151,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> getAttributeKeysByScope(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr
, @PathVariable("scope") String scope) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope));
try {
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')")
@ -157,9 +165,13 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> getAttributes(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr));
try {
SecurityUser user = getCurrentUser();
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')")
@ -169,9 +181,13 @@ public class TelemetryController extends BaseController {
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope,
@RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr));
try {
SecurityUser user = getCurrentUser();
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')")
@ -179,8 +195,12 @@ public class TelemetryController extends BaseController {
@ResponseBody
public DeferredResult<ResponseEntity> getTimeseriesKeys(
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor()));
try {
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')")
@ -190,10 +210,14 @@ public class TelemetryController extends BaseController {
@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@RequestParam(name = "keys", required = false) String keysStr,
@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,
(result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes));
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(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 = "orderBy", defaultValue = "DESC") String orderBy,
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> {
// If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted
Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr);
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());
});
try {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> {
// If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted
Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr);
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());
});
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@ -227,8 +255,12 @@ public class TelemetryController extends BaseController {
@ResponseBody
public DeferredResult<ResponseEntity> saveDeviceAttributes(@PathVariable("deviceId") String deviceIdStr, @PathVariable("scope") String scope,
@RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
try {
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')")
@ -237,8 +269,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityAttributesV1(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope,
@RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
try {
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')")
@ -247,8 +283,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityAttributesV2(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope,
@RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
try {
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')")
@ -257,8 +297,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityTelemetry(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope,
@RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveTelemetry(getTenantId(), entityId, requestBody, 0L);
try {
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')")
@ -267,8 +311,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> saveEntityTelemetryWithTTL(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope, @PathVariable("ttl") Long ttl,
@RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveTelemetry(getTenantId(), entityId, requestBody, ttl);
try {
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')")
@ -280,8 +328,12 @@ public class TelemetryController extends BaseController {
@RequestParam(name = "startTs", required = false) Long startTs,
@RequestParam(name = "endTs", required = false) Long endTs,
@RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted);
try {
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,
@ -335,8 +387,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("deviceId") String deviceIdStr,
@PathVariable("scope") String scope,
@RequestParam(name = "keys") String keysStr) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return deleteAttributes(entityId, scope, keysStr);
try {
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')")
@ -345,8 +401,12 @@ public class TelemetryController extends BaseController {
public DeferredResult<ResponseEntity> deleteEntityAttributes(@PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
@PathVariable("scope") String scope,
@RequestParam(name = "keys") String keysStr) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return deleteAttributes(entityId, scope, keysStr);
try {
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 {
@ -392,6 +452,7 @@ public class TelemetryController extends BaseController {
}
if (json.isObject()) {
List<AttributeKvEntry> attributes = extractRequestAttributes(json);
attributes.forEach(ConstraintValidator::validateFields);
if (attributes.isEmpty()) {
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 org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@EqualsAndHashCode(callSuper = true)
public abstract class ContactBased<I extends UUIDBased> extends SearchTextBasedWithAdditionalInfo<I> implements HasName {
private static final long serialVersionUID = 5047448057830660988L;
@Length(fieldName = "country")
@NoXss
protected String country;
@Length(fieldName = "state")
@NoXss
protected String state;
@Length(fieldName = "city")
@NoXss
protected String city;
@NoXss
protected String address;
@NoXss
protected String address2;
@Length(fieldName = "zip or postal code")
@NoXss
protected String zip;
@Length(fieldName = "phone")
@NoXss
protected String phone;
@NoXss
protected String email;
public ContactBased() {
super();
}
@ -48,7 +54,7 @@ public abstract class ContactBased<I extends UUIDBased> extends SearchTextBasedW
public ContactBased(I id) {
super(id);
}
public ContactBased(ContactBased<I> contact) {
super(contact);
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 org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
public class Customer extends ContactBased<CustomerId> implements HasTenantId {
private static final long serialVersionUID = -1599722990298929275L;
@NoXss
@Length(fieldName = "title")
private String title;
private TenantId tenantId;
@ -37,7 +39,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId {
public Customer(CustomerId id) {
super(id);
}
public Customer(Customer customer) {
super(customer);
this.tenantId = customer.getTenantId();
@ -51,7 +53,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId {
public void setTenantId(TenantId tenantId) {
this.tenantId = tenantId;
}
public String getTitle() {
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.DashboardId;
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 javax.validation.Valid;
@ -29,6 +30,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa
private TenantId tenantId;
@NoXss
@Length(fieldName = "title")
private String title;
private String image;
@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.OtaPackageId;
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 java.io.ByteArrayInputStream;
@ -39,10 +40,13 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
private TenantId tenantId;
private CustomerId customerId;
@NoXss
@Length(fieldName = "name")
private String name;
@NoXss
@Length(fieldName = "type")
private String type;
@NoXss
@Length(fieldName = "label")
private String label;
private DeviceProfileId deviceProfileId;
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.RuleChainId;
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 javax.validation.Valid;
@ -41,6 +42,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
private TenantId tenantId;
@NoXss
@Length(fieldName = "name")
private String name;
@NoXss
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.TenantId;
import org.thingsboard.server.common.data.objects.TelemetryEntityView;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
/**
@ -41,8 +42,10 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId>
private TenantId tenantId;
private CustomerId customerId;
@NoXss
@Length(fieldName = "name")
private String name;
@NoXss
@Length(fieldName = "type")
private String type;
private TelemetryEntityView keys;
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.EqualsAndHashCode;
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.OtaPackageId;
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
@Data
@ -35,10 +36,14 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage
private TenantId tenantId;
private DeviceProfileId deviceProfileId;
private OtaPackageType type;
@Length(fieldName = "title")
private String title;
@Length(fieldName = "version")
private String version;
@Length(fieldName = "url")
private String url;
private boolean hasData;
@Length(fieldName = "file name")
private String fileName;
private String contentType;
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.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@Slf4j
@ -29,6 +30,7 @@ public class TbResource extends TbResourceInfo {
private static final long serialVersionUID = 7379609705527272306L;
@NoXss
@Length(fieldName = "file name")
private String fileName;
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 org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@Slf4j
@ -32,6 +33,7 @@ public class TbResourceInfo extends SearchTextBased<TbResourceId> implements Has
private TenantId tenantId;
@NoXss
@Length(fieldName = "title")
private String title;
private ResourceType resourceType;
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 org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.TenantProfileId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@EqualsAndHashCode(callSuper = true)
@ -27,8 +28,10 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId {
private static final long serialVersionUID = 8057243243859922101L;
@Length(fieldName = "title")
@NoXss
private String title;
@Length(fieldName = "region")
@NoXss
private String region;
private TenantProfileId tenantProfileId;
@ -40,7 +43,7 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId {
public Tenant(TenantId id) {
super(id);
}
public Tenant(Tenant tenant) {
super(tenant);
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.tenant.profile.DefaultTenantProfileConfiguration;
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 java.io.ByteArrayInputStream;
@ -37,6 +38,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn
public class TenantProfile extends SearchTextBased<TenantProfileId> implements HasName {
@NoXss
@Length(fieldName = "name")
private String name;
@NoXss
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.UserId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@EqualsAndHashCode(callSuper = true)
@ -36,8 +36,10 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H
private String email;
private Authority authority;
@NoXss
@Length(fieldName = "firs name")
private String firstName;
@NoXss
@Length(fieldName = "last name")
private String lastName;
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.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import java.util.List;
@ -41,6 +42,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha
private TenantId tenantId;
private CustomerId customerId;
@Length(fieldName = "type")
private String type;
private EntityId originator;
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;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode;
import lombok.Getter;
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.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
@EqualsAndHashCode(callSuper = true)
@ToString
@ -40,8 +40,11 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H
private TenantId tenantId;
private CustomerId customerId;
private RuleChainId rootRuleChainId;
@Length(fieldName = "name")
private String name;
@Length(fieldName = "type")
private String type;
@Length(fieldName = "label")
private String label;
private String routingKey;
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;
import com.fasterxml.jackson.databind.JsonNode;
import javax.validation.Valid;
import java.util.Optional;
/**
@ -25,6 +24,7 @@ import java.util.Optional;
public class BaseAttributeKvEntry implements AttributeKvEntry {
private final long lastUpdateTs;
@Valid
private final KvEntry kv;
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;
import org.thingsboard.server.common.data.validation.Length;
import java.util.Objects;
import java.util.Optional;
public abstract class BasicKvEntry implements KvEntry {
@Length(fieldName = "attribute key")
private final 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 {
private static final long serialVersionUID = 1L;
private final 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;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
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;
@Slf4j
@ -38,6 +35,7 @@ public class EntityRelation implements Serializable {
private EntityId from;
private EntityId to;
@Length(fieldName = "type")
private String type;
private RelationTypeGroup typeGroup;
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.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.validation.Length;
import org.thingsboard.server.common.data.validation.NoXss;
@Data
@ -37,6 +38,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im
private TenantId tenantId;
@NoXss
@Length(fieldName = "name")
private String name;
private RuleChainType type;
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.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.validation.Length;
@Data
@EqualsAndHashCode(callSuper = true)
@ -33,7 +34,9 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo<RuleNodeId> impl
private static final long serialVersionUID = -5656679015121235465L;
private RuleChainId ruleChainId;
@Length(fieldName = "type")
private String type;
@Length(fieldName = "name")
private String name;
private boolean debugMode;
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.id.TenantId;
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 java.util.Arrays;
public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements HasTenantId {
private static final long serialVersionUID = -7627368878362410489L;
@ -31,6 +30,7 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H
@NoXss
private String alias;
@NoXss
@Length(fieldName = "title")
private String title;
private String image;
@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 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.EntityType;
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.TenantEntityWithDataDao;
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.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Slf4j
public abstract class DataValidator<D extends BaseData<?>> {
private static final Pattern EMAIL_PATTERN =
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) {
try {
if (data == null) {
throw new DataValidationException("Data object can't be null!");
}
List<String> validationErrors = validateFields(data);
if (!validationErrors.isEmpty()) {
throw new IllegalArgumentException("Validation error: " + String.join(", ", validationErrors));
}
ConstraintValidator.validateFields(data);
TenantId tenantId = tenantIdFunction.apply(data);
validateDataImpl(tenantId, data);
@ -104,14 +85,6 @@ public abstract class DataValidator<D extends BaseData<?>> {
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,
TenantEntityDao tenantEntityDao,
long maxEntities,
@ -126,10 +99,10 @@ public abstract class DataValidator<D extends BaseData<?>> {
}
protected void validateMaxSumDataSizePerTenant(TenantId tenantId,
TenantEntityWithDataDao dataDao,
long maxSumDataSize,
long currentDataSize,
EntityType entityType) {
TenantEntityWithDataDao dataDao,
long maxSumDataSize,
long currentDataSize,
EntityType entityType) {
if (maxSumDataSize > 0) {
if (dataDao.sumDataSizeByTenantId(tenantId) + currentDataSize > maxSumDataSize) {
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')">
{{ 'attribute.key-required' | translate }}
</mat-error>
<mat-error *ngIf="attributeFormGroup.get('key').hasError('maxlength')">
{{ 'attribute.key-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-value-input
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 {
this.attributeFormGroup = this.fb.group({
key: ['', [Validators.required]],
key: ['', [Validators.required, Validators.maxLength(255)]],
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 {
const entityForm = this.buildEntityForm(entity);
entityForm.addControl('country', this.fb.control(entity ? entity.country : '', []));
entityForm.addControl('city', this.fb.control(entity ? entity.city : '', []));
entityForm.addControl('state', this.fb.control(entity ? entity.state : '', []));
entityForm.addControl('country', this.fb.control(entity ? entity.country : '', [Validators.maxLength(255)]));
entityForm.addControl('city', this.fb.control(entity ? entity.city : '', [Validators.maxLength(255)]));
entityForm.addControl('state', this.fb.control(entity ? entity.state : '', [Validators.maxLength(255)]));
entityForm.addControl('zip', this.fb.control(entity ? entity.zip : '',
this.zipValidators(entity ? entity.country : '')
));
entityForm.addControl('address', this.fb.control(entity ? entity.address : '', []));
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]));
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')">
{{ 'device-profile.name-required' | translate }}
</mat-error>
<mat-error *ngIf="deviceProfileDetailsFormGroup.get('name').hasError('maxlength')">
{{ 'device-profile.name-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-rule-chain-autocomplete
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);
this.deviceProfileDetailsFormGroup = this.fb.group(
{
name: [data.deviceProfileName, [Validators.required]],
name: [data.deviceProfileName, [Validators.required, Validators.maxLength(255)]],
type: [DeviceProfileType.DEFAULT, [Validators.required]],
image: [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')">
{{ 'device-profile.name-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'device-profile.name-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-rule-chain-autocomplete
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(
{
name: [entity ? entity.name : '', [Validators.required]],
name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
type: [entity ? entity.type : null, [Validators.required]],
image: [entity ? entity.image : null],
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')">
{{ 'tenant-profile.name-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'tenant-profile.name-max-length' | translate }}
</mat-error>
</mat-form-field>
<div fxLayout="column">
<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 {
return this.fb.group(
{
name: [entity ? entity.name : '', [Validators.required]],
name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
isolatedTbCore: [entity ? entity.isolatedTbCore : false, []],
isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []],
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')">
{{ 'device.name-required' | translate }}
</mat-error>
<mat-error *ngIf="deviceWizardFormGroup.get('name').hasError('maxlength')">
{{ 'device.name-max-length' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block">
<mat-label translate>device.label</mat-label>
<input matInput formControlName="label">
<mat-error *ngIf="deviceWizardFormGroup.get('label').hasError('maxlength')">
{{ 'device.label-max-length' | translate }}
</mat-error>
</mat-form-field>
<div fxLayout="row" fxLayoutGap="16px">
<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) {
super(store, router, dialogRef);
this.deviceWizardFormGroup = this.fb.group({
name: ['', Validators.required],
label: [''],
name: ['', [Validators.required, Validators.maxLength(255)]],
label: ['', Validators.maxLength(255)],
gateway: [false],
overwriteActivityTime: [false],
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')">
{{ 'resource.title-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'resource.title-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-file-input *ngIf="isAdd"
formControlName="data"

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

@ -29,7 +29,7 @@ import {
ResourceTypeMIMETypes,
ResourceTypeTranslationMap
} 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";
@Component({
@ -56,16 +56,14 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
super.ngOnInit();
this.entityForm.get('resourceType').valueChanges.pipe(
startWith(ResourceType.LWM2M_MODEL),
pairwise(),
filter(() => this.isAdd),
takeUntil(this.destroy$)
).subscribe(([previousType, type]) => {
if (previousType === this.resourceType.LWM2M_MODEL) {
this.entityForm.get('title').setValidators(Validators.required);
this.entityForm.get('title').updateValueAndValidity({emitEvent: false});
}
).subscribe((type) => {
if (type === this.resourceType.LWM2M_MODEL) {
this.entityForm.get('title').clearValidators();
this.entityForm.get('title').updateValueAndValidity({emitEvent: false});
this.entityForm.get('title').disable({emitEvent: false});
this.entityForm.patchValue({title: ''}, {emitEvent: false});
} else {
this.entityForm.get('title').enable({emitEvent: false})
}
this.entityForm.patchValue({
data: null,
@ -91,11 +89,8 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme
buildForm(entity: Resource): FormGroup {
const form = this.fb.group(
{
title: [entity ? entity.title : '', []],
resourceType: [{
value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL,
disabled: !this.isAdd
}, [Validators.required]],
title: [entity ? entity.title : "", [Validators.required, Validators.maxLength(255)]],
resourceType: [entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, [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')">
{{ 'asset.name-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'asset.name-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-entity-subtype-autocomplete
formControlName="type"
@ -86,6 +89,9 @@
<mat-form-field class="mat-block">
<mat-label translate>asset.label</mat-label>
<input matInput formControlName="label">
<mat-error *ngIf="entityForm.get('label').hasError('maxlength')">
{{ 'asset.label-max-length' | translate }}
</mat-error>
</mat-form-field>
<div formGroupName="additionalInfo">
<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 {
return this.fb.group(
{
name: [entity ? entity.name : '', [Validators.required]],
type: [entity ? entity.type : null, [Validators.required]],
label: [entity ? entity.label : ''],
name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
type: [entity ? entity.type : null, [Validators.required, Validators.maxLength(255)]],
label: [entity ? entity.label : '', Validators.maxLength(255)],
additionalInfo: this.fb.group(
{
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')">
{{ 'customer.title-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'customer.title-max-length' | translate }}
</mat-error>
</mat-form-field>
<div formGroupName="additionalInfo" fxLayout="column">
<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 {
return this.fb.group(
{
title: [entity ? entity.title : '', [Validators.required]],
title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
additionalInfo: this.fb.group(
{
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')">
{{ 'dashboard.title-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'dashboard.title-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-image-input fxFlex
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);
return this.fb.group(
{
title: [entity ? entity.title : '', [Validators.required]],
title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
image: [entity ? entity.image : null],
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')">
{{ 'device.name-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'device.name-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-device-profile-autocomplete
[selectDefaultProfile]="isAdd"
@ -100,6 +103,9 @@
<mat-form-field class="mat-block">
<mat-label translate>device.label</mat-label>
<input matInput formControlName="label">
<mat-error *ngIf="entityForm.get('label').hasError('maxlength')">
{{ 'device.label-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-ota-package-autocomplete
[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 {
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]],
firmwareId: [entity ? entity.firmwareId : null],
softwareId: [entity ? entity.softwareId : null],
label: [entity ? entity.label : ''],
label: [entity ? entity.label : '', [Validators.maxLength(255)]],
deviceData: [entity ? entity.deviceData : null, [Validators.required]],
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')">
{{ 'edge.name-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'edge.name-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-entity-subtype-autocomplete
formControlName="type"
@ -140,6 +143,9 @@
<mat-error *ngIf="entityForm.get('edgeLicenseKey').hasError('required')">
{{ 'edge.edge-license-key-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('type').hasError('maxlength')">
{{ 'edge.type-max-length' | translate }}
</mat-error>
</mat-form-field>
</div>
<div [fxShow]="edgeScope !== 'customer_user'">
@ -179,6 +185,9 @@
<mat-form-field class="mat-block">
<mat-label translate>edge.label</mat-label>
<input matInput formControlName="label">
<mat-error *ngIf="entityForm.get('label').hasError('maxlength')">
{{ 'edge.label-max-length' | translate }}
</mat-error>
</mat-form-field>
<div formGroupName="additionalInfo" fxLayout="column">
<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 {
const form = this.fb.group(
{
name: [entity ? entity.name : '', [Validators.required]],
type: [entity?.type ? entity.type : 'default', [Validators.required]],
label: [entity ? entity.label : ''],
name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
type: [entity?.type ? entity.type : 'default', [Validators.required, Validators.maxLength(255)]],
label: [entity ? entity.label : '', Validators.maxLength(255)],
cloudEndpoint: [null, [Validators.required]],
edgeLicenseKey: ['', [Validators.required]],
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')">
{{ 'entity-view.name-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'entity-view.name-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-entity-subtype-autocomplete
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 {
return this.fb.group(
{
name: [entity ? entity.name : '', [Validators.required]],
type: [entity ? entity.type : null, [Validators.required]],
name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
type: [entity ? entity.type : null, Validators.required],
entityId: [entity ? entity.entityId : null, [Validators.required]],
startTimeMs: [entity ? entity.startTimeMs : 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')">
{{ 'ota-update.title-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'ota-update.title-max-length' | translate }}
</mat-error>
</mat-form-field>
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>ota-update.version</mat-label>
@ -72,6 +75,9 @@
<mat-error *ngIf="entityForm.get('version').hasError('required')">
{{ 'ota-update.version-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('version').hasError('maxlength')">
{{ 'ota-update.version-max-length' | translate }}
</mat-error>
</mat-form-field>
</div>
<tb-device-profile-autocomplete
@ -111,7 +117,8 @@
</mat-checkbox>
</section>
<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-label translate>ota-update.checksum-algorithm</mat-label>
<mat-select formControlName="checksumAlgorithm">
@ -149,7 +156,8 @@
<input matInput formControlName="url"
type="text"
[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
</mat-error>
</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')">
{{ 'rulechain.name-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('name').hasError('maxlength')">
{{ 'rulechain.name-max-length' | translate }}
</mat-error>
</mat-form-field>
<mat-checkbox fxFlex formControlName="debugMode" style="padding-bottom: 16px;">
{{ '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 {
return this.fb.group(
{
name: [entity ? entity.name : '', [Validators.required]],
name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]],
debugMode: [entity ? entity.debugMode : false],
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')">
{{ 'tenant.title-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'tenant.title-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-tenant-profile-autocomplete
[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 {
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]],
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')">
{{ 'widgets-bundle.title-required' | translate }}
</mat-error>
<mat-error *ngIf="entityForm.get('title').hasError('maxlength')">
{{ 'widgets-bundle.title-max-length' | translate }}
</mat-error>
</mat-form-field>
<tb-image-input fxFlex
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 {
return this.fb.group(
{
title: [entity ? entity.title : '', [Validators.required]],
title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
image: [entity ? entity.image : ''],
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-label translate>contact.city</mat-label>
<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 class="mat-block">
<mat-label translate>contact.state</mat-label>
<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 class="mat-block">
<mat-label translate>contact.postal-code</mat-label>
@ -52,6 +58,9 @@
<mat-form-field class="mat-block">
<mat-label translate>contact.phone</mat-label>
<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 class="mat-block">
<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')">
{{ entitySubtypeRequiredText | translate }}
</mat-error>
<mat-error *ngIf="subTypeFormGroup.get('subType').hasError('maxlength')">
{{ entitySubtypeMaxLength | translate }}
</mat-error>
</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 { 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 {
catchError,
@ -58,9 +58,11 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
entityType: EntityType;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
}
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
@ -74,6 +76,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
selectEntitySubtypeText: string;
entitySubtypeText: string;
entitySubtypeRequiredText: string;
entitySubtypeMaxLength: string;
filteredSubTypes: Observable<Array<string>>;
@ -96,7 +99,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
private entityViewService: EntityViewService,
private fb: FormBuilder) {
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.entitySubtypeText = 'asset.asset-type';
this.entitySubtypeRequiredText = 'asset.asset-type-required';
this.entitySubtypeMaxLength = 'asset.asset-type-max-length';
this.broadcastSubscription = this.broadcast.on('assetSaved', () => {
this.subTypes = null;
});
@ -122,6 +126,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
this.selectEntitySubtypeText = 'device.select-device-type';
this.entitySubtypeText = 'device.device-type';
this.entitySubtypeRequiredText = 'device.device-type-required';
this.entitySubtypeMaxLength = 'device.device-type-max-length';
this.broadcastSubscription = this.broadcast.on('deviceSaved', () => {
this.subTypes = null;
});
@ -130,6 +135,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
this.selectEntitySubtypeText = 'edge.select-edge-type';
this.entitySubtypeText = 'edge.edge-type';
this.entitySubtypeRequiredText = 'edge.edge-type-required';
this.entitySubtypeMaxLength = 'edge.type-max-length';
this.broadcastSubscription = this.broadcast.on('edgeSaved', () => {
this.subTypes = null;
});
@ -138,6 +144,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
this.selectEntitySubtypeText = 'entity-view.select-entity-view-type';
this.entitySubtypeText = 'entity-view.entity-view-type';
this.entitySubtypeRequiredText = 'entity-view.entity-view-type-required';
this.entitySubtypeMaxLength = 'entity-view.type-max-length'
this.broadcastSubscription = this.broadcast.on('entityViewSaved', () => {
this.subTypes = null;
});
@ -149,7 +156,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
debounceTime(150),
distinctUntilChanged(),
tap(value => {
this.updateView(value);
this.updateView(value);
}),
// startWith<string | EntitySubtype>(''),
map(value => value ? value : ''),
@ -203,7 +210,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor,
fetchSubTypes(searchText?: string, strictMatch: boolean = false): Observable<Array<string>> {
this.searchText = searchText;
return this.getSubTypes().pipe(
map(subTypes => subTypes.filter( subType => {
map(subTypes => subTypes.filter(subType => {
if (strictMatch) {
return searchText ? subType === searchText : false;
} 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')">
{{ 'relation.relation-type-required' | translate }}
</mat-error>
<mat-error *ngIf="relationTypeFormGroup.get('relationType').hasError('maxlength')">
{{ 'relation.relation-type-max-length' | translate }}
</mat-error>
</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,
private fb: FormBuilder) {
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",
"view-assets": "View Assets",
"add": "Add Asset",
"asset-type-max-length": "Asset type should be less than 256",
"assign-to-customer": "Assign 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",
@ -391,6 +392,8 @@
"asset-types": "Asset types",
"name": "Name",
"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",
"type": "Type",
"type-required": "Type is required.",
@ -449,6 +452,7 @@
"scope-shared": "Shared attributes",
"add": "Add attribute",
"key": "Key",
"key-max-length": "Key should be less than 256",
"last-update-time": "Last update time",
"key-required": "Attribute key is required.",
"value": "Value",
@ -590,7 +594,10 @@
"address2": "Address 2",
"phone": "Phone",
"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": {
"username": "Username",
@ -646,6 +653,7 @@
"manage-dashboards": "Manage dashboards",
"title": "Title",
"title-required": "Title is required.",
"title-max-length": "Title should be less than 256",
"description": "Description",
"details": "Details",
"events": "Events",
@ -700,6 +708,7 @@
"select-widget-subtitle": "List of available widget types",
"delete": "Delete dashboard",
"title-required": "Title is required.",
"title-max-length": "Title should be less than 256",
"description": "Description",
"details": "Details",
"dashboard-details": "Dashboard details",
@ -886,6 +895,7 @@
"management": "Device management",
"view-devices": "View Devices",
"device-alias": "Device alias",
"device-type-max-length": "Device type should be less than 256",
"aliases": "Device aliases",
"no-alias-matching": "'{{alias}}' not found.",
"no-aliases-found": "No aliases found.",
@ -998,6 +1008,8 @@
"device-types": "Device types",
"name": "Name",
"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",
"label": "Label",
"events": "Events",
@ -1050,6 +1062,7 @@
"set-default": "Make device profile default",
"delete": "Delete device profile",
"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-required": "Device profile name is required.",
"name": "Name",
@ -1401,6 +1414,9 @@
"edge": "Edge",
"edge-instances": "Edge instances",
"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",
"no-edges-matching": "No edges matching '{{entity}}' were found.",
"rulechain-templates": "Rule chain templates",
@ -1741,6 +1757,8 @@
"created-time": "Created time",
"name": "Name",
"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",
"events": "Events",
"details": "Details",
@ -2335,12 +2353,14 @@
"selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected",
"title": "Title",
"title-required": "Title is required.",
"title-max-length": "Title should be less than 256",
"types": {
"firmware": "Firmware",
"software": "Software"
},
"version": "Version",
"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."
},
"position": {
@ -2379,6 +2399,7 @@
"delete": "Delete relation",
"relation-type": "Relation type",
"relation-type-required": "Relation type is required.",
"relation-type-max-length": "Relation type should be less than 256",
"any-relation-type": "Any type",
"add": "Add relation",
"edit": "Edit relation",
@ -2423,7 +2444,8 @@
"selected-resources": "{ count, plural, 1 {1 resource} other {# resources} } selected",
"system": "System",
"title": "Title",
"title-required": "Title is required."
"title-required": "Title is required.",
"title-max-length": "Title should be less than 256"
},
"rulechain": {
"rulechain": "Rule chain",
@ -2432,6 +2454,7 @@
"delete": "Delete rule chain",
"name": "Name",
"name-required": "Name is required.",
"name-max-length": "Name should be less than 256",
"description": "Description",
"add": "Add Rule Chain",
"set-root": "Make rule chain root",
@ -2573,6 +2596,7 @@
"add-tenant-text": "Add new tenant",
"no-tenants-text": "No tenants found",
"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-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} }?",
@ -2602,6 +2626,7 @@
"edit": "Edit tenant profile",
"tenant-profile-details": "Tenant profile details",
"no-tenant-profiles-text": "No tenant profiles found",
"name-max-length": "Name should be less than 256",
"search": "Search tenant profiles",
"selected-tenant-profiles": "{ count, plural, 1 {1 tenant profile} other {# tenant profiles} } selected",
"no-tenant-profiles-matching": "No tenant profile matching '{{entity}}' were found.",
@ -2925,6 +2950,7 @@
"delete": "Delete widgets bundle",
"title": "Title",
"title-required": "Title is required.",
"title-max-length": "Title should be less than 256",
"description": "Description",
"image-preview": "Image preview",
"add-widgets-bundle-text": "Add new widgets bundle",

Loading…
Cancel
Save