Commit 71bed873b01bffc8107c6d6bdd68f9a7fbd64176
Committed by
GitHub
Merge pull request #5166 from ViacheslavKlimov/fields-validation
[3.3.2] Fields length validation
Showing
81 changed files
with
662 additions
and
141 deletions
... | ... | @@ -76,6 +76,7 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; |
76 | 76 | import org.thingsboard.server.common.data.kv.TsKvEntry; |
77 | 77 | import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
78 | 78 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
79 | +import org.thingsboard.server.dao.service.ConstraintValidator; | |
79 | 80 | import org.thingsboard.server.dao.timeseries.TimeseriesService; |
80 | 81 | import org.thingsboard.server.queue.util.TbCoreComponent; |
81 | 82 | import org.thingsboard.server.service.security.AccessValidator; |
... | ... | @@ -184,7 +185,11 @@ public class TelemetryController extends BaseController { |
184 | 185 | public DeferredResult<ResponseEntity> getAttributeKeys( |
185 | 186 | @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, |
186 | 187 | @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { |
187 | - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); | |
188 | + try { | |
189 | + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback); | |
190 | + } catch (Exception e) { | |
191 | + throw handleException(e); | |
192 | + } | |
188 | 193 | } |
189 | 194 | |
190 | 195 | @ApiOperation(value = "Get all attribute keys by scope (getAttributeKeysByScope)", |
... | ... | @@ -201,8 +206,12 @@ public class TelemetryController extends BaseController { |
201 | 206 | @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, |
202 | 207 | @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, |
203 | 208 | @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope) throws ThingsboardException { |
204 | - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, | |
205 | - (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); | |
209 | + try { | |
210 | + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, | |
211 | + (result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope)); | |
212 | + } catch (Exception e) { | |
213 | + throw handleException(e); | |
214 | + } | |
206 | 215 | } |
207 | 216 | |
208 | 217 | @ApiOperation(value = "Get attributes (getAttributes)", |
... | ... | @@ -220,9 +229,13 @@ public class TelemetryController extends BaseController { |
220 | 229 | @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, |
221 | 230 | @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, |
222 | 231 | @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { |
223 | - SecurityUser user = getCurrentUser(); | |
224 | - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, | |
225 | - (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); | |
232 | + try { | |
233 | + SecurityUser user = getCurrentUser(); | |
234 | + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, | |
235 | + (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr)); | |
236 | + } catch (Exception e) { | |
237 | + throw handleException(e); | |
238 | + } | |
226 | 239 | } |
227 | 240 | |
228 | 241 | |
... | ... | @@ -244,9 +257,13 @@ public class TelemetryController extends BaseController { |
244 | 257 | @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, |
245 | 258 | @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, |
246 | 259 | @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException { |
247 | - SecurityUser user = getCurrentUser(); | |
248 | - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, | |
249 | - (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); | |
260 | + try { | |
261 | + SecurityUser user = getCurrentUser(); | |
262 | + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, | |
263 | + (result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr)); | |
264 | + } catch (Exception e) { | |
265 | + throw handleException(e); | |
266 | + } | |
250 | 267 | } |
251 | 268 | |
252 | 269 | @ApiOperation(value = "Get time-series keys (getTimeseriesKeys)", |
... | ... | @@ -259,8 +276,12 @@ public class TelemetryController extends BaseController { |
259 | 276 | public DeferredResult<ResponseEntity> getTimeseriesKeys( |
260 | 277 | @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType, |
261 | 278 | @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException { |
262 | - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, | |
263 | - (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); | |
279 | + try { | |
280 | + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, | |
281 | + (result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor())); | |
282 | + } catch (Exception e) { | |
283 | + throw handleException(e); | |
284 | + } | |
264 | 285 | } |
265 | 286 | |
266 | 287 | @ApiOperation(value = "Get latest time-series value (getLatestTimeseries)", |
... | ... | @@ -285,9 +306,13 @@ public class TelemetryController extends BaseController { |
285 | 306 | @ApiParam(value = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr, |
286 | 307 | @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION) |
287 | 308 | @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { |
288 | - SecurityUser user = getCurrentUser(); | |
289 | - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, | |
290 | - (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); | |
309 | + try { | |
310 | + SecurityUser user = getCurrentUser(); | |
311 | + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, | |
312 | + (result, tenantId, entityId) -> getLatestTimeseriesValuesCallback(result, user, entityId, keysStr, useStrictDataTypes)); | |
313 | + } catch (Exception e) { | |
314 | + throw handleException(e); | |
315 | + } | |
291 | 316 | } |
292 | 317 | |
293 | 318 | @ApiOperation(value = "Get time-series data (getTimeseries)", |
... | ... | @@ -324,15 +349,19 @@ public class TelemetryController extends BaseController { |
324 | 349 | @RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy, |
325 | 350 | @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION) |
326 | 351 | @RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException { |
327 | - return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, | |
328 | - (result, tenantId, entityId) -> { | |
329 | - // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted | |
330 | - Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); | |
331 | - List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy)) | |
332 | - .collect(Collectors.toList()); | |
333 | - | |
334 | - Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); | |
335 | - }); | |
352 | + try { | |
353 | + return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr, | |
354 | + (result, tenantId, entityId) -> { | |
355 | + // If interval is 0, convert this to a NONE aggregation, which is probably what the user really wanted | |
356 | + Aggregation agg = interval == 0L ? Aggregation.valueOf(Aggregation.NONE.name()) : Aggregation.valueOf(aggStr); | |
357 | + List<ReadTsKvQuery> queries = toKeysList(keys).stream().map(key -> new BaseReadTsKvQuery(key, startTs, endTs, interval, limit, agg, orderBy)) | |
358 | + .collect(Collectors.toList()); | |
359 | + | |
360 | + Futures.addCallback(tsService.findAll(tenantId, entityId, queries), getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor()); | |
361 | + }); | |
362 | + } catch (Exception e) { | |
363 | + throw handleException(e); | |
364 | + } | |
336 | 365 | } |
337 | 366 | |
338 | 367 | @ApiOperation(value = "Save device attributes (saveDeviceAttributes)", |
... | ... | @@ -356,8 +385,12 @@ public class TelemetryController extends BaseController { |
356 | 385 | @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr, |
357 | 386 | @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, |
358 | 387 | @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { |
359 | - EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); | |
360 | - return saveAttributes(getTenantId(), entityId, scope, request); | |
388 | + try { | |
389 | + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); | |
390 | + return saveAttributes(getTenantId(), entityId, scope, request); | |
391 | + } catch (Exception e) { | |
392 | + throw handleException(e); | |
393 | + } | |
361 | 394 | } |
362 | 395 | |
363 | 396 | @ApiOperation(value = "Save entity attributes (saveEntityAttributesV1)", |
... | ... | @@ -380,8 +413,12 @@ public class TelemetryController extends BaseController { |
380 | 413 | @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, |
381 | 414 | @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope, |
382 | 415 | @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { |
383 | - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
384 | - return saveAttributes(getTenantId(), entityId, scope, request); | |
416 | + try { | |
417 | + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
418 | + return saveAttributes(getTenantId(), entityId, scope, request); | |
419 | + } catch (Exception e) { | |
420 | + throw handleException(e); | |
421 | + } | |
385 | 422 | } |
386 | 423 | |
387 | 424 | @ApiOperation(value = "Save entity attributes (saveEntityAttributesV2)", |
... | ... | @@ -404,8 +441,12 @@ public class TelemetryController extends BaseController { |
404 | 441 | @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, |
405 | 442 | @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, |
406 | 443 | @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException { |
407 | - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
408 | - return saveAttributes(getTenantId(), entityId, scope, request); | |
444 | + try { | |
445 | + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
446 | + return saveAttributes(getTenantId(), entityId, scope, request); | |
447 | + } catch (Exception e) { | |
448 | + throw handleException(e); | |
449 | + } | |
409 | 450 | } |
410 | 451 | |
411 | 452 | |
... | ... | @@ -429,8 +470,12 @@ public class TelemetryController extends BaseController { |
429 | 470 | @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, |
430 | 471 | @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION, required = true, allowableValues = "ANY") @PathVariable("scope") String scope, |
431 | 472 | @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { |
432 | - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
433 | - return saveTelemetry(getTenantId(), entityId, requestBody, 0L); | |
473 | + try { | |
474 | + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
475 | + return saveTelemetry(getTenantId(), entityId, requestBody, 0L); | |
476 | + } catch (Exception e) { | |
477 | + throw handleException(e); | |
478 | + } | |
434 | 479 | } |
435 | 480 | |
436 | 481 | @ApiOperation(value = "Save or update time-series data with TTL (saveEntityTelemetryWithTTL)", |
... | ... | @@ -455,8 +500,12 @@ public class TelemetryController extends BaseController { |
455 | 500 | @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION, required = true, allowableValues = "ANY") @PathVariable("scope") String scope, |
456 | 501 | @ApiParam(value = "A long value representing TTL (Time to Live) parameter.", required = true) @PathVariable("ttl") Long ttl, |
457 | 502 | @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException { |
458 | - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
459 | - return saveTelemetry(getTenantId(), entityId, requestBody, ttl); | |
503 | + try { | |
504 | + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
505 | + return saveTelemetry(getTenantId(), entityId, requestBody, ttl); | |
506 | + } catch (Exception e) { | |
507 | + throw handleException(e); | |
508 | + } | |
460 | 509 | } |
461 | 510 | |
462 | 511 | @ApiOperation(value = "Delete entity time-series data (deleteEntityTimeseries)", |
... | ... | @@ -489,8 +538,12 @@ public class TelemetryController extends BaseController { |
489 | 538 | @RequestParam(name = "endTs", required = false) Long endTs, |
490 | 539 | @ApiParam(value = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.") |
491 | 540 | @RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException { |
492 | - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
493 | - return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); | |
541 | + try { | |
542 | + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
543 | + return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted); | |
544 | + } catch (Exception e) { | |
545 | + throw handleException(e); | |
546 | + } | |
494 | 547 | } |
495 | 548 | |
496 | 549 | private DeferredResult<ResponseEntity> deleteTimeseries(EntityId entityIdStr, String keysStr, boolean deleteAllDataForKeys, |
... | ... | @@ -556,8 +609,12 @@ public class TelemetryController extends BaseController { |
556 | 609 | @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr, |
557 | 610 | @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope, |
558 | 611 | @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { |
559 | - EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); | |
560 | - return deleteAttributes(entityId, scope, keysStr); | |
612 | + try { | |
613 | + EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr); | |
614 | + return deleteAttributes(entityId, scope, keysStr); | |
615 | + } catch (Exception e) { | |
616 | + throw handleException(e); | |
617 | + } | |
561 | 618 | } |
562 | 619 | |
563 | 620 | @ApiOperation(value = "Delete entity attributes (deleteEntityAttributes)", |
... | ... | @@ -580,8 +637,12 @@ public class TelemetryController extends BaseController { |
580 | 637 | @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr, |
581 | 638 | @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope, |
582 | 639 | @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException { |
583 | - EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
584 | - return deleteAttributes(entityId, scope, keysStr); | |
640 | + try { | |
641 | + EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr); | |
642 | + return deleteAttributes(entityId, scope, keysStr); | |
643 | + } catch (Exception e) { | |
644 | + throw handleException(e); | |
645 | + } | |
585 | 646 | } |
586 | 647 | |
587 | 648 | private DeferredResult<ResponseEntity> deleteAttributes(EntityId entityIdSrc, String scope, String keysStr) throws ThingsboardException { |
... | ... | @@ -627,6 +688,7 @@ public class TelemetryController extends BaseController { |
627 | 688 | } |
628 | 689 | if (json.isObject()) { |
629 | 690 | List<AttributeKvEntry> attributes = extractRequestAttributes(json); |
691 | + attributes.forEach(ConstraintValidator::validateFields); | |
630 | 692 | if (attributes.isEmpty()) { |
631 | 693 | return getImmediateDeferredResult("No attributes data found in request body!", HttpStatus.BAD_REQUEST); |
632 | 694 | } | ... | ... |
... | ... | @@ -100,6 +100,20 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest { |
100 | 100 | } |
101 | 101 | |
102 | 102 | @Test |
103 | + public void testSaveAssetWithViolationOfLengthValidation() throws Exception { | |
104 | + Asset asset = new Asset(); | |
105 | + asset.setName(RandomStringUtils.randomAlphabetic(300)); | |
106 | + asset.setType("default"); | |
107 | + doPost("/api/asset", asset).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); | |
108 | + asset.setName("Normal name"); | |
109 | + asset.setType(RandomStringUtils.randomAlphabetic(300)); | |
110 | + doPost("/api/asset", asset).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); | |
111 | + asset.setType("default"); | |
112 | + asset.setLabel(RandomStringUtils.randomAlphabetic(300)); | |
113 | + doPost("/api/asset", asset).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); | |
114 | + } | |
115 | + | |
116 | + @Test | |
103 | 117 | public void testUpdateAssetFromDifferentTenant() throws Exception { |
104 | 118 | Asset asset = new Asset(); |
105 | 119 | asset.setName("My asset"); | ... | ... |
... | ... | @@ -91,6 +91,28 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest |
91 | 91 | } |
92 | 92 | |
93 | 93 | @Test |
94 | + public void testSaveCustomerWithViolationOfValidation() throws Exception { | |
95 | + Customer customer = new Customer(); | |
96 | + customer.setTitle(RandomStringUtils.randomAlphabetic(300)); | |
97 | + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); | |
98 | + customer.setTitle("Normal title"); | |
99 | + customer.setCity(RandomStringUtils.randomAlphabetic(300)); | |
100 | + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of city must be equal or less than 255"))); | |
101 | + customer.setCity("Normal city"); | |
102 | + customer.setCountry(RandomStringUtils.randomAlphabetic(300)); | |
103 | + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of country must be equal or less than 255"))); | |
104 | + customer.setCountry("Ukraine"); | |
105 | + customer.setPhone(RandomStringUtils.randomAlphabetic(300)); | |
106 | + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of phone must be equal or less than 255"))); | |
107 | + customer.setPhone("+3892555554512"); | |
108 | + customer.setState(RandomStringUtils.randomAlphabetic(300)); | |
109 | + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of state must be equal or less than 255"))); | |
110 | + customer.setState("Normal state"); | |
111 | + customer.setZip(RandomStringUtils.randomAlphabetic(300)); | |
112 | + doPost("/api/customer", customer).andExpect(statusReason(containsString("length of zip or postal code must be equal or less than 255"))); | |
113 | + } | |
114 | + | |
115 | + @Test | |
94 | 116 | public void testUpdateCustomerFromDifferentTenant() throws Exception { |
95 | 117 | Customer customer = new Customer(); |
96 | 118 | customer.setTitle("My customer"); | ... | ... |
... | ... | @@ -94,6 +94,13 @@ public abstract class BaseDashboardControllerTest extends AbstractControllerTest |
94 | 94 | } |
95 | 95 | |
96 | 96 | @Test |
97 | + public void testSaveDashboardInfoWithViolationOfValidation() throws Exception { | |
98 | + Dashboard dashboard = new Dashboard(); | |
99 | + dashboard.setTitle(RandomStringUtils.randomAlphabetic(300)); | |
100 | + doPost("/api/dashboard", dashboard).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); | |
101 | + } | |
102 | + | |
103 | + @Test | |
97 | 104 | public void testUpdateDashboardFromDifferentTenant() throws Exception { |
98 | 105 | Dashboard dashboard = new Dashboard(); |
99 | 106 | dashboard.setTitle("My dashboard"); | ... | ... |
... | ... | @@ -115,6 +115,20 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { |
115 | 115 | } |
116 | 116 | |
117 | 117 | @Test |
118 | + public void saveDeviceWithViolationOfValidation() throws Exception { | |
119 | + Device device = new Device(); | |
120 | + device.setName(RandomStringUtils.randomAlphabetic(300)); | |
121 | + device.setType("default"); | |
122 | + doPost("/api/device", device).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); | |
123 | + device.setName("Normal Name"); | |
124 | + device.setType(RandomStringUtils.randomAlphabetic(300)); | |
125 | + doPost("/api/device", device).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); | |
126 | + device.setType("Normal type"); | |
127 | + device.setLabel(RandomStringUtils.randomAlphabetic(300)); | |
128 | + doPost("/api/device", device).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); | |
129 | + } | |
130 | + | |
131 | + @Test | |
118 | 132 | public void testUpdateDeviceFromDifferentTenant() throws Exception { |
119 | 133 | Device device = new Device(); |
120 | 134 | device.setName("My device"); | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import com.google.protobuf.DynamicMessage; |
22 | 22 | import com.google.protobuf.InvalidProtocolBufferException; |
23 | 23 | import com.google.protobuf.util.JsonFormat; |
24 | 24 | import com.squareup.wire.schema.internal.parser.ProtoFileElement; |
25 | +import org.apache.commons.lang3.RandomStringUtils; | |
25 | 26 | import org.junit.After; |
26 | 27 | import org.junit.Assert; |
27 | 28 | import org.junit.Before; |
... | ... | @@ -110,6 +111,12 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController |
110 | 111 | } |
111 | 112 | |
112 | 113 | @Test |
114 | + public void saveDeviceProfileWithViolationOfValidation() throws Exception { | |
115 | + doPost("/api/deviceProfile", this.createDeviceProfile(RandomStringUtils.randomAlphabetic(300), null)) | |
116 | + .andExpect(statusReason(containsString("length of name must be equal or less than 255"))); | |
117 | + } | |
118 | + | |
119 | + @Test | |
113 | 120 | public void testFindDeviceProfileById() throws Exception { |
114 | 121 | DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); |
115 | 122 | DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); | ... | ... |
... | ... | @@ -48,6 +48,7 @@ import org.thingsboard.server.gen.edge.v1.UserUpdateMsg; |
48 | 48 | import java.util.ArrayList; |
49 | 49 | import java.util.Collections; |
50 | 50 | import java.util.List; |
51 | +import java.util.Random; | |
51 | 52 | |
52 | 53 | import static org.hamcrest.Matchers.containsString; |
53 | 54 | import static org.hamcrest.Matchers.nullValue; |
... | ... | @@ -113,6 +114,18 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest { |
113 | 114 | } |
114 | 115 | |
115 | 116 | @Test |
117 | + public void testSaveEdgeWithViolationOfLengthValidation() throws Exception { | |
118 | + Edge edge = constructEdge(RandomStringUtils.randomAlphabetic(300), "default"); | |
119 | + doPost("/api/edge", edge).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); | |
120 | + edge.setName("normal name"); | |
121 | + edge.setType(RandomStringUtils.randomAlphabetic(300)); | |
122 | + doPost("/api/edge", edge).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); | |
123 | + edge.setType("normal type"); | |
124 | + edge.setLabel(RandomStringUtils.randomAlphabetic(300)); | |
125 | + doPost("/api/edge", edge).andExpect(statusReason(containsString("length of label must be equal or less than 255"))); | |
126 | + } | |
127 | + | |
128 | + @Test | |
116 | 129 | public void testFindEdgeById() throws Exception { |
117 | 130 | Edge edge = constructEdge("My edge", "default"); |
118 | 131 | Edge savedEdge = doPost("/api/edge", edge, Edge.class); | ... | ... |
... | ... | @@ -133,6 +133,14 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes |
133 | 133 | assertEquals(foundEntityView.getKeys(), telemetry); |
134 | 134 | } |
135 | 135 | |
136 | + @Test | |
137 | + public void testSaveEntityViewWithViolationOfValidation() throws Exception { | |
138 | + EntityView entityView = createEntityView(RandomStringUtils.randomAlphabetic(300), 0, 0); | |
139 | + doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); | |
140 | + entityView.setName("Normal name"); | |
141 | + entityView.setType(RandomStringUtils.randomAlphabetic(300)); | |
142 | + doPost("/api/entityView", entityView).andExpect(statusReason(containsString("length of type must be equal or less than 255"))); | |
143 | + } | |
136 | 144 | |
137 | 145 | @Test |
138 | 146 | public void testUpdateEntityViewFromDifferentTenant() throws Exception { | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.core.type.TypeReference; |
19 | +import org.apache.commons.lang3.RandomStringUtils; | |
19 | 20 | import org.junit.After; |
20 | 21 | import org.junit.Assert; |
21 | 22 | import org.junit.Before; |
... | ... | @@ -40,6 +41,7 @@ import java.util.ArrayList; |
40 | 41 | import java.util.Collections; |
41 | 42 | import java.util.List; |
42 | 43 | |
44 | +import static org.hamcrest.Matchers.containsString; | |
43 | 45 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
44 | 46 | import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; |
45 | 47 | |
... | ... | @@ -118,6 +120,24 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes |
118 | 120 | } |
119 | 121 | |
120 | 122 | @Test |
123 | + public void saveOtaPackageInfoWithViolationOfLengthValidation() throws Exception { | |
124 | + SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest(); | |
125 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
126 | + firmwareInfo.setType(FIRMWARE); | |
127 | + firmwareInfo.setTitle(RandomStringUtils.randomAlphabetic(300)); | |
128 | + firmwareInfo.setVersion(VERSION); | |
129 | + firmwareInfo.setUsesUrl(false); | |
130 | + doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); | |
131 | + firmwareInfo.setTitle(TITLE); | |
132 | + firmwareInfo.setVersion(RandomStringUtils.randomAlphabetic(300)); | |
133 | + doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of version must be equal or less than 255"))); | |
134 | + firmwareInfo.setVersion(VERSION); | |
135 | + firmwareInfo.setUsesUrl(true); | |
136 | + firmwareInfo.setUrl(RandomStringUtils.randomAlphabetic(300)); | |
137 | + doPost("/api/otaPackage", firmwareInfo).andExpect(statusReason(containsString("length of url must be equal or less than 255"))); | |
138 | + } | |
139 | + | |
140 | + @Test | |
121 | 141 | public void testSaveFirmwareData() throws Exception { |
122 | 142 | SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest(); |
123 | 143 | firmwareInfo.setDeviceProfileId(deviceProfileId); | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.core.type.TypeReference; |
19 | +import org.apache.commons.lang3.RandomStringUtils; | |
19 | 20 | import org.junit.After; |
20 | 21 | import org.junit.Assert; |
21 | 22 | import org.junit.Before; |
... | ... | @@ -33,6 +34,7 @@ import java.util.ArrayList; |
33 | 34 | import java.util.Collections; |
34 | 35 | import java.util.List; |
35 | 36 | |
37 | +import static org.hamcrest.Matchers.containsString; | |
36 | 38 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
37 | 39 | |
38 | 40 | public abstract class BaseRuleChainControllerTest extends AbstractControllerTest { |
... | ... | @@ -85,6 +87,13 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest |
85 | 87 | } |
86 | 88 | |
87 | 89 | @Test |
90 | + public void testSaveRuleChainWithViolationOfLengthValidation() throws Exception { | |
91 | + RuleChain ruleChain = new RuleChain(); | |
92 | + ruleChain.setName(RandomStringUtils.randomAlphabetic(300)); | |
93 | + doPost("/api/ruleChain", ruleChain).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); | |
94 | + } | |
95 | + | |
96 | + @Test | |
88 | 97 | public void testFindRuleChainById() throws Exception { |
89 | 98 | RuleChain ruleChain = new RuleChain(); |
90 | 99 | ruleChain.setName("RuleChain"); | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.core.type.TypeReference; |
19 | +import org.apache.commons.lang3.RandomStringUtils; | |
19 | 20 | import org.junit.After; |
20 | 21 | import org.junit.Assert; |
21 | 22 | import org.junit.Before; |
... | ... | @@ -33,6 +34,7 @@ import java.util.ArrayList; |
33 | 34 | import java.util.Collections; |
34 | 35 | import java.util.List; |
35 | 36 | |
37 | +import static org.hamcrest.Matchers.containsString; | |
36 | 38 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
37 | 39 | |
38 | 40 | public abstract class BaseTbResourceControllerTest extends AbstractControllerTest { |
... | ... | @@ -99,6 +101,16 @@ public abstract class BaseTbResourceControllerTest extends AbstractControllerTes |
99 | 101 | } |
100 | 102 | |
101 | 103 | @Test |
104 | + public void saveResourceInfoWithViolationOfLengthValidation() throws Exception { | |
105 | + TbResource resource = new TbResource(); | |
106 | + resource.setResourceType(ResourceType.JKS); | |
107 | + resource.setTitle(RandomStringUtils.randomAlphabetic(300)); | |
108 | + resource.setFileName(DEFAULT_FILE_NAME); | |
109 | + resource.setData("Test Data"); | |
110 | + doPost("/api/resource", resource).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); | |
111 | + } | |
112 | + | |
113 | + @Test | |
102 | 114 | public void testUpdateTbResourceFromDifferentTenant() throws Exception { |
103 | 115 | TbResource resource = new TbResource(); |
104 | 116 | resource.setResourceType(ResourceType.JKS); | ... | ... |
... | ... | @@ -52,6 +52,14 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { |
52 | 52 | doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) |
53 | 53 | .andExpect(status().isOk()); |
54 | 54 | } |
55 | + | |
56 | + @Test | |
57 | + public void testSaveTenantWithViolationOfValidation() throws Exception { | |
58 | + loginSysAdmin(); | |
59 | + Tenant tenant = new Tenant(); | |
60 | + tenant.setTitle(RandomStringUtils.randomAlphanumeric(300)); | |
61 | + doPost("/api/tenant", tenant).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); | |
62 | + } | |
55 | 63 | |
56 | 64 | @Test |
57 | 65 | public void testFindTenantById() throws Exception { | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.core.type.TypeReference; |
19 | +import org.apache.commons.lang3.RandomStringUtils; | |
19 | 20 | import org.junit.After; |
20 | 21 | import org.junit.Assert; |
21 | 22 | import org.junit.Test; |
... | ... | @@ -75,6 +76,13 @@ public abstract class BaseTenantProfileControllerTest extends AbstractController |
75 | 76 | } |
76 | 77 | |
77 | 78 | @Test |
79 | + public void testSaveTenantProfileWithViolationOfLengthValidation() throws Exception { | |
80 | + loginSysAdmin(); | |
81 | + TenantProfile tenantProfile = this.createTenantProfile(RandomStringUtils.randomAlphabetic(300)); | |
82 | + doPost("/api/tenantProfile", tenantProfile).andExpect(statusReason(containsString("length of name must be equal or less than 255"))); | |
83 | + } | |
84 | + | |
85 | + @Test | |
78 | 86 | public void testFindTenantProfileById() throws Exception { |
79 | 87 | loginSysAdmin(); |
80 | 88 | TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); | ... | ... |
... | ... | @@ -106,6 +106,28 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest { |
106 | 106 | } |
107 | 107 | |
108 | 108 | @Test |
109 | + public void testSaveUserWithViolationOfFiledValidation() throws Exception { | |
110 | + loginSysAdmin(); | |
111 | + | |
112 | + Tenant tenant = new Tenant(); | |
113 | + tenant.setTitle("My tenant"); | |
114 | + Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); | |
115 | + Assert.assertNotNull(savedTenant); | |
116 | + | |
117 | + String email = "tenant2@thingsboard.org"; | |
118 | + User user = new User(); | |
119 | + user.setAuthority(Authority.TENANT_ADMIN); | |
120 | + user.setTenantId(savedTenant.getId()); | |
121 | + user.setEmail(email); | |
122 | + user.setFirstName(RandomStringUtils.randomAlphabetic(300)); | |
123 | + user.setLastName("Downs"); | |
124 | + doPost("/api/user", user).andExpect(statusReason(containsString("Validation error: length of first name must be equal or less than 255"))); | |
125 | + user.setFirstName("Normal name"); | |
126 | + user.setLastName(RandomStringUtils.randomAlphabetic(300)); | |
127 | + doPost("/api/user", user).andExpect(statusReason(containsString("length of last name must be equal or less than 255"))); | |
128 | + } | |
129 | + | |
130 | + @Test | |
109 | 131 | public void testUpdateUserFromDifferentTenant() throws Exception { |
110 | 132 | loginSysAdmin(); |
111 | 133 | Tenant tenant = new Tenant(); | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.core.type.TypeReference; |
19 | +import org.apache.commons.lang3.RandomStringUtils; | |
19 | 20 | import org.junit.After; |
20 | 21 | import org.junit.Assert; |
21 | 22 | import org.junit.Before; |
... | ... | @@ -88,6 +89,13 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController |
88 | 89 | Assert.assertEquals(foundWidgetsBundle.getTitle(), savedWidgetsBundle.getTitle()); |
89 | 90 | } |
90 | 91 | |
92 | + @Test | |
93 | + public void testSaveWidgetBundleWithViolationOfLengthValidation() throws Exception { | |
94 | + WidgetsBundle widgetsBundle = new WidgetsBundle(); | |
95 | + widgetsBundle.setTitle(RandomStringUtils.randomAlphabetic(300)); | |
96 | + doPost("/api/widgetsBundle", widgetsBundle).andExpect(statusReason(containsString("length of title must be equal or less than 255"))); | |
97 | + } | |
98 | + | |
91 | 99 | @Test |
92 | 100 | public void testUpdateWidgetsBundleFromDifferentTenant() throws Exception { |
93 | 101 | WidgetsBundle widgetsBundle = new WidgetsBundle(); | ... | ... |
... | ... | @@ -17,30 +17,36 @@ package org.thingsboard.server.common.data; |
17 | 17 | |
18 | 18 | import lombok.EqualsAndHashCode; |
19 | 19 | import org.thingsboard.server.common.data.id.UUIDBased; |
20 | +import org.thingsboard.server.common.data.validation.Length; | |
20 | 21 | import org.thingsboard.server.common.data.validation.NoXss; |
21 | 22 | |
22 | 23 | @EqualsAndHashCode(callSuper = true) |
23 | 24 | public abstract class ContactBased<I extends UUIDBased> extends SearchTextBasedWithAdditionalInfo<I> implements HasName { |
24 | - | |
25 | + | |
25 | 26 | private static final long serialVersionUID = 5047448057830660988L; |
26 | 27 | |
28 | + @Length(fieldName = "country") | |
27 | 29 | @NoXss |
28 | 30 | protected String country; |
31 | + @Length(fieldName = "state") | |
29 | 32 | @NoXss |
30 | 33 | protected String state; |
34 | + @Length(fieldName = "city") | |
31 | 35 | @NoXss |
32 | 36 | protected String city; |
33 | 37 | @NoXss |
34 | 38 | protected String address; |
35 | 39 | @NoXss |
36 | 40 | protected String address2; |
41 | + @Length(fieldName = "zip or postal code") | |
37 | 42 | @NoXss |
38 | 43 | protected String zip; |
44 | + @Length(fieldName = "phone") | |
39 | 45 | @NoXss |
40 | 46 | protected String phone; |
41 | 47 | @NoXss |
42 | 48 | protected String email; |
43 | - | |
49 | + | |
44 | 50 | public ContactBased() { |
45 | 51 | super(); |
46 | 52 | } |
... | ... | @@ -48,7 +54,7 @@ public abstract class ContactBased<I extends UUIDBased> extends SearchTextBasedW |
48 | 54 | public ContactBased(I id) { |
49 | 55 | super(id); |
50 | 56 | } |
51 | - | |
57 | + | |
52 | 58 | public ContactBased(ContactBased<I> contact) { |
53 | 59 | super(contact); |
54 | 60 | this.country = contact.getCountry(); | ... | ... |
... | ... | @@ -22,13 +22,15 @@ import com.fasterxml.jackson.databind.JsonNode; |
22 | 22 | import io.swagger.annotations.ApiModelProperty; |
23 | 23 | import org.thingsboard.server.common.data.id.CustomerId; |
24 | 24 | import org.thingsboard.server.common.data.id.TenantId; |
25 | +import org.thingsboard.server.common.data.validation.Length; | |
25 | 26 | import org.thingsboard.server.common.data.validation.NoXss; |
26 | 27 | |
27 | 28 | public class Customer extends ContactBased<CustomerId> implements HasTenantId { |
28 | - | |
29 | + | |
29 | 30 | private static final long serialVersionUID = -1599722990298929275L; |
30 | 31 | |
31 | 32 | @NoXss |
33 | + @Length(fieldName = "title") | |
32 | 34 | @ApiModelProperty(position = 3, value = "Title of the customer", example = "Company A") |
33 | 35 | private String title; |
34 | 36 | @ApiModelProperty(position = 5, required = true, value = "JSON object with Tenant Id") |
... | ... | @@ -41,7 +43,7 @@ public class Customer extends ContactBased<CustomerId> implements HasTenantId { |
41 | 43 | public Customer(CustomerId id) { |
42 | 44 | super(id); |
43 | 45 | } |
44 | - | |
46 | + | |
45 | 47 | public Customer(Customer customer) { |
46 | 48 | super(customer); |
47 | 49 | this.tenantId = customer.getTenantId(); | ... | ... |
... | ... | @@ -21,6 +21,7 @@ import io.swagger.annotations.ApiModelProperty; |
21 | 21 | import org.thingsboard.server.common.data.id.CustomerId; |
22 | 22 | import org.thingsboard.server.common.data.id.DashboardId; |
23 | 23 | import org.thingsboard.server.common.data.id.TenantId; |
24 | +import org.thingsboard.server.common.data.validation.Length; | |
24 | 25 | import org.thingsboard.server.common.data.validation.NoXss; |
25 | 26 | |
26 | 27 | import javax.validation.Valid; |
... | ... | @@ -32,6 +33,7 @@ public class DashboardInfo extends SearchTextBased<DashboardId> implements HasNa |
32 | 33 | |
33 | 34 | private TenantId tenantId; |
34 | 35 | @NoXss |
36 | + @Length(fieldName = "title") | |
35 | 37 | private String title; |
36 | 38 | private String image; |
37 | 39 | @Valid | ... | ... |
... | ... | @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.DeviceId; |
28 | 28 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
29 | 29 | import org.thingsboard.server.common.data.id.OtaPackageId; |
30 | 30 | import org.thingsboard.server.common.data.id.TenantId; |
31 | +import org.thingsboard.server.common.data.validation.Length; | |
31 | 32 | import org.thingsboard.server.common.data.validation.NoXss; |
32 | 33 | |
33 | 34 | import java.io.ByteArrayInputStream; |
... | ... | @@ -44,10 +45,13 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
44 | 45 | private TenantId tenantId; |
45 | 46 | private CustomerId customerId; |
46 | 47 | @NoXss |
48 | + @Length(fieldName = "name") | |
47 | 49 | private String name; |
48 | 50 | @NoXss |
51 | + @Length(fieldName = "type") | |
49 | 52 | private String type; |
50 | 53 | @NoXss |
54 | + @Length(fieldName = "label") | |
51 | 55 | private String label; |
52 | 56 | private DeviceProfileId deviceProfileId; |
53 | 57 | private transient DeviceData deviceData; | ... | ... |
... | ... | @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; |
28 | 28 | import org.thingsboard.server.common.data.id.OtaPackageId; |
29 | 29 | import org.thingsboard.server.common.data.id.RuleChainId; |
30 | 30 | import org.thingsboard.server.common.data.id.TenantId; |
31 | +import org.thingsboard.server.common.data.validation.Length; | |
31 | 32 | import org.thingsboard.server.common.data.validation.NoXss; |
32 | 33 | |
33 | 34 | import javax.validation.Valid; |
... | ... | @@ -47,6 +48,7 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H |
47 | 48 | @ApiModelProperty(position = 3, value = "JSON object with Tenant Id that owns the profile.", readOnly = true) |
48 | 49 | private TenantId tenantId; |
49 | 50 | @NoXss |
51 | + @Length(fieldName = "name") | |
50 | 52 | @ApiModelProperty(position = 4, value = "Unique Device Profile Name in scope of Tenant.", example = "Moisture Sensor") |
51 | 53 | private String name; |
52 | 54 | @NoXss | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.EntityId; |
25 | 25 | import org.thingsboard.server.common.data.id.EntityViewId; |
26 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
27 | 27 | import org.thingsboard.server.common.data.objects.TelemetryEntityView; |
28 | +import org.thingsboard.server.common.data.validation.Length; | |
28 | 29 | import org.thingsboard.server.common.data.validation.NoXss; |
29 | 30 | |
30 | 31 | /** |
... | ... | @@ -44,9 +45,11 @@ public class EntityView extends SearchTextBasedWithAdditionalInfo<EntityViewId> |
44 | 45 | private TenantId tenantId; |
45 | 46 | private CustomerId customerId; |
46 | 47 | @NoXss |
48 | + @Length(fieldName = "name") | |
47 | 49 | @ApiModelProperty(position = 5, required = true, value = "Entity View name", example = "A4B72CCDFF33") |
48 | 50 | private String name; |
49 | 51 | @NoXss |
52 | + @Length(fieldName = "type") | |
50 | 53 | @ApiModelProperty(position = 6, required = true, value = "Device Profile Name", example = "Temperature Sensor") |
51 | 54 | private String type; |
52 | 55 | @ApiModelProperty(position = 8, required = true, value = "Set of telemetry and attribute keys to expose via Entity View.") | ... | ... |
... | ... | @@ -27,6 +27,10 @@ import org.thingsboard.server.common.data.id.OtaPackageId; |
27 | 27 | import org.thingsboard.server.common.data.id.TenantId; |
28 | 28 | import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; |
29 | 29 | import org.thingsboard.server.common.data.ota.OtaPackageType; |
30 | +import org.thingsboard.server.common.data.validation.Length; | |
31 | +import org.thingsboard.server.common.data.validation.NoXss; | |
32 | +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; | |
33 | +import org.thingsboard.server.common.data.ota.OtaPackageType; | |
30 | 34 | |
31 | 35 | @ApiModel |
32 | 36 | @Slf4j |
... | ... | @@ -42,16 +46,26 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage |
42 | 46 | private DeviceProfileId deviceProfileId; |
43 | 47 | @ApiModelProperty(position = 5, value = "OTA Package type.", example = "FIRMWARE", readOnly = true) |
44 | 48 | private OtaPackageType type; |
49 | + @Length(fieldName = "title") | |
50 | + @NoXss | |
45 | 51 | @ApiModelProperty(position = 6, value = "OTA Package title.", example = "fw", readOnly = true) |
46 | 52 | private String title; |
53 | + @Length(fieldName = "version") | |
54 | + @NoXss | |
47 | 55 | @ApiModelProperty(position = 7, value = "OTA Package version.", example = "1.0", readOnly = true) |
48 | 56 | private String version; |
57 | + @Length(fieldName = "tag") | |
58 | + @NoXss | |
49 | 59 | @ApiModelProperty(position = 8, value = "OTA Package tag.", example = "fw_1.0", readOnly = true) |
50 | 60 | private String tag; |
61 | + @Length(fieldName = "url") | |
62 | + @NoXss | |
51 | 63 | @ApiModelProperty(position = 9, value = "OTA Package url.", example = "http://thingsboard.org/fw/1", readOnly = true) |
52 | 64 | private String url; |
53 | 65 | @ApiModelProperty(position = 10, value = "Indicates OTA Package 'has data'. Field is returned from DB ('true' if data exists or url is set). If OTA Package 'has data' is 'false' we can not assign the OTA Package to the Device or Device Profile.", example = "true", readOnly = true) |
54 | 66 | private boolean hasData; |
67 | + @Length(fieldName = "file name") | |
68 | + @NoXss | |
55 | 69 | @ApiModelProperty(position = 11, value = "OTA Package file name.", example = "fw_1.0", readOnly = true) |
56 | 70 | private String fileName; |
57 | 71 | @ApiModelProperty(position = 12, value = "OTA Package content type.", example = "APPLICATION_OCTET_STREAM", readOnly = true) | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import lombok.Data; |
20 | 20 | import lombok.EqualsAndHashCode; |
21 | 21 | import lombok.extern.slf4j.Slf4j; |
22 | 22 | import org.thingsboard.server.common.data.id.TbResourceId; |
23 | +import org.thingsboard.server.common.data.validation.Length; | |
23 | 24 | import org.thingsboard.server.common.data.validation.NoXss; |
24 | 25 | |
25 | 26 | @Slf4j |
... | ... | @@ -30,6 +31,7 @@ public class TbResource extends TbResourceInfo { |
30 | 31 | private static final long serialVersionUID = 7379609705527272306L; |
31 | 32 | |
32 | 33 | @NoXss |
34 | + @Length(fieldName = "file name") | |
33 | 35 | @ApiModelProperty(position = 8, value = "Resource file name.", example = "19.xml", readOnly = true) |
34 | 36 | private String fileName; |
35 | 37 | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import lombok.EqualsAndHashCode; |
23 | 23 | import lombok.extern.slf4j.Slf4j; |
24 | 24 | import org.thingsboard.server.common.data.id.TbResourceId; |
25 | 25 | import org.thingsboard.server.common.data.id.TenantId; |
26 | +import org.thingsboard.server.common.data.validation.Length; | |
26 | 27 | import org.thingsboard.server.common.data.validation.NoXss; |
27 | 28 | |
28 | 29 | @ApiModel |
... | ... | @@ -36,6 +37,7 @@ public class TbResourceInfo extends SearchTextBased<TbResourceId> implements Has |
36 | 37 | @ApiModelProperty(position = 3, value = "JSON object with Tenant Id. Tenant Id of the resource can't be changed.", readOnly = true) |
37 | 38 | private TenantId tenantId; |
38 | 39 | @NoXss |
40 | + @Length(fieldName = "title") | |
39 | 41 | @ApiModelProperty(position = 4, value = "Resource title.", example = "BinaryAppDataContainer id=19 v1.0") |
40 | 42 | private String title; |
41 | 43 | @ApiModelProperty(position = 5, value = "Resource type.", example = "LWM2M_MODEL", readOnly = true) | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import io.swagger.annotations.ApiModelProperty; |
23 | 23 | import lombok.EqualsAndHashCode; |
24 | 24 | import org.thingsboard.server.common.data.id.TenantId; |
25 | 25 | import org.thingsboard.server.common.data.id.TenantProfileId; |
26 | +import org.thingsboard.server.common.data.validation.Length; | |
26 | 27 | import org.thingsboard.server.common.data.validation.NoXss; |
27 | 28 | |
28 | 29 | @ApiModel |
... | ... | @@ -31,6 +32,7 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId { |
31 | 32 | |
32 | 33 | private static final long serialVersionUID = 8057243243859922101L; |
33 | 34 | |
35 | + @Length(fieldName = "title") | |
34 | 36 | @NoXss |
35 | 37 | @ApiModelProperty(position = 3, value = "Title of the tenant", example = "Company A") |
36 | 38 | private String title; |
... | ... | @@ -48,7 +50,7 @@ public class Tenant extends ContactBased<TenantId> implements HasTenantId { |
48 | 50 | public Tenant(TenantId id) { |
49 | 51 | super(id); |
50 | 52 | } |
51 | - | |
53 | + | |
52 | 54 | public Tenant(Tenant tenant) { |
53 | 55 | super(tenant); |
54 | 56 | this.title = tenant.getTitle(); | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; |
25 | 25 | import org.thingsboard.server.common.data.id.TenantProfileId; |
26 | 26 | import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
27 | 27 | import org.thingsboard.server.common.data.tenant.profile.TenantProfileData; |
28 | +import org.thingsboard.server.common.data.validation.Length; | |
28 | 29 | import org.thingsboard.server.common.data.validation.NoXss; |
29 | 30 | |
30 | 31 | import java.io.ByteArrayInputStream; |
... | ... | @@ -42,6 +43,7 @@ public class TenantProfile extends SearchTextBased<TenantProfileId> implements H |
42 | 43 | private static final long serialVersionUID = 3021989561267192281L; |
43 | 44 | |
44 | 45 | @NoXss |
46 | + @Length(fieldName = "name") | |
45 | 47 | @ApiModelProperty(position = 3, value = "Name of the tenant profile", example = "High Priority Tenants") |
46 | 48 | private String name; |
47 | 49 | @NoXss | ... | ... |
... | ... | @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.EntityId; |
26 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
27 | 27 | import org.thingsboard.server.common.data.id.UserId; |
28 | 28 | import org.thingsboard.server.common.data.security.Authority; |
29 | - | |
29 | +import org.thingsboard.server.common.data.validation.Length; | |
30 | 30 | import org.thingsboard.server.common.data.validation.NoXss; |
31 | 31 | |
32 | 32 | @ApiModel |
... | ... | @@ -40,8 +40,10 @@ public class User extends SearchTextBasedWithAdditionalInfo<UserId> implements H |
40 | 40 | private String email; |
41 | 41 | private Authority authority; |
42 | 42 | @NoXss |
43 | + @Length(fieldName = "first name") | |
43 | 44 | private String firstName; |
44 | 45 | @NoXss |
46 | + @Length(fieldName = "last name") | |
45 | 47 | private String lastName; |
46 | 48 | |
47 | 49 | public User() { | ... | ... |
... | ... | @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.id.AlarmId; |
30 | 30 | import org.thingsboard.server.common.data.id.CustomerId; |
31 | 31 | import org.thingsboard.server.common.data.id.EntityId; |
32 | 32 | import org.thingsboard.server.common.data.id.TenantId; |
33 | +import org.thingsboard.server.common.data.validation.Length; | |
33 | 34 | |
34 | 35 | import java.util.List; |
35 | 36 | |
... | ... | @@ -49,6 +50,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId, Ha |
49 | 50 | private CustomerId customerId; |
50 | 51 | |
51 | 52 | @ApiModelProperty(position = 6, required = true, value = "representing type of the Alarm", example = "High Temperature Alarm") |
53 | + @Length(fieldName = "type") | |
52 | 54 | private String type; |
53 | 55 | @ApiModelProperty(position = 7, required = true, value = "JSON object with alarm originator id") |
54 | 56 | private EntityId originator; | ... | ... |
... | ... | @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; |
26 | 26 | import org.thingsboard.server.common.data.id.AssetId; |
27 | 27 | import org.thingsboard.server.common.data.id.CustomerId; |
28 | 28 | import org.thingsboard.server.common.data.id.TenantId; |
29 | +import org.thingsboard.server.common.data.validation.Length; | |
29 | 30 | import org.thingsboard.server.common.data.validation.NoXss; |
30 | 31 | |
31 | 32 | import java.util.Optional; |
... | ... | @@ -39,10 +40,13 @@ public class Asset extends SearchTextBasedWithAdditionalInfo<AssetId> implements |
39 | 40 | private TenantId tenantId; |
40 | 41 | private CustomerId customerId; |
41 | 42 | @NoXss |
43 | + @Length(fieldName = "name") | |
42 | 44 | private String name; |
43 | 45 | @NoXss |
46 | + @Length(fieldName = "type") | |
44 | 47 | private String type; |
45 | 48 | @NoXss |
49 | + @Length(fieldName = "label") | |
46 | 50 | private String label; |
47 | 51 | |
48 | 52 | public Asset() { | ... | ... |
... | ... | @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.CustomerId; |
28 | 28 | import org.thingsboard.server.common.data.id.EdgeId; |
29 | 29 | import org.thingsboard.server.common.data.id.RuleChainId; |
30 | 30 | import org.thingsboard.server.common.data.id.TenantId; |
31 | +import org.thingsboard.server.common.data.validation.Length; | |
31 | 32 | |
32 | 33 | @ApiModel |
33 | 34 | @EqualsAndHashCode(callSuper = true) |
... | ... | @@ -40,8 +41,11 @@ public class Edge extends SearchTextBasedWithAdditionalInfo<EdgeId> implements H |
40 | 41 | private TenantId tenantId; |
41 | 42 | private CustomerId customerId; |
42 | 43 | private RuleChainId rootRuleChainId; |
44 | + @Length(fieldName = "name") | |
43 | 45 | private String name; |
46 | + @Length(fieldName = "type") | |
44 | 47 | private String type; |
48 | + @Length(fieldName = "label") | |
45 | 49 | private String label; |
46 | 50 | private String routingKey; |
47 | 51 | private String secret; | ... | ... |
... | ... | @@ -15,8 +15,7 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.data.kv; |
17 | 17 | |
18 | -import com.fasterxml.jackson.databind.JsonNode; | |
19 | - | |
18 | +import javax.validation.Valid; | |
20 | 19 | import java.util.Optional; |
21 | 20 | |
22 | 21 | /** |
... | ... | @@ -27,6 +26,7 @@ public class BaseAttributeKvEntry implements AttributeKvEntry { |
27 | 26 | private static final long serialVersionUID = -6460767583563159407L; |
28 | 27 | |
29 | 28 | private final long lastUpdateTs; |
29 | + @Valid | |
30 | 30 | private final KvEntry kv; |
31 | 31 | |
32 | 32 | public BaseAttributeKvEntry(KvEntry kv, long lastUpdateTs) { | ... | ... |
... | ... | @@ -15,11 +15,14 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.data.kv; |
17 | 17 | |
18 | +import org.thingsboard.server.common.data.validation.Length; | |
19 | + | |
18 | 20 | import java.util.Objects; |
19 | 21 | import java.util.Optional; |
20 | 22 | |
21 | 23 | public abstract class BasicKvEntry implements KvEntry { |
22 | 24 | |
25 | + @Length(fieldName = "attribute key") | |
23 | 26 | private final String key; |
24 | 27 | |
25 | 28 | protected BasicKvEntry(String key) { | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import io.swagger.annotations.ApiModelProperty; |
22 | 22 | import lombok.extern.slf4j.Slf4j; |
23 | 23 | import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; |
24 | 24 | import org.thingsboard.server.common.data.id.EntityId; |
25 | +import org.thingsboard.server.common.data.validation.Length; | |
25 | 26 | |
26 | 27 | import java.io.Serializable; |
27 | 28 | |
... | ... | @@ -37,6 +38,7 @@ public class EntityRelation implements Serializable { |
37 | 38 | |
38 | 39 | private EntityId from; |
39 | 40 | private EntityId to; |
41 | + @Length(fieldName = "type") | |
40 | 42 | private String type; |
41 | 43 | private RelationTypeGroup typeGroup; |
42 | 44 | private transient JsonNode additionalInfo; | ... | ... |
... | ... | @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; |
28 | 28 | import org.thingsboard.server.common.data.id.RuleChainId; |
29 | 29 | import org.thingsboard.server.common.data.id.RuleNodeId; |
30 | 30 | import org.thingsboard.server.common.data.id.TenantId; |
31 | +import org.thingsboard.server.common.data.validation.Length; | |
31 | 32 | import org.thingsboard.server.common.data.validation.NoXss; |
32 | 33 | |
33 | 34 | @ApiModel |
... | ... | @@ -41,6 +42,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> im |
41 | 42 | @ApiModelProperty(position = 3, required = true, value = "JSON object with Tenant Id.", readOnly = true) |
42 | 43 | private TenantId tenantId; |
43 | 44 | @NoXss |
45 | + @Length(fieldName = "name") | |
44 | 46 | @ApiModelProperty(position = 4, required = true, value = "Rule Chain name", example = "Humidity data processing") |
45 | 47 | private String name; |
46 | 48 | @ApiModelProperty(position = 5, value = "Rule Chain type. 'EDGE' rule chains are processing messages on the edge devices only.", example = "A4B72CCDFF33") | ... | ... |
... | ... | @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.HasName; |
26 | 26 | import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; |
27 | 27 | import org.thingsboard.server.common.data.id.RuleChainId; |
28 | 28 | import org.thingsboard.server.common.data.id.RuleNodeId; |
29 | +import org.thingsboard.server.common.data.validation.Length; | |
29 | 30 | |
30 | 31 | @ApiModel |
31 | 32 | @Data |
... | ... | @@ -37,8 +38,10 @@ public class RuleNode extends SearchTextBasedWithAdditionalInfo<RuleNodeId> impl |
37 | 38 | |
38 | 39 | @ApiModelProperty(position = 3, value = "JSON object with the Rule Chain Id. ", readOnly = true) |
39 | 40 | private RuleChainId ruleChainId; |
41 | + @Length(fieldName = "type") | |
40 | 42 | @ApiModelProperty(position = 4, value = "Full Java Class Name of the rule node implementation. ", example = "com.mycompany.iot.rule.engine.ProcessingNode") |
41 | 43 | private String type; |
44 | + @Length(fieldName = "name") | |
42 | 45 | @ApiModelProperty(position = 5, value = "User defined name of the rule node. Used on UI and for logging. ", example = "Process sensor reading") |
43 | 46 | private String name; |
44 | 47 | @ApiModelProperty(position = 6, value = "Enable/disable debug. ", example = "false") | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.validation; | |
17 | + | |
18 | +import javax.validation.Constraint; | |
19 | +import javax.validation.Payload; | |
20 | +import java.lang.annotation.ElementType; | |
21 | +import java.lang.annotation.Retention; | |
22 | +import java.lang.annotation.RetentionPolicy; | |
23 | +import java.lang.annotation.Target; | |
24 | + | |
25 | +@Retention(RetentionPolicy.RUNTIME) | |
26 | +@Target(ElementType.FIELD) | |
27 | +@Constraint(validatedBy = {}) | |
28 | +public @interface Length { | |
29 | + String message() default "length of {fieldName} must be equal or less than {max}"; | |
30 | + | |
31 | + String fieldName(); | |
32 | + | |
33 | + int max() default 255; | |
34 | + | |
35 | + Class<?>[] groups() default {}; | |
36 | + | |
37 | + Class<? extends Payload>[] payload() default {}; | |
38 | +} | ... | ... |
... | ... | @@ -23,10 +23,9 @@ import org.thingsboard.server.common.data.HasTenantId; |
23 | 23 | import org.thingsboard.server.common.data.SearchTextBased; |
24 | 24 | import org.thingsboard.server.common.data.id.TenantId; |
25 | 25 | import org.thingsboard.server.common.data.id.WidgetsBundleId; |
26 | +import org.thingsboard.server.common.data.validation.Length; | |
26 | 27 | import org.thingsboard.server.common.data.validation.NoXss; |
27 | 28 | |
28 | -import java.util.Arrays; | |
29 | - | |
30 | 29 | @ApiModel |
31 | 30 | public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements HasTenantId { |
32 | 31 | |
... | ... | @@ -38,12 +37,14 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H |
38 | 37 | private TenantId tenantId; |
39 | 38 | |
40 | 39 | @NoXss |
40 | + @Length(fieldName = "alias") | |
41 | 41 | @Getter |
42 | 42 | @Setter |
43 | 43 | @ApiModelProperty(position = 4, value = "Unique alias that is used in widget types as a reference widget bundle", readOnly = true) |
44 | 44 | private String alias; |
45 | 45 | |
46 | 46 | @NoXss |
47 | + @Length(fieldName = "title") | |
47 | 48 | @Getter |
48 | 49 | @Setter |
49 | 50 | @ApiModelProperty(position = 5, value = "Title used in search and UI", readOnly = true) |
... | ... | @@ -55,6 +56,7 @@ public class WidgetsBundle extends SearchTextBased<WidgetsBundleId> implements H |
55 | 56 | private String image; |
56 | 57 | |
57 | 58 | @NoXss |
59 | + @Length(fieldName = "description") | |
58 | 60 | @Getter |
59 | 61 | @Setter |
60 | 62 | @ApiModelProperty(position = 7, value = "Description", readOnly = true) | ... | ... |
... | ... | @@ -41,6 +41,7 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
41 | 41 | import org.thingsboard.server.common.data.relation.RelationsSearchParameters; |
42 | 42 | import org.thingsboard.server.dao.entity.EntityService; |
43 | 43 | import org.thingsboard.server.dao.exception.DataValidationException; |
44 | +import org.thingsboard.server.dao.service.ConstraintValidator; | |
44 | 45 | |
45 | 46 | import javax.annotation.Nullable; |
46 | 47 | import java.util.ArrayList; |
... | ... | @@ -559,6 +560,7 @@ public class BaseRelationService implements RelationService { |
559 | 560 | if (relation == null) { |
560 | 561 | throw new DataValidationException("Relation type should be specified!"); |
561 | 562 | } |
563 | + ConstraintValidator.validateFields(relation); | |
562 | 564 | validate(relation.getFrom(), relation.getTo(), relation.getType(), relation.getTypeGroup()); |
563 | 565 | } |
564 | 566 | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao.service; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.hibernate.validator.HibernateValidator; | |
20 | +import org.hibernate.validator.HibernateValidatorConfiguration; | |
21 | +import org.hibernate.validator.cfg.ConstraintMapping; | |
22 | +import org.thingsboard.server.common.data.validation.Length; | |
23 | +import org.thingsboard.server.common.data.validation.NoXss; | |
24 | + | |
25 | +import javax.validation.ConstraintViolation; | |
26 | +import javax.validation.Validation; | |
27 | +import javax.validation.ValidationException; | |
28 | +import javax.validation.Validator; | |
29 | +import java.util.List; | |
30 | +import java.util.Set; | |
31 | +import java.util.stream.Collectors; | |
32 | + | |
33 | +@Slf4j | |
34 | +public class ConstraintValidator { | |
35 | + | |
36 | + private static Validator fieldsValidator; | |
37 | + | |
38 | + static { | |
39 | + initializeValidators(); | |
40 | + } | |
41 | + | |
42 | + public static void validateFields(Object data) { | |
43 | + Set<ConstraintViolation<Object>> constraintsViolations = fieldsValidator.validate(data); | |
44 | + List<String> validationErrors = constraintsViolations.stream() | |
45 | + .map(ConstraintViolation::getMessage) | |
46 | + .distinct() | |
47 | + .collect(Collectors.toList()); | |
48 | + if (!validationErrors.isEmpty()) { | |
49 | + throw new ValidationException("Validation error: " + String.join(", ", validationErrors)); | |
50 | + } | |
51 | + } | |
52 | + | |
53 | + private static void initializeValidators() { | |
54 | + HibernateValidatorConfiguration validatorConfiguration = Validation.byProvider(HibernateValidator.class).configure(); | |
55 | + | |
56 | + ConstraintMapping constraintMapping = validatorConfiguration.createConstraintMapping(); | |
57 | + constraintMapping.constraintDefinition(NoXss.class).validatedBy(NoXssValidator.class); | |
58 | + constraintMapping.constraintDefinition(Length.class).validatedBy(StringLengthValidator.class); | |
59 | + validatorConfiguration.addMapping(constraintMapping); | |
60 | + | |
61 | + fieldsValidator = validatorConfiguration.buildValidatorFactory().getValidator(); | |
62 | + } | |
63 | +} | ... | ... |
... | ... | @@ -17,51 +17,32 @@ package org.thingsboard.server.dao.service; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.databind.JsonNode; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | -import org.hibernate.validator.HibernateValidator; | |
21 | -import org.hibernate.validator.HibernateValidatorConfiguration; | |
22 | -import org.hibernate.validator.cfg.ConstraintMapping; | |
23 | 20 | import org.thingsboard.server.common.data.BaseData; |
24 | 21 | import org.thingsboard.server.common.data.EntityType; |
25 | 22 | import org.thingsboard.server.common.data.id.TenantId; |
26 | -import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; | |
27 | -import org.thingsboard.server.common.data.validation.NoXss; | |
28 | 23 | import org.thingsboard.server.dao.TenantEntityDao; |
29 | 24 | import org.thingsboard.server.dao.TenantEntityWithDataDao; |
30 | 25 | import org.thingsboard.server.dao.exception.DataValidationException; |
31 | 26 | |
32 | -import javax.validation.ConstraintViolation; | |
33 | -import javax.validation.Validation; | |
34 | -import javax.validation.Validator; | |
35 | 27 | import java.util.HashSet; |
36 | 28 | import java.util.Iterator; |
37 | -import java.util.List; | |
38 | 29 | import java.util.Set; |
39 | 30 | import java.util.function.Function; |
40 | 31 | import java.util.regex.Matcher; |
41 | 32 | import java.util.regex.Pattern; |
42 | -import java.util.stream.Collectors; | |
43 | 33 | |
44 | 34 | @Slf4j |
45 | 35 | public abstract class DataValidator<D extends BaseData<?>> { |
46 | 36 | private static final Pattern EMAIL_PATTERN = |
47 | 37 | Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$", Pattern.CASE_INSENSITIVE); |
48 | 38 | |
49 | - private static Validator fieldsValidator; | |
50 | - | |
51 | - static { | |
52 | - initializeFieldsValidator(); | |
53 | - } | |
54 | - | |
55 | 39 | public void validate(D data, Function<D, TenantId> tenantIdFunction) { |
56 | 40 | try { |
57 | 41 | if (data == null) { |
58 | 42 | throw new DataValidationException("Data object can't be null!"); |
59 | 43 | } |
60 | 44 | |
61 | - List<String> validationErrors = validateFields(data); | |
62 | - if (!validationErrors.isEmpty()) { | |
63 | - throw new IllegalArgumentException("Validation error: " + String.join(", ", validationErrors)); | |
64 | - } | |
45 | + ConstraintValidator.validateFields(data); | |
65 | 46 | |
66 | 47 | TenantId tenantId = tenantIdFunction.apply(data); |
67 | 48 | validateDataImpl(tenantId, data); |
... | ... | @@ -104,14 +85,6 @@ public abstract class DataValidator<D extends BaseData<?>> { |
104 | 85 | return emailMatcher.matches(); |
105 | 86 | } |
106 | 87 | |
107 | - private List<String> validateFields(D data) { | |
108 | - Set<ConstraintViolation<D>> constraintsViolations = fieldsValidator.validate(data); | |
109 | - return constraintsViolations.stream() | |
110 | - .map(ConstraintViolation::getMessage) | |
111 | - .distinct() | |
112 | - .collect(Collectors.toList()); | |
113 | - } | |
114 | - | |
115 | 88 | protected void validateNumberOfEntitiesPerTenant(TenantId tenantId, |
116 | 89 | TenantEntityDao tenantEntityDao, |
117 | 90 | long maxEntities, |
... | ... | @@ -126,10 +99,10 @@ public abstract class DataValidator<D extends BaseData<?>> { |
126 | 99 | } |
127 | 100 | |
128 | 101 | protected void validateMaxSumDataSizePerTenant(TenantId tenantId, |
129 | - TenantEntityWithDataDao dataDao, | |
130 | - long maxSumDataSize, | |
131 | - long currentDataSize, | |
132 | - EntityType entityType) { | |
102 | + TenantEntityWithDataDao dataDao, | |
103 | + long maxSumDataSize, | |
104 | + long currentDataSize, | |
105 | + EntityType entityType) { | |
133 | 106 | if (maxSumDataSize > 0) { |
134 | 107 | if (dataDao.sumDataSizeByTenantId(tenantId) + currentDataSize > maxSumDataSize) { |
135 | 108 | 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<?>> { |
156 | 129 | } |
157 | 130 | } |
158 | 131 | |
159 | - private static void initializeFieldsValidator() { | |
160 | - HibernateValidatorConfiguration validatorConfiguration = Validation.byProvider(HibernateValidator.class).configure(); | |
161 | - ConstraintMapping constraintMapping = validatorConfiguration.createConstraintMapping(); | |
162 | - constraintMapping.constraintDefinition(NoXss.class).validatedBy(NoXssValidator.class); | |
163 | - validatorConfiguration.addMapping(constraintMapping); | |
164 | - | |
165 | - fieldsValidator = validatorConfiguration.buildValidatorFactory().getValidator(); | |
166 | - } | |
167 | 132 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao.service; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.thingsboard.server.common.data.StringUtils; | |
20 | +import org.thingsboard.server.common.data.validation.Length; | |
21 | + | |
22 | +import javax.validation.ConstraintValidator; | |
23 | +import javax.validation.ConstraintValidatorContext; | |
24 | + | |
25 | +@Slf4j | |
26 | +public class StringLengthValidator implements ConstraintValidator<Length, String> { | |
27 | + private int max; | |
28 | + | |
29 | + @Override | |
30 | + public boolean isValid(String value, ConstraintValidatorContext context) { | |
31 | + if (StringUtils.isEmpty(value)) { | |
32 | + return true; | |
33 | + } | |
34 | + return value.length() <= max; | |
35 | + } | |
36 | + | |
37 | + @Override | |
38 | + public void initialize(Length constraintAnnotation) { | |
39 | + this.max = constraintAnnotation.max(); | |
40 | + } | |
41 | +} | ... | ... |
... | ... | @@ -38,6 +38,7 @@ import org.thingsboard.server.common.data.page.PageLink; |
38 | 38 | import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; |
39 | 39 | import org.thingsboard.server.dao.exception.DataValidationException; |
40 | 40 | |
41 | +import javax.validation.ValidationException; | |
41 | 42 | import java.nio.ByteBuffer; |
42 | 43 | import java.util.ArrayList; |
43 | 44 | import java.util.Collections; |
... | ... | @@ -673,8 +674,8 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { |
673 | 674 | firmwareInfo.setUrl(URL); |
674 | 675 | firmwareInfo.setTenantId(tenantId); |
675 | 676 | |
676 | - thrown.expect(DataValidationException.class); | |
677 | - thrown.expectMessage("The length of title should be equal or shorter than 255"); | |
677 | + thrown.expect(ValidationException.class); | |
678 | + thrown.expectMessage("length of title must be equal or less than 255"); | |
678 | 679 | |
679 | 680 | otaPackageService.saveOtaPackageInfo(firmwareInfo, true); |
680 | 681 | } |
... | ... | @@ -689,7 +690,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { |
689 | 690 | firmwareInfo.setTitle(TITLE); |
690 | 691 | |
691 | 692 | firmwareInfo.setVersion(RandomStringUtils.random(257)); |
692 | - thrown.expectMessage("The length of version should be equal or shorter than 255"); | |
693 | + thrown.expectMessage("length of version must be equal or less than 255"); | |
693 | 694 | |
694 | 695 | otaPackageService.saveOtaPackageInfo(firmwareInfo, true); |
695 | 696 | } | ... | ... |
... | ... | @@ -36,6 +36,9 @@ |
36 | 36 | <mat-error *ngIf="attributeFormGroup.get('key').hasError('required')"> |
37 | 37 | {{ 'attribute.key-required' | translate }} |
38 | 38 | </mat-error> |
39 | + <mat-error *ngIf="attributeFormGroup.get('key').hasError('maxlength')"> | |
40 | + {{ 'attribute.key-max-length' | translate }} | |
41 | + </mat-error> | |
39 | 42 | </mat-form-field> |
40 | 43 | <tb-value-input |
41 | 44 | formControlName="value" | ... | ... |
... | ... | @@ -56,7 +56,7 @@ export class AddAttributeDialogComponent extends DialogComponent<AddAttributeDia |
56 | 56 | |
57 | 57 | ngOnInit(): void { |
58 | 58 | this.attributeFormGroup = this.fb.group({ |
59 | - key: ['', [Validators.required]], | |
59 | + key: ['', [Validators.required, Validators.maxLength(255)]], | |
60 | 60 | value: [null, [Validators.required]] |
61 | 61 | }); |
62 | 62 | } | ... | ... |
... | ... | @@ -37,15 +37,15 @@ export abstract class ContactBasedComponent<T extends ContactBased<HasId>> exten |
37 | 37 | |
38 | 38 | buildForm(entity: T): FormGroup { |
39 | 39 | const entityForm = this.buildEntityForm(entity); |
40 | - entityForm.addControl('country', this.fb.control(entity ? entity.country : '', [])); | |
41 | - entityForm.addControl('city', this.fb.control(entity ? entity.city : '', [])); | |
42 | - entityForm.addControl('state', this.fb.control(entity ? entity.state : '', [])); | |
40 | + entityForm.addControl('country', this.fb.control(entity ? entity.country : '', [Validators.maxLength(255)])); | |
41 | + entityForm.addControl('city', this.fb.control(entity ? entity.city : '', [Validators.maxLength(255)])); | |
42 | + entityForm.addControl('state', this.fb.control(entity ? entity.state : '', [Validators.maxLength(255)])); | |
43 | 43 | entityForm.addControl('zip', this.fb.control(entity ? entity.zip : '', |
44 | 44 | this.zipValidators(entity ? entity.country : '') |
45 | 45 | )); |
46 | 46 | entityForm.addControl('address', this.fb.control(entity ? entity.address : '', [])); |
47 | 47 | entityForm.addControl('address2', this.fb.control(entity ? entity.address2 : '', [])); |
48 | - entityForm.addControl('phone', this.fb.control(entity ? entity.phone : '', [])); | |
48 | + entityForm.addControl('phone', this.fb.control(entity ? entity.phone : '', [Validators.maxLength(255)])); | |
49 | 49 | entityForm.addControl('email', this.fb.control(entity ? entity.email : '', [Validators.email])); |
50 | 50 | return entityForm; |
51 | 51 | } | ... | ... |
... | ... | @@ -41,6 +41,9 @@ |
41 | 41 | <mat-error *ngIf="deviceProfileDetailsFormGroup.get('name').hasError('required')"> |
42 | 42 | {{ 'device-profile.name-required' | translate }} |
43 | 43 | </mat-error> |
44 | + <mat-error *ngIf="deviceProfileDetailsFormGroup.get('name').hasError('maxlength')"> | |
45 | + {{ 'device-profile.name-max-length' | translate }} | |
46 | + </mat-error> | |
44 | 47 | </mat-form-field> |
45 | 48 | <tb-rule-chain-autocomplete |
46 | 49 | labelText="device-profile.default-rule-chain" | ... | ... |
... | ... | @@ -105,7 +105,7 @@ export class AddDeviceProfileDialogComponent extends |
105 | 105 | super(store, router, dialogRef); |
106 | 106 | this.deviceProfileDetailsFormGroup = this.fb.group( |
107 | 107 | { |
108 | - name: [data.deviceProfileName, [Validators.required]], | |
108 | + name: [data.deviceProfileName, [Validators.required, Validators.maxLength(255)]], | |
109 | 109 | type: [DeviceProfileType.DEFAULT, [Validators.required]], |
110 | 110 | image: [null, []], |
111 | 111 | defaultRuleChainId: [null, []], | ... | ... |
... | ... | @@ -54,6 +54,9 @@ |
54 | 54 | <mat-error *ngIf="entityForm.get('name').hasError('required')"> |
55 | 55 | {{ 'device-profile.name-required' | translate }} |
56 | 56 | </mat-error> |
57 | + <mat-error *ngIf="entityForm.get('name').hasError('maxlength')"> | |
58 | + {{ 'device-profile.name-max-length' | translate }} | |
59 | + </mat-error> | |
57 | 60 | </mat-form-field> |
58 | 61 | <tb-rule-chain-autocomplete |
59 | 62 | labelText="device-profile.default-rule-chain" | ... | ... |
... | ... | @@ -103,7 +103,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> { |
103 | 103 | }; |
104 | 104 | const form = this.fb.group( |
105 | 105 | { |
106 | - name: [entity ? entity.name : '', [Validators.required]], | |
106 | + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], | |
107 | 107 | type: [entity ? entity.type : null, [Validators.required]], |
108 | 108 | image: [entity ? entity.image : null], |
109 | 109 | transportType: [entity ? entity.transportType : null, [Validators.required]], | ... | ... |
... | ... | @@ -48,6 +48,9 @@ |
48 | 48 | <mat-error *ngIf="entityForm.get('name').hasError('required')"> |
49 | 49 | {{ 'tenant-profile.name-required' | translate }} |
50 | 50 | </mat-error> |
51 | + <mat-error *ngIf="entityForm.get('name').hasError('maxlength')"> | |
52 | + {{ 'tenant-profile.name-max-length' | translate }} | |
53 | + </mat-error> | |
51 | 54 | </mat-form-field> |
52 | 55 | <div fxLayout="column"> |
53 | 56 | <mat-checkbox class="hinted-checkbox" formControlName="isolatedTbCore"> | ... | ... |
... | ... | @@ -59,7 +59,7 @@ export class TenantProfileComponent extends EntityComponent<TenantProfile> { |
59 | 59 | buildForm(entity: TenantProfile): FormGroup { |
60 | 60 | return this.fb.group( |
61 | 61 | { |
62 | - name: [entity ? entity.name : '', [Validators.required]], | |
62 | + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], | |
63 | 63 | isolatedTbCore: [entity ? entity.isolatedTbCore : false, []], |
64 | 64 | isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], |
65 | 65 | profileData: [entity && !this.isAdd ? entity.profileData : { | ... | ... |
... | ... | @@ -44,10 +44,16 @@ |
44 | 44 | <mat-error *ngIf="deviceWizardFormGroup.get('name').hasError('required')"> |
45 | 45 | {{ 'device.name-required' | translate }} |
46 | 46 | </mat-error> |
47 | + <mat-error *ngIf="deviceWizardFormGroup.get('name').hasError('maxlength')"> | |
48 | + {{ 'device.name-max-length' | translate }} | |
49 | + </mat-error> | |
47 | 50 | </mat-form-field> |
48 | 51 | <mat-form-field class="mat-block"> |
49 | 52 | <mat-label translate>device.label</mat-label> |
50 | 53 | <input matInput formControlName="label"> |
54 | + <mat-error *ngIf="deviceWizardFormGroup.get('label').hasError('maxlength')"> | |
55 | + {{ 'device.label-max-length' | translate }} | |
56 | + </mat-error> | |
51 | 57 | </mat-form-field> |
52 | 58 | <div fxLayout="row" fxLayoutGap="16px"> |
53 | 59 | <mat-radio-group fxLayout="column" formControlName="addProfileType" fxLayoutAlign="space-around"> | ... | ... |
... | ... | @@ -105,8 +105,8 @@ export class DeviceWizardDialogComponent extends |
105 | 105 | private fb: FormBuilder) { |
106 | 106 | super(store, router, dialogRef); |
107 | 107 | this.deviceWizardFormGroup = this.fb.group({ |
108 | - name: ['', Validators.required], | |
109 | - label: [''], | |
108 | + name: ['', [Validators.required, Validators.maxLength(255)]], | |
109 | + label: ['', Validators.maxLength(255)], | |
110 | 110 | gateway: [false], |
111 | 111 | overwriteActivityTime: [false], |
112 | 112 | addProfileType: [0], | ... | ... |
... | ... | @@ -56,6 +56,9 @@ |
56 | 56 | <mat-error *ngIf="entityForm.get('title').hasError('required')"> |
57 | 57 | {{ 'resource.title-required' | translate }} |
58 | 58 | </mat-error> |
59 | + <mat-error *ngIf="entityForm.get('title').hasError('maxlength')"> | |
60 | + {{ 'resource.title-max-length' | translate }} | |
61 | + </mat-error> | |
59 | 62 | </mat-form-field> |
60 | 63 | <tb-file-input *ngIf="isAdd" |
61 | 64 | formControlName="data" | ... | ... |
... | ... | @@ -29,7 +29,7 @@ import { |
29 | 29 | ResourceTypeMIMETypes, |
30 | 30 | ResourceTypeTranslationMap |
31 | 31 | } from '@shared/models/resource.models'; |
32 | -import { pairwise, startWith, takeUntil } from 'rxjs/operators'; | |
32 | +import {filter, pairwise, startWith, takeUntil} from 'rxjs/operators'; | |
33 | 33 | import { ActionNotificationShow } from '@core/notification/notification.actions'; |
34 | 34 | |
35 | 35 | @Component({ |
... | ... | @@ -57,16 +57,14 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme |
57 | 57 | super.ngOnInit(); |
58 | 58 | this.entityForm.get('resourceType').valueChanges.pipe( |
59 | 59 | startWith(ResourceType.LWM2M_MODEL), |
60 | - pairwise(), | |
60 | + filter(() => this.isAdd), | |
61 | 61 | takeUntil(this.destroy$) |
62 | - ).subscribe(([previousType, type]) => { | |
63 | - if (previousType === this.resourceType.LWM2M_MODEL) { | |
64 | - this.entityForm.get('title').setValidators(Validators.required); | |
65 | - this.entityForm.get('title').updateValueAndValidity({emitEvent: false}); | |
66 | - } | |
62 | + ).subscribe((type) => { | |
67 | 63 | if (type === this.resourceType.LWM2M_MODEL) { |
68 | - this.entityForm.get('title').clearValidators(); | |
69 | - this.entityForm.get('title').updateValueAndValidity({emitEvent: false}); | |
64 | + this.entityForm.get('title').disable({emitEvent: false}); | |
65 | + this.entityForm.patchValue({title: ''}, {emitEvent: false}); | |
66 | + } else { | |
67 | + this.entityForm.get('title').enable({emitEvent: false}) | |
70 | 68 | } |
71 | 69 | this.entityForm.patchValue({ |
72 | 70 | data: null, |
... | ... | @@ -92,11 +90,8 @@ export class ResourcesLibraryComponent extends EntityComponent<Resource> impleme |
92 | 90 | buildForm(entity: Resource): FormGroup { |
93 | 91 | const form = this.fb.group( |
94 | 92 | { |
95 | - title: [entity ? entity.title : '', []], | |
96 | - resourceType: [{ | |
97 | - value: entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, | |
98 | - disabled: !this.isAdd | |
99 | - }, [Validators.required]], | |
93 | + title: [entity ? entity.title : "", [Validators.required, Validators.maxLength(255)]], | |
94 | + resourceType: [entity?.resourceType ? entity.resourceType : ResourceType.LWM2M_MODEL, [Validators.required]], | |
100 | 95 | fileName: [entity ? entity.fileName : null, [Validators.required]], |
101 | 96 | } |
102 | 97 | ); | ... | ... |
... | ... | @@ -76,6 +76,9 @@ |
76 | 76 | <mat-error *ngIf="entityForm.get('name').hasError('required')"> |
77 | 77 | {{ 'asset.name-required' | translate }} |
78 | 78 | </mat-error> |
79 | + <mat-error *ngIf="entityForm.get('name').hasError('maxlength')"> | |
80 | + {{ 'asset.name-max-length' | translate }} | |
81 | + </mat-error> | |
79 | 82 | </mat-form-field> |
80 | 83 | <tb-entity-subtype-autocomplete |
81 | 84 | formControlName="type" |
... | ... | @@ -86,6 +89,9 @@ |
86 | 89 | <mat-form-field class="mat-block"> |
87 | 90 | <mat-label translate>asset.label</mat-label> |
88 | 91 | <input matInput formControlName="label"> |
92 | + <mat-error *ngIf="entityForm.get('label').hasError('maxlength')"> | |
93 | + {{ 'asset.label-max-length' | translate }} | |
94 | + </mat-error> | |
89 | 95 | </mat-form-field> |
90 | 96 | <div formGroupName="additionalInfo"> |
91 | 97 | <mat-form-field class="mat-block"> | ... | ... |
... | ... | @@ -66,9 +66,9 @@ export class AssetComponent extends EntityComponent<AssetInfo> { |
66 | 66 | buildForm(entity: AssetInfo): FormGroup { |
67 | 67 | return this.fb.group( |
68 | 68 | { |
69 | - name: [entity ? entity.name : '', [Validators.required]], | |
70 | - type: [entity ? entity.type : null, [Validators.required]], | |
71 | - label: [entity ? entity.label : ''], | |
69 | + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], | |
70 | + type: [entity ? entity.type : null, [Validators.required, Validators.maxLength(255)]], | |
71 | + label: [entity ? entity.label : '', Validators.maxLength(255)], | |
72 | 72 | additionalInfo: this.fb.group( |
73 | 73 | { |
74 | 74 | description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], | ... | ... |
... | ... | @@ -73,6 +73,9 @@ |
73 | 73 | <mat-error *ngIf="entityForm.get('title').hasError('required')"> |
74 | 74 | {{ 'customer.title-required' | translate }} |
75 | 75 | </mat-error> |
76 | + <mat-error *ngIf="entityForm.get('title').hasError('maxlength')"> | |
77 | + {{ 'customer.title-max-length' | translate }} | |
78 | + </mat-error> | |
76 | 79 | </mat-form-field> |
77 | 80 | <div formGroupName="additionalInfo" fxLayout="column"> |
78 | 81 | <mat-form-field class="mat-block"> | ... | ... |
... | ... | @@ -58,7 +58,7 @@ export class CustomerComponent extends ContactBasedComponent<Customer> { |
58 | 58 | buildEntityForm(entity: Customer): FormGroup { |
59 | 59 | return this.fb.group( |
60 | 60 | { |
61 | - title: [entity ? entity.title : '', [Validators.required]], | |
61 | + title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], | |
62 | 62 | additionalInfo: this.fb.group( |
63 | 63 | { |
64 | 64 | description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], | ... | ... |
... | ... | @@ -104,6 +104,9 @@ |
104 | 104 | <mat-error *ngIf="entityForm.get('title').hasError('required')"> |
105 | 105 | {{ 'dashboard.title-required' | translate }} |
106 | 106 | </mat-error> |
107 | + <mat-error *ngIf="entityForm.get('title').hasError('maxlength')"> | |
108 | + {{ 'dashboard.title-max-length' | translate }} | |
109 | + </mat-error> | |
107 | 110 | </mat-form-field> |
108 | 111 | <div formGroupName="configuration" fxLayout="column"> |
109 | 112 | <mat-form-field class="mat-block"> | ... | ... |
... | ... | @@ -80,7 +80,7 @@ export class DashboardFormComponent extends EntityComponent<Dashboard> { |
80 | 80 | this.updateFields(entity); |
81 | 81 | return this.fb.group( |
82 | 82 | { |
83 | - title: [entity ? entity.title : '', [Validators.required]], | |
83 | + title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], | |
84 | 84 | image: [entity ? entity.image : null], |
85 | 85 | mobileHide: [entity ? entity.mobileHide : false], |
86 | 86 | mobileOrder: [entity ? entity.mobileOrder : null, [Validators.pattern(/^-?[0-9]+$/)]], | ... | ... |
... | ... | @@ -89,6 +89,9 @@ |
89 | 89 | <mat-error *ngIf="entityForm.get('name').hasError('required')"> |
90 | 90 | {{ 'device.name-required' | translate }} |
91 | 91 | </mat-error> |
92 | + <mat-error *ngIf="entityForm.get('name').hasError('maxlength')"> | |
93 | + {{ 'device.name-max-length' | translate }} | |
94 | + </mat-error> | |
92 | 95 | </mat-form-field> |
93 | 96 | <tb-device-profile-autocomplete |
94 | 97 | [selectDefaultProfile]="isAdd" |
... | ... | @@ -100,6 +103,9 @@ |
100 | 103 | <mat-form-field class="mat-block"> |
101 | 104 | <mat-label translate>device.label</mat-label> |
102 | 105 | <input matInput formControlName="label"> |
106 | + <mat-error *ngIf="entityForm.get('label').hasError('maxlength')"> | |
107 | + {{ 'device.label-max-length' | translate }} | |
108 | + </mat-error> | |
103 | 109 | </mat-form-field> |
104 | 110 | <tb-ota-package-autocomplete |
105 | 111 | [useFullEntityId]="true" | ... | ... |
... | ... | @@ -82,11 +82,11 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> { |
82 | 82 | buildForm(entity: DeviceInfo): FormGroup { |
83 | 83 | const form = this.fb.group( |
84 | 84 | { |
85 | - name: [entity ? entity.name : '', [Validators.required]], | |
85 | + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], | |
86 | 86 | deviceProfileId: [entity ? entity.deviceProfileId : null, [Validators.required]], |
87 | 87 | firmwareId: [entity ? entity.firmwareId : null], |
88 | 88 | softwareId: [entity ? entity.softwareId : null], |
89 | - label: [entity ? entity.label : ''], | |
89 | + label: [entity ? entity.label : '', [Validators.maxLength(255)]], | |
90 | 90 | deviceData: [entity ? entity.deviceData : null, [Validators.required]], |
91 | 91 | additionalInfo: this.fb.group( |
92 | 92 | { | ... | ... |
... | ... | @@ -126,6 +126,9 @@ |
126 | 126 | <mat-error *ngIf="entityForm.get('name').hasError('required')"> |
127 | 127 | {{ 'edge.name-required' | translate }} |
128 | 128 | </mat-error> |
129 | + <mat-error *ngIf="entityForm.get('name').hasError('maxlength')"> | |
130 | + {{ 'edge.name-max-length' | translate }} | |
131 | + </mat-error> | |
129 | 132 | </mat-form-field> |
130 | 133 | <tb-entity-subtype-autocomplete |
131 | 134 | formControlName="type" |
... | ... | @@ -140,6 +143,9 @@ |
140 | 143 | <mat-error *ngIf="entityForm.get('edgeLicenseKey').hasError('required')"> |
141 | 144 | {{ 'edge.edge-license-key-required' | translate }} |
142 | 145 | </mat-error> |
146 | + <mat-error *ngIf="entityForm.get('type').hasError('maxlength')"> | |
147 | + {{ 'edge.type-max-length' | translate }} | |
148 | + </mat-error> | |
143 | 149 | </mat-form-field> |
144 | 150 | </div> |
145 | 151 | <div [fxShow]="edgeScope !== 'customer_user'"> |
... | ... | @@ -179,6 +185,9 @@ |
179 | 185 | <mat-form-field class="mat-block"> |
180 | 186 | <mat-label translate>edge.label</mat-label> |
181 | 187 | <input matInput formControlName="label"> |
188 | + <mat-error *ngIf="entityForm.get('label').hasError('maxlength')"> | |
189 | + {{ 'edge.label-max-length' | translate }} | |
190 | + </mat-error> | |
182 | 191 | </mat-form-field> |
183 | 192 | <div formGroupName="additionalInfo" fxLayout="column"> |
184 | 193 | <mat-form-field class="mat-block"> | ... | ... |
... | ... | @@ -70,9 +70,9 @@ export class EdgeComponent extends EntityComponent<EdgeInfo> { |
70 | 70 | buildForm(entity: EdgeInfo): FormGroup { |
71 | 71 | const form = this.fb.group( |
72 | 72 | { |
73 | - name: [entity ? entity.name : '', [Validators.required]], | |
74 | - type: [entity?.type ? entity.type : 'default', [Validators.required]], | |
75 | - label: [entity ? entity.label : ''], | |
73 | + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], | |
74 | + type: [entity?.type ? entity.type : 'default', [Validators.required, Validators.maxLength(255)]], | |
75 | + label: [entity ? entity.label : '', Validators.maxLength(255)], | |
76 | 76 | cloudEndpoint: [null, [Validators.required]], |
77 | 77 | edgeLicenseKey: ['', [Validators.required]], |
78 | 78 | routingKey: this.fb.control({value: entity ? entity.routingKey : null, disabled: true}), | ... | ... |
... | ... | @@ -76,6 +76,9 @@ |
76 | 76 | <mat-error *ngIf="entityForm.get('name').hasError('required')"> |
77 | 77 | {{ 'entity-view.name-required' | translate }} |
78 | 78 | </mat-error> |
79 | + <mat-error *ngIf="entityForm.get('name').hasError('maxlength')"> | |
80 | + {{ 'entity-view.name-max-length' | translate }} | |
81 | + </mat-error> | |
79 | 82 | </mat-form-field> |
80 | 83 | <tb-entity-subtype-autocomplete |
81 | 84 | formControlName="type" | ... | ... |
... | ... | @@ -81,8 +81,8 @@ export class EntityViewComponent extends EntityComponent<EntityViewInfo> { |
81 | 81 | buildForm(entity: EntityViewInfo): FormGroup { |
82 | 82 | return this.fb.group( |
83 | 83 | { |
84 | - name: [entity ? entity.name : '', [Validators.required]], | |
85 | - type: [entity ? entity.type : null, [Validators.required]], | |
84 | + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], | |
85 | + type: [entity ? entity.type : null, Validators.required], | |
86 | 86 | entityId: [entity ? entity.entityId : null, [Validators.required]], |
87 | 87 | startTimeMs: [entity ? entity.startTimeMs : null], |
88 | 88 | endTimeMs: [entity ? entity.endTimeMs : null], | ... | ... |
... | ... | @@ -65,6 +65,9 @@ |
65 | 65 | <mat-error *ngIf="entityForm.get('title').hasError('required')"> |
66 | 66 | {{ 'ota-update.title-required' | translate }} |
67 | 67 | </mat-error> |
68 | + <mat-error *ngIf="entityForm.get('title').hasError('maxlength')"> | |
69 | + {{ 'ota-update.title-max-length' | translate }} | |
70 | + </mat-error> | |
68 | 71 | </mat-form-field> |
69 | 72 | <mat-form-field class="mat-block" fxFlex> |
70 | 73 | <mat-label translate>ota-update.version</mat-label> |
... | ... | @@ -72,6 +75,9 @@ |
72 | 75 | <mat-error *ngIf="entityForm.get('version').hasError('required')"> |
73 | 76 | {{ 'ota-update.version-required' | translate }} |
74 | 77 | </mat-error> |
78 | + <mat-error *ngIf="entityForm.get('version').hasError('maxlength')"> | |
79 | + {{ 'ota-update.version-max-length' | translate }} | |
80 | + </mat-error> | |
75 | 81 | </mat-form-field> |
76 | 82 | </div> |
77 | 83 | <mat-form-field class="mat-block" fxFlex style="margin-bottom: 8px"> |
... | ... | @@ -116,7 +122,8 @@ |
116 | 122 | </mat-checkbox> |
117 | 123 | </section> |
118 | 124 | <div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayoutGap.sm="8px" |
119 | - fxLayout.xs="column" fxLayout.md="column" *ngIf="!(isAdd && this.entityForm.get('generateChecksum').value)"> | |
125 | + fxLayout.xs="column" fxLayout.md="column" | |
126 | + *ngIf="!(isAdd && this.entityForm.get('generateChecksum').value)"> | |
120 | 127 | <mat-form-field class="mat-block" fxFlex="33"> |
121 | 128 | <mat-label translate>ota-update.checksum-algorithm</mat-label> |
122 | 129 | <mat-select formControlName="checksumAlgorithm"> |
... | ... | @@ -154,7 +161,8 @@ |
154 | 161 | <input matInput formControlName="url" |
155 | 162 | type="text" |
156 | 163 | [required]="entityForm.get('isURL').value"> |
157 | - <mat-error *ngIf="entityForm.get('url').hasError('required') || entityForm.get('url').hasError('pattern')" translate> | |
164 | + <mat-error *ngIf="entityForm.get('url').hasError('required') || entityForm.get('url').hasError('pattern')" | |
165 | + translate> | |
158 | 166 | ota-update.direct-url-required |
159 | 167 | </mat-error> |
160 | 168 | </mat-form-field> | ... | ... |
... | ... | @@ -84,6 +84,9 @@ |
84 | 84 | <mat-error *ngIf="entityForm.get('name').hasError('required')"> |
85 | 85 | {{ 'rulechain.name-required' | translate }} |
86 | 86 | </mat-error> |
87 | + <mat-error *ngIf="entityForm.get('name').hasError('maxlength')"> | |
88 | + {{ 'rulechain.name-max-length' | translate }} | |
89 | + </mat-error> | |
87 | 90 | </mat-form-field> |
88 | 91 | <mat-checkbox fxFlex formControlName="debugMode" style="padding-bottom: 16px;"> |
89 | 92 | {{ 'rulechain.debug-mode' | translate }} | ... | ... |
... | ... | @@ -58,7 +58,7 @@ export class RuleChainComponent extends EntityComponent<RuleChain> { |
58 | 58 | buildForm(entity: RuleChain): FormGroup { |
59 | 59 | return this.fb.group( |
60 | 60 | { |
61 | - name: [entity ? entity.name : '', [Validators.required]], | |
61 | + name: [entity ? entity.name : '', [Validators.required, Validators.maxLength(255)]], | |
62 | 62 | debugMode: [entity ? entity.debugMode : false], |
63 | 63 | additionalInfo: this.fb.group( |
64 | 64 | { | ... | ... |
... | ... | @@ -48,6 +48,9 @@ |
48 | 48 | <mat-error *ngIf="entityForm.get('title').hasError('required')"> |
49 | 49 | {{ 'tenant.title-required' | translate }} |
50 | 50 | </mat-error> |
51 | + <mat-error *ngIf="entityForm.get('title').hasError('maxlength')"> | |
52 | + {{ 'tenant.title-max-length' | translate }} | |
53 | + </mat-error> | |
51 | 54 | </mat-form-field> |
52 | 55 | <tb-tenant-profile-autocomplete |
53 | 56 | [selectDefaultProfile]="isAdd" | ... | ... |
... | ... | @@ -52,7 +52,7 @@ export class TenantComponent extends ContactBasedComponent<TenantInfo> { |
52 | 52 | buildEntityForm(entity: TenantInfo): FormGroup { |
53 | 53 | return this.fb.group( |
54 | 54 | { |
55 | - title: [entity ? entity.title : '', [Validators.required]], | |
55 | + title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], | |
56 | 56 | tenantProfileId: [entity ? entity.tenantProfileId : null, [Validators.required]], |
57 | 57 | additionalInfo: this.fb.group( |
58 | 58 | { | ... | ... |
... | ... | @@ -44,6 +44,9 @@ |
44 | 44 | <mat-error *ngIf="entityForm.get('title').hasError('required')"> |
45 | 45 | {{ 'widgets-bundle.title-required' | translate }} |
46 | 46 | </mat-error> |
47 | + <mat-error *ngIf="entityForm.get('title').hasError('maxlength')"> | |
48 | + {{ 'widgets-bundle.title-max-length' | translate }} | |
49 | + </mat-error> | |
47 | 50 | </mat-form-field> |
48 | 51 | <tb-image-input fxFlex |
49 | 52 | label="{{'widgets-bundle.image-preview' | translate}}" | ... | ... |
... | ... | @@ -48,7 +48,7 @@ export class WidgetsBundleComponent extends EntityComponent<WidgetsBundle> { |
48 | 48 | buildForm(entity: WidgetsBundle): FormGroup { |
49 | 49 | return this.fb.group( |
50 | 50 | { |
51 | - title: [entity ? entity.title : '', [Validators.required]], | |
51 | + title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], | |
52 | 52 | image: [entity ? entity.image : ''], |
53 | 53 | description: [entity ? entity.description : '', Validators.maxLength(255)] |
54 | 54 | } | ... | ... |
... | ... | @@ -28,10 +28,16 @@ |
28 | 28 | <mat-form-field class="mat-block"> |
29 | 29 | <mat-label translate>contact.city</mat-label> |
30 | 30 | <input matInput formControlName="city"> |
31 | + <mat-error *ngIf="parentForm.get('city').hasError('maxlength')"> | |
32 | + {{ 'contact.city-max-length' | translate }} | |
33 | + </mat-error> | |
31 | 34 | </mat-form-field> |
32 | 35 | <mat-form-field class="mat-block"> |
33 | 36 | <mat-label translate>contact.state</mat-label> |
34 | 37 | <input matInput formControlName="state"> |
38 | + <mat-error *ngIf="parentForm.get('state').hasError('maxlength')"> | |
39 | + {{ 'contact.state-max-length' | translate }} | |
40 | + </mat-error> | |
35 | 41 | </mat-form-field> |
36 | 42 | <mat-form-field class="mat-block"> |
37 | 43 | <mat-label translate>contact.postal-code</mat-label> |
... | ... | @@ -52,6 +58,9 @@ |
52 | 58 | <mat-form-field class="mat-block"> |
53 | 59 | <mat-label translate>contact.phone</mat-label> |
54 | 60 | <input matInput formControlName="phone"> |
61 | + <mat-error *ngIf="parentForm.get('phone').hasError('maxlength')"> | |
62 | + {{ 'contact.phone-max-length' | translate }} | |
63 | + </mat-error> | |
55 | 64 | </mat-form-field> |
56 | 65 | <mat-form-field class="mat-block"> |
57 | 66 | <mat-label translate>contact.email</mat-label> | ... | ... |
... | ... | @@ -40,4 +40,7 @@ |
40 | 40 | <mat-error *ngIf="subTypeFormGroup.get('subType').hasError('required')"> |
41 | 41 | {{ entitySubtypeRequiredText | translate }} |
42 | 42 | </mat-error> |
43 | + <mat-error *ngIf="subTypeFormGroup.get('subType').hasError('maxlength')"> | |
44 | + {{ entitySubtypeMaxLength | translate }} | |
45 | + </mat-error> | |
43 | 46 | </mat-form-field> | ... | ... |
... | ... | @@ -15,7 +15,7 @@ |
15 | 15 | /// |
16 | 16 | |
17 | 17 | import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; |
18 | -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; | |
18 | +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; | |
19 | 19 | import { Observable, of, Subscription, throwError } from 'rxjs'; |
20 | 20 | import { |
21 | 21 | catchError, |
... | ... | @@ -58,9 +58,11 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, |
58 | 58 | entityType: EntityType; |
59 | 59 | |
60 | 60 | private requiredValue: boolean; |
61 | + | |
61 | 62 | get required(): boolean { |
62 | 63 | return this.requiredValue; |
63 | 64 | } |
65 | + | |
64 | 66 | @Input() |
65 | 67 | set required(value: boolean) { |
66 | 68 | this.requiredValue = coerceBooleanProperty(value); |
... | ... | @@ -74,6 +76,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, |
74 | 76 | selectEntitySubtypeText: string; |
75 | 77 | entitySubtypeText: string; |
76 | 78 | entitySubtypeRequiredText: string; |
79 | + entitySubtypeMaxLength: string; | |
77 | 80 | |
78 | 81 | filteredSubTypes: Observable<Array<string>>; |
79 | 82 | |
... | ... | @@ -96,7 +99,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, |
96 | 99 | private entityViewService: EntityViewService, |
97 | 100 | private fb: FormBuilder) { |
98 | 101 | this.subTypeFormGroup = this.fb.group({ |
99 | - subType: [null] | |
102 | + subType: [null, Validators.maxLength(255)] | |
100 | 103 | }); |
101 | 104 | } |
102 | 105 | |
... | ... | @@ -114,6 +117,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, |
114 | 117 | this.selectEntitySubtypeText = 'asset.select-asset-type'; |
115 | 118 | this.entitySubtypeText = 'asset.asset-type'; |
116 | 119 | this.entitySubtypeRequiredText = 'asset.asset-type-required'; |
120 | + this.entitySubtypeMaxLength = 'asset.asset-type-max-length'; | |
117 | 121 | this.broadcastSubscription = this.broadcast.on('assetSaved', () => { |
118 | 122 | this.subTypes = null; |
119 | 123 | }); |
... | ... | @@ -122,6 +126,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, |
122 | 126 | this.selectEntitySubtypeText = 'device.select-device-type'; |
123 | 127 | this.entitySubtypeText = 'device.device-type'; |
124 | 128 | this.entitySubtypeRequiredText = 'device.device-type-required'; |
129 | + this.entitySubtypeMaxLength = 'device.device-type-max-length'; | |
125 | 130 | this.broadcastSubscription = this.broadcast.on('deviceSaved', () => { |
126 | 131 | this.subTypes = null; |
127 | 132 | }); |
... | ... | @@ -130,6 +135,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, |
130 | 135 | this.selectEntitySubtypeText = 'edge.select-edge-type'; |
131 | 136 | this.entitySubtypeText = 'edge.edge-type'; |
132 | 137 | this.entitySubtypeRequiredText = 'edge.edge-type-required'; |
138 | + this.entitySubtypeMaxLength = 'edge.type-max-length'; | |
133 | 139 | this.broadcastSubscription = this.broadcast.on('edgeSaved', () => { |
134 | 140 | this.subTypes = null; |
135 | 141 | }); |
... | ... | @@ -138,6 +144,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, |
138 | 144 | this.selectEntitySubtypeText = 'entity-view.select-entity-view-type'; |
139 | 145 | this.entitySubtypeText = 'entity-view.entity-view-type'; |
140 | 146 | this.entitySubtypeRequiredText = 'entity-view.entity-view-type-required'; |
147 | + this.entitySubtypeMaxLength = 'entity-view.type-max-length' | |
141 | 148 | this.broadcastSubscription = this.broadcast.on('entityViewSaved', () => { |
142 | 149 | this.subTypes = null; |
143 | 150 | }); |
... | ... | @@ -149,7 +156,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, |
149 | 156 | debounceTime(150), |
150 | 157 | distinctUntilChanged(), |
151 | 158 | tap(value => { |
152 | - this.updateView(value); | |
159 | + this.updateView(value); | |
153 | 160 | }), |
154 | 161 | // startWith<string | EntitySubtype>(''), |
155 | 162 | map(value => value ? value : ''), |
... | ... | @@ -203,7 +210,7 @@ export class EntitySubTypeAutocompleteComponent implements ControlValueAccessor, |
203 | 210 | fetchSubTypes(searchText?: string, strictMatch: boolean = false): Observable<Array<string>> { |
204 | 211 | this.searchText = searchText; |
205 | 212 | return this.getSubTypes().pipe( |
206 | - map(subTypes => subTypes.filter( subType => { | |
213 | + map(subTypes => subTypes.filter(subType => { | |
207 | 214 | if (strictMatch) { |
208 | 215 | return searchText ? subType === searchText : false; |
209 | 216 | } else { | ... | ... |
... | ... | @@ -39,4 +39,7 @@ |
39 | 39 | <mat-error *ngIf="relationTypeFormGroup.get('relationType').hasError('required')"> |
40 | 40 | {{ 'relation.relation-type-required' | translate }} |
41 | 41 | </mat-error> |
42 | + <mat-error *ngIf="relationTypeFormGroup.get('relationType').hasError('maxlength')"> | |
43 | + {{ 'relation.relation-type-max-length' | translate }} | |
44 | + </mat-error> | |
42 | 45 | </mat-form-field> | ... | ... |
... | ... | @@ -68,7 +68,7 @@ export class RelationTypeAutocompleteComponent implements ControlValueAccessor, |
68 | 68 | public translate: TranslateService, |
69 | 69 | private fb: FormBuilder) { |
70 | 70 | this.relationTypeFormGroup = this.fb.group({ |
71 | - relationType: [null, this.required ? [Validators.required] : []] | |
71 | + relationType: [null, this.required ? [Validators.required, Validators.maxLength(255)] : [Validators.maxLength(255)]] | |
72 | 72 | }); |
73 | 73 | } |
74 | 74 | ... | ... |
... | ... | @@ -370,6 +370,7 @@ |
370 | 370 | "management": "Asset management", |
371 | 371 | "view-assets": "View Assets", |
372 | 372 | "add": "Add Asset", |
373 | + "asset-type-max-length": "Asset type should be less than 256", | |
373 | 374 | "assign-to-customer": "Assign to customer", |
374 | 375 | "assign-asset-to-customer": "Assign Asset(s) To Customer", |
375 | 376 | "assign-asset-to-customer-text": "Please select the assets to assign to the customer", |
... | ... | @@ -392,6 +393,8 @@ |
392 | 393 | "asset-types": "Asset types", |
393 | 394 | "name": "Name", |
394 | 395 | "name-required": "Name is required.", |
396 | + "name-max-length": "Name should be less than 256", | |
397 | + "label-max-length": "Label should be less than 256", | |
395 | 398 | "description": "Description", |
396 | 399 | "type": "Type", |
397 | 400 | "type-required": "Type is required.", |
... | ... | @@ -451,6 +454,7 @@ |
451 | 454 | "scope-shared": "Shared attributes", |
452 | 455 | "add": "Add attribute", |
453 | 456 | "key": "Key", |
457 | + "key-max-length": "Key should be less than 256", | |
454 | 458 | "last-update-time": "Last update time", |
455 | 459 | "key-required": "Attribute key is required.", |
456 | 460 | "value": "Value", |
... | ... | @@ -592,7 +596,10 @@ |
592 | 596 | "address2": "Address 2", |
593 | 597 | "phone": "Phone", |
594 | 598 | "email": "Email", |
595 | - "no-address": "No address" | |
599 | + "no-address": "No address", | |
600 | + "state-max-length": "State length should be less than 256", | |
601 | + "phone-max-length": "Phone number should be less than 256", | |
602 | + "city-max-length": "Specified city should be less than 256" | |
596 | 603 | }, |
597 | 604 | "common": { |
598 | 605 | "username": "Username", |
... | ... | @@ -648,6 +655,7 @@ |
648 | 655 | "manage-dashboards": "Manage dashboards", |
649 | 656 | "title": "Title", |
650 | 657 | "title-required": "Title is required.", |
658 | + "title-max-length": "Title should be less than 256", | |
651 | 659 | "description": "Description", |
652 | 660 | "details": "Details", |
653 | 661 | "events": "Events", |
... | ... | @@ -705,6 +713,7 @@ |
705 | 713 | "select-widget-subtitle": "List of available widget types", |
706 | 714 | "delete": "Delete dashboard", |
707 | 715 | "title-required": "Title is required.", |
716 | + "title-max-length": "Title should be less than 256", | |
708 | 717 | "description": "Description", |
709 | 718 | "details": "Details", |
710 | 719 | "dashboard-details": "Dashboard details", |
... | ... | @@ -891,6 +900,7 @@ |
891 | 900 | "management": "Device management", |
892 | 901 | "view-devices": "View Devices", |
893 | 902 | "device-alias": "Device alias", |
903 | + "device-type-max-length": "Device type should be less than 256", | |
894 | 904 | "aliases": "Device aliases", |
895 | 905 | "no-alias-matching": "'{{alias}}' not found.", |
896 | 906 | "no-aliases-found": "No aliases found.", |
... | ... | @@ -1003,6 +1013,8 @@ |
1003 | 1013 | "device-types": "Device types", |
1004 | 1014 | "name": "Name", |
1005 | 1015 | "name-required": "Name is required.", |
1016 | + "name-max-length": "Name should be less than 256", | |
1017 | + "label-max-length": "Label should be less than 256", | |
1006 | 1018 | "description": "Description", |
1007 | 1019 | "label": "Label", |
1008 | 1020 | "events": "Events", |
... | ... | @@ -1055,6 +1067,7 @@ |
1055 | 1067 | "set-default": "Make device profile default", |
1056 | 1068 | "delete": "Delete device profile", |
1057 | 1069 | "copyId": "Copy device profile Id", |
1070 | + "name-max-length": "Name should be less than 256", | |
1058 | 1071 | "new-device-profile-name": "Device profile name", |
1059 | 1072 | "new-device-profile-name-required": "Device profile name is required.", |
1060 | 1073 | "name": "Name", |
... | ... | @@ -1416,6 +1429,9 @@ |
1416 | 1429 | "edge": "Edge", |
1417 | 1430 | "edge-instances": "Edge instances", |
1418 | 1431 | "edge-file": "Edge file", |
1432 | + "name-max-length": "Name should be less than 256", | |
1433 | + "label-max-length": "Label should be less than 256", | |
1434 | + "type-max-length": "Type should be less than 256", | |
1419 | 1435 | "management": "Edge management", |
1420 | 1436 | "no-edges-matching": "No edges matching '{{entity}}' were found.", |
1421 | 1437 | "add": "Add Edge", |
... | ... | @@ -1750,6 +1766,8 @@ |
1750 | 1766 | "created-time": "Created time", |
1751 | 1767 | "name": "Name", |
1752 | 1768 | "name-required": "Name is required.", |
1769 | + "name-max-length": "Name should be less than 256", | |
1770 | + "type-max-length": "Entity view type should be less than 256", | |
1753 | 1771 | "description": "Description", |
1754 | 1772 | "events": "Events", |
1755 | 1773 | "details": "Details", |
... | ... | @@ -2374,6 +2392,7 @@ |
2374 | 2392 | "selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected", |
2375 | 2393 | "title": "Title", |
2376 | 2394 | "title-required": "Title is required.", |
2395 | + "title-max-length": "Title should be less than 256", | |
2377 | 2396 | "types": { |
2378 | 2397 | "firmware": "Firmware", |
2379 | 2398 | "software": "Software" |
... | ... | @@ -2384,6 +2403,7 @@ |
2384 | 2403 | "version-required": "Version is required.", |
2385 | 2404 | "version-tag": "Version Tag", |
2386 | 2405 | "version-tag-hint": "Custom tag should match the package version reported by your device.", |
2406 | + "version-max-length": "Version should be less than 256", | |
2387 | 2407 | "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." |
2388 | 2408 | }, |
2389 | 2409 | "position": { |
... | ... | @@ -2426,6 +2446,7 @@ |
2426 | 2446 | "delete": "Delete relation", |
2427 | 2447 | "relation-type": "Relation type", |
2428 | 2448 | "relation-type-required": "Relation type is required.", |
2449 | + "relation-type-max-length": "Relation type should be less than 256", | |
2429 | 2450 | "any-relation-type": "Any type", |
2430 | 2451 | "add": "Add relation", |
2431 | 2452 | "edit": "Edit relation", |
... | ... | @@ -2470,7 +2491,8 @@ |
2470 | 2491 | "selected-resources": "{ count, plural, 1 {1 resource} other {# resources} } selected", |
2471 | 2492 | "system": "System", |
2472 | 2493 | "title": "Title", |
2473 | - "title-required": "Title is required." | |
2494 | + "title-required": "Title is required.", | |
2495 | + "title-max-length": "Title should be less than 256" | |
2474 | 2496 | }, |
2475 | 2497 | "rulechain": { |
2476 | 2498 | "rulechain": "Rule chain", |
... | ... | @@ -2479,6 +2501,7 @@ |
2479 | 2501 | "delete": "Delete rule chain", |
2480 | 2502 | "name": "Name", |
2481 | 2503 | "name-required": "Name is required.", |
2504 | + "name-max-length": "Name should be less than 256", | |
2482 | 2505 | "description": "Description", |
2483 | 2506 | "add": "Add Rule Chain", |
2484 | 2507 | "set-root": "Make rule chain root", |
... | ... | @@ -2621,6 +2644,7 @@ |
2621 | 2644 | "add-tenant-text": "Add new tenant", |
2622 | 2645 | "no-tenants-text": "No tenants found", |
2623 | 2646 | "tenant-details": "Tenant details", |
2647 | + "title-max-length": "Title should be less than 256", | |
2624 | 2648 | "delete-tenant-title": "Are you sure you want to delete the tenant '{{tenantTitle}}'?", |
2625 | 2649 | "delete-tenant-text": "Be careful, after the confirmation the tenant and all related data will become unrecoverable.", |
2626 | 2650 | "delete-tenants-title": "Are you sure you want to delete { count, plural, 1 {1 tenant} other {# tenants} }?", |
... | ... | @@ -2650,6 +2674,7 @@ |
2650 | 2674 | "edit": "Edit tenant profile", |
2651 | 2675 | "tenant-profile-details": "Tenant profile details", |
2652 | 2676 | "no-tenant-profiles-text": "No tenant profiles found", |
2677 | + "name-max-length": "Name should be less than 256", | |
2653 | 2678 | "search": "Search tenant profiles", |
2654 | 2679 | "selected-tenant-profiles": "{ count, plural, 1 {1 tenant profile} other {# tenant profiles} } selected", |
2655 | 2680 | "no-tenant-profiles-matching": "No tenant profile matching '{{entity}}' were found.", |
... | ... | @@ -2997,6 +3022,7 @@ |
2997 | 3022 | "delete": "Delete widgets bundle", |
2998 | 3023 | "title": "Title", |
2999 | 3024 | "title-required": "Title is required.", |
3025 | + "title-max-length": "Title should be less than 256", | |
3000 | 3026 | "description": "Description", |
3001 | 3027 | "image-preview": "Image preview", |
3002 | 3028 | "add-widgets-bundle-text": "Add new widgets bundle", | ... | ... |