From 7bc43cb7bfa7134cc60a5d9051810ff45993b2ea Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 8 Dec 2023 12:33:43 +0200 Subject: [PATCH 1/6] Inline images when creating version --- .../service/sync/ie/DefaultEntitiesExportImportService.java | 4 ++-- .../sync/ie/exporting/impl/AssetProfileExportService.java | 1 + .../sync/ie/exporting/impl/DashboardExportService.java | 1 + .../sync/ie/exporting/impl/DefaultEntityExportService.java | 3 +++ .../sync/ie/exporting/impl/DeviceProfileExportService.java | 1 + .../sync/ie/exporting/impl/WidgetTypeExportService.java | 5 +++-- .../sync/ie/exporting/impl/WidgetsBundleExportService.java | 1 + 7 files changed, 12 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java index 1cf0f0fc4d..bc1735f30b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java @@ -65,8 +65,8 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS private final TbNotificationEntityService entityNotificationService; protected static final List SUPPORTED_ENTITY_TYPES = List.of( - EntityType.CUSTOMER, EntityType.RULE_CHAIN, EntityType.DASHBOARD, - EntityType.ASSET_PROFILE, EntityType.ASSET, + EntityType.CUSTOMER, EntityType.RULE_CHAIN, EntityType.TB_RESOURCE, + EntityType.DASHBOARD, EntityType.ASSET_PROFILE, EntityType.ASSET, EntityType.DEVICE_PROFILE, EntityType.DEVICE, EntityType.ENTITY_VIEW, EntityType.WIDGET_TYPE, EntityType.WIDGETS_BUNDLE, EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_RULE diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java index 710e33f106..d8a3e76b7f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/AssetProfileExportService.java @@ -34,6 +34,7 @@ public class AssetProfileExportService extends BaseEntityExportService ctx, I entityId) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java index b95d8b83e2..8c6269831d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DeviceProfileExportService.java @@ -34,6 +34,7 @@ public class DeviceProfileExportService extends BaseEntityExportService { @Override - protected void setRelatedEntities(EntitiesExportCtx ctx, WidgetTypeDetails widgetsBundle, WidgetTypeExportData exportData) { - if (widgetsBundle.getTenantId() == null || widgetsBundle.getTenantId().isNullUid()) { + protected void setRelatedEntities(EntitiesExportCtx ctx, WidgetTypeDetails widgetTypeDetails, WidgetTypeExportData exportData) { + if (widgetTypeDetails.getTenantId() == null || widgetTypeDetails.getTenantId().isNullUid()) { throw new IllegalArgumentException("Export of system Widget Type is not allowed"); } + imageService.inlineImages(widgetTypeDetails); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java index f984bb9d79..46078ac293 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/WidgetsBundleExportService.java @@ -41,6 +41,7 @@ public class WidgetsBundleExportService extends BaseEntityExportService fqns = widgetTypeService.findWidgetFqnsByWidgetsBundleId(ctx.getTenantId(), widgetsBundle.getId()); exportData.setFqns(fqns); From b9ac141bffae2c71c1287cb08bc90ed379c6e006 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 8 Dec 2023 12:34:24 +0200 Subject: [PATCH 2/6] When replacing base64 with links, keep the transaction with no rollback --- .../server/dao/resource/BaseImageService.java | 9 +++++---- .../dao/sql/resource/TbResourceInfoRepository.java | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java index ad3934f76e..29f9ba8ae0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java @@ -27,7 +27,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.tuple.Pair; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Base64Utils; import org.thingsboard.common.util.JacksonUtil; @@ -273,7 +272,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic return resourceInfoDao.findSystemOrTenantImageByEtag(tenantId, ResourceType.IMAGE, etag); } - @Transactional(propagation = Propagation.NOT_SUPPORTED)// we don't want transaction to rollback in case of an image processing failure + @Transactional(noRollbackFor = Exception.class) // we don't want transaction to rollback in case of an image processing failure @Override public boolean replaceBase64WithImageUrl(HasImage entity, String type) { log.trace("Executing replaceBase64WithImageUrl [{}] [{}] [{}]", entity.getTenantId(), type, entity.getName()); @@ -288,7 +287,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic return result.isUpdated(); } - @Transactional(propagation = Propagation.NOT_SUPPORTED)// we don't want transaction to rollback in case of an image processing failure + @Transactional(noRollbackFor = Exception.class) // we don't want transaction to rollback in case of an image processing failure @Override public boolean replaceBase64WithImageUrl(WidgetTypeDetails entity) { log.trace("Executing replaceBase64WithImageUrl [{}] [WidgetTypeDetails] [{}]", entity.getTenantId(), entity.getId()); @@ -314,7 +313,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic return updated; } - @Transactional(propagation = Propagation.NOT_SUPPORTED)// we don't want transaction to rollback in case of an image processing failure + @Transactional(noRollbackFor = Exception.class) // we don't want transaction to rollback in case of an image processing failure @Override public boolean replaceBase64WithImageUrl(Dashboard entity) { log.trace("Executing replaceBase64WithImageUrl [{}] [Dashboard] [{}]", entity.getTenantId(), entity.getId()); @@ -462,6 +461,8 @@ public class BaseImageService extends BaseResourceService implements ImageServic } return UpdateResult.of(false, data); } + } else { + log.debug("[{}] Using existing image {} ({} - '{}') for '{}'", tenantId, imageInfo.getResourceKey(), imageInfo.getTenantId(), imageInfo.getName(), name); } return UpdateResult.of(true, DataConstants.TB_IMAGE_PREFIX + imageInfo.getLink()); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java index ced94e1ea8..62d9ec5155 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java @@ -66,7 +66,7 @@ public interface TbResourceInfoRepository extends JpaRepository findByTenantIdAndEtagAndResourceKeyStartingWith(UUID tenantId, String etag, String query); @Query(value = "SELECT * FROM resource r WHERE (r.tenant_id = '13814000-1dd2-11b2-8080-808080808080' OR r.tenant_id = :tenantId) " + - "AND r.resource_type = :resourceType AND r.etag = :etag LIMIT 1", nativeQuery = true) + "AND r.resource_type = :resourceType AND r.etag = :etag ORDER BY created_time, id LIMIT 1", nativeQuery = true) TbResourceInfoEntity findSystemOrTenantImageByEtag(@Param("tenantId") UUID tenantId, @Param("resourceType") String resourceType, @Param("etag") String etag); From c6559d6bbac0b0e95d19c562e987cfbe9dce69b8 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 8 Dec 2023 13:57:49 +0200 Subject: [PATCH 3/6] Always load system images; update images only after loading system widgets --- .../server/install/ThingsboardInstallService.java | 7 +++++-- .../thingsboard/server/service/install/InstallScripts.java | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 3fc68b9cc3..5ab3b7b28e 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -271,9 +271,8 @@ public class ThingsboardInstallService { case "3.6.1": log.info("Upgrading ThingsBoard from version 3.6.1 to 3.6.2 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.6.1"); - installScripts.loadSystemImages(); if (!getEnv("SKIP_IMAGES_MIGRATION", false)) { - installScripts.updateImages(); + installScripts.setUpdateImages(true); } else { log.info("Skipping images migration. Run the upgrade with fromVersion as '3.6.2-images' to migrate"); } @@ -288,6 +287,10 @@ public class ThingsboardInstallService { dataUpdateService.upgradeRuleNodes(); systemDataLoaderService.loadSystemWidgets(); installScripts.loadSystemLwm2mResources(); + installScripts.loadSystemImages(); + if (installScripts.isUpdateImages()) { + installScripts.updateImages(); + } } log.info("Upgrade finished successfully!"); diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 598c3c8965..9373ac6d34 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -16,6 +16,8 @@ package org.thingsboard.server.service.install; import com.fasterxml.jackson.databind.JsonNode; +import lombok.Getter; +import lombok.Setter; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -113,6 +115,8 @@ public class InstallScripts { @Autowired private ImagesUpdater imagesUpdater; + @Getter @Setter + private boolean updateImages = false; Path getTenantRuleChainsDir() { return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); From 0ae62e7a579a164a8b459009ae55a440f64495b9 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 11 Dec 2023 17:42:04 +0200 Subject: [PATCH 4/6] Remove @Length validation for image fields --- .../java/org/thingsboard/server/common/data/DashboardInfo.java | 1 - .../java/org/thingsboard/server/common/data/DeviceProfile.java | 1 - .../org/thingsboard/server/common/data/asset/AssetProfile.java | 1 - .../server/common/data/widget/WidgetTypeDetails.java | 1 - .../thingsboard/server/common/data/widget/WidgetsBundle.java | 2 -- 5 files changed, 6 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java index 19b69f4f27..c3a06a0e6a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java @@ -38,7 +38,6 @@ public class DashboardInfo extends BaseData implements HasName, Has @NoXss @Length(fieldName = "title") private String title; - @Length(fieldName = "image", max = 1000000) private String image; @Valid private Set assignedCustomers; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index abee7284c8..be4426a1d4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -54,7 +54,6 @@ public class DeviceProfile extends BaseData implements HasName, @NoXss @ApiModelProperty(position = 11, value = "Device Profile description. ") private String description; - @Length(fieldName = "image", max = 1000000) @ApiModelProperty(position = 12, value = "Either URL or Base64 data of the icon. Used in the mobile application to visualize set of device profiles in the grid view. ") private String image; private boolean isDefault; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java index 96b24ad2f2..2daa64061d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/AssetProfile.java @@ -52,7 +52,6 @@ public class AssetProfile extends BaseData implements HasName, H @NoXss @ApiModelProperty(position = 11, value = "Asset Profile description. ") private String description; - @Length(fieldName = "image", max = 1000000) @ApiModelProperty(position = 12, value = "Either URL or Base64 data of the icon. Used in the mobile application to visualize set of asset profiles in the grid view. ") private String image; private boolean isDefault; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java index b335038b54..02992421eb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java @@ -32,7 +32,6 @@ import org.thingsboard.server.common.data.validation.NoXss; @JsonPropertyOrder({ "fqn", "name", "deprecated", "image", "description", "descriptor", "externalId" }) public class WidgetTypeDetails extends WidgetType implements HasName, HasTenantId, HasImage, ExportableEntity { - @Length(fieldName = "image", max = 1000000) @ApiModelProperty(position = 9, value = "Relative or external image URL. Replaced with image data URL (Base64) in case of relative URL and 'inlineImages' option enabled.") private String image; @NoXss diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java index ce0367f57d..5d38c10550 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetsBundle.java @@ -57,7 +57,6 @@ public class WidgetsBundle extends BaseData implements HasName, @ApiModelProperty(position = 5, value = "Title used in search and UI", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String title; - @Length(fieldName = "image", max = 1000000) @Getter @Setter @ApiModelProperty(position = 6, value = "Relative or external image URL. Replaced with image data URL (Base64) in case of relative URL and 'inlineImages' option enabled.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @@ -66,7 +65,6 @@ public class WidgetsBundle extends BaseData implements HasName, @NoXss @Length(fieldName = "description", max = 1024) @Getter - @Setter @ApiModelProperty(position = 7, value = "Description", accessMode = ApiModelProperty.AccessMode.READ_ONLY) private String description; From 27743c3e3ae524bc84e8ffab25ce0148f54d5039 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 11 Dec 2023 17:51:06 +0200 Subject: [PATCH 5/6] Evict image from cache when deleted --- .../server/service/resource/DefaultTbImageService.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java index cd69f884ac..1fbd62f88f 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java @@ -145,6 +145,15 @@ public class DefaultTbImageService extends AbstractTbEntityService implements Tb TbImageDeleteResult result = imageService.deleteImage(imageInfo, force); if (result.isSuccess()) { notificationEntityService.logEntityAction(tenantId, imageId, imageInfo, ActionType.DELETED, user, imageId.toString()); + evictETag(new ImageCacheKey(tenantId, imageInfo.getResourceKey(), false)); + evictETag(new ImageCacheKey(tenantId, imageInfo.getResourceKey(), true)); + clusterService.broadcastToCore(TransportProtos.ToCoreNotificationMsg.newBuilder() + .setResourceCacheInvalidateMsg(TransportProtos.ResourceCacheInvalidateMsg.newBuilder() + .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) + .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) + .setResourceKey(imageInfo.getResourceKey()) + .build()) + .build()); } return result; } catch (Exception e) { From 2cfa040cc5213c5734c5e7c2e04a7619c99afb1e Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Tue, 12 Dec 2023 11:55:57 +0200 Subject: [PATCH 6/6] Validate image size as soon as possible; pretty max size in the error --- .../server/controller/ImageController.java | 10 ++++-- .../server/dao/service/DataValidator.java | 3 +- .../validator/ResourceDataValidator.java | 33 +++++++++++-------- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java index 7685640929..815adaca5f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java @@ -50,9 +50,10 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.dao.resource.ImageService; -import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.dao.resource.ImageCacheKey; +import org.thingsboard.server.dao.resource.ImageService; +import org.thingsboard.server.dao.service.validator.ResourceDataValidator; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.resource.TbImageService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; @@ -77,6 +78,8 @@ public class ImageController extends BaseController { private final ImageService imageService; private final TbImageService tbImageService; + private final ResourceDataValidator resourceValidator; + @Value("${cache.image.systemImagesBrowserTtlInMinutes:0}") private int systemImagesBrowserTtlInMinutes; @Value("${cache.image.tenantImagesBrowserTtlInMinutes:0}") @@ -94,6 +97,7 @@ public class ImageController extends BaseController { TbResource image = new TbResource(); image.setTenantId(user.getTenantId()); accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.CREATE, null, image); + resourceValidator.validateResourceSize(user.getTenantId(), null, file.getSize()); image.setFileName(file.getOriginalFilename()); if (StringUtils.isNotEmpty(title)) { @@ -115,6 +119,8 @@ public class ImageController extends BaseController { @PathVariable String key, @RequestPart MultipartFile file) throws Exception { TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE); + resourceValidator.validateResourceSize(getTenantId(), imageInfo.getId(), file.getSize()); + TbResource image = new TbResource(imageInfo); image.setData(file.getBytes()); image.setFileName(file.getOriginalFilename()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index 961fffcdc0..80c9376f1c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.service; import com.fasterxml.jackson.databind.JsonNode; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.BaseData; @@ -129,7 +130,7 @@ public abstract class DataValidator> { EntityType entityType) { if (maxSumDataSize > 0) { if (dataDao.sumDataSizeByTenantId(tenantId) + currentDataSize > maxSumDataSize) { - throw new DataValidationException(String.format("%ss total size exceeds the maximum of " + maxSumDataSize + " bytes", entityType.getNormalName())); + throw new DataValidationException(String.format("%ss total size exceeds the maximum of " + FileUtils.byteCountToDisplaySize(maxSumDataSize), entityType.getNormalName())); } } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java index 26d8ab75fb..352966d561 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/ResourceDataValidator.java @@ -15,12 +15,14 @@ */ package org.thingsboard.server.dao.service.validator; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.widget.BaseWidgetType; @@ -83,19 +85,8 @@ public class ResourceDataValidator extends DataValidator { if (resource.getResourceType() == null) { throw new DataValidationException("Resource type should be specified!"); } - if (!resource.getTenantId().isSysTenantId() && resource.getData() != null) { - DefaultTenantProfileConfiguration profileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); - long maxResourceSize = profileConfiguration.getMaxResourceSize(); - if (maxResourceSize > 0 && resource.getData().length > maxResourceSize) { - throw new IllegalArgumentException("Resource exceeds the maximum size of " + maxResourceSize + " bytes"); - } - long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); - int dataSize = resource.getData().length; - if (resource.getId() != null) { - long prevSize = resourceDao.getResourceSize(tenantId, resource.getId()); - dataSize -= prevSize; - } - validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, dataSize, TB_RESOURCE); + if (resource.getData() != null) { + validateResourceSize(resource.getTenantId(), resource.getId(), resource.getData().length); } if (StringUtils.isEmpty(resource.getFileName())) { throw new DataValidationException("Resource file name should be specified!"); @@ -108,6 +99,22 @@ public class ResourceDataValidator extends DataValidator { } } + public void validateResourceSize(TenantId tenantId, TbResourceId resourceId, long dataSize) { + if (!tenantId.isSysTenantId()) { + DefaultTenantProfileConfiguration profileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration(); + long maxResourceSize = profileConfiguration.getMaxResourceSize(); + if (maxResourceSize > 0 && dataSize > maxResourceSize) { + throw new IllegalArgumentException("Resource exceeds the maximum size of " + FileUtils.byteCountToDisplaySize(maxResourceSize)); + } + long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); + if (resourceId != null) { + long prevSize = resourceDao.getResourceSize(tenantId, resourceId); + dataSize -= prevSize; + } + validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, dataSize, TB_RESOURCE); + } + } + @Override public void validateDelete(TenantId tenantId, EntityId resourceId) { List widgets = widgetTypeDao.findWidgetTypesInfosByTenantIdAndResourceId(tenantId.getId(),