From f25f2e0fa8cca7a17c52e45a22505c7fa20484a6 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 1 Dec 2023 15:16:00 +0200 Subject: [PATCH 01/10] Inline systemTenantId param for findSystemOrTenantImageByEtag --- .../server/dao/sql/resource/JpaTbResourceInfoDao.java | 2 +- .../server/dao/sql/resource/TbResourceInfoRepository.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java index 038aedcf28..d3123fe38c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java @@ -107,6 +107,6 @@ public class JpaTbResourceInfoDao extends JpaAbstractDao findByTenantIdAndEtagAndResourceKeyStartingWith(UUID tenantId, String etag, String query); - @Query(value = "SELECT * FROM resource r WHERE (r.tenant_id = :systemTenantId OR r.tenant_id = :tenantId) AND r.resource_type = :resourceType AND r.etag = :etag LIMIT 1", nativeQuery = true) - TbResourceInfoEntity findSystemOrTenantImageByEtag(@Param("systemTenantId") UUID sysTenantId, - @Param("tenantId") UUID tenantId, + @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) + TbResourceInfoEntity findSystemOrTenantImageByEtag(@Param("tenantId") UUID tenantId, @Param("resourceType") String resourceType, @Param("etag") String etag); } From cca7f47abc02368a26e717bab10800b5db871457 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 1 Dec 2023 15:16:42 +0200 Subject: [PATCH 02/10] Add more logging for ImageService --- .../server/dao/resource/BaseImageService.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 2d27919e1f..b5ee8b4211 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 @@ -148,6 +148,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic image.setDescriptorValue(descriptor); image.setPreview(result.getRight()); + log.debug("[{}] Creating image {} ('{}')", image.getTenantId(), image.getResourceKey(), image.getName()); return new TbResourceInfo(doSaveResource(image)); } @@ -188,21 +189,25 @@ public class BaseImageService extends BaseResourceService implements ImageServic resourceKey = basename + "_(" + idx + ")." + extension; idx++; } + log.debug("[{}] Generated unique key {} for image {}", tenantId, resourceKey, filename); return resourceKey; } @Override public TbResourceInfo saveImageInfo(TbResourceInfo imageInfo) { + log.trace("Executing saveImageInfo [{}] [{}]", imageInfo.getTenantId(), imageInfo.getId()); return saveResource(new TbResource(imageInfo)); } @Override public TbResourceInfo getImageInfoByTenantIdAndKey(TenantId tenantId, String key) { + log.trace("Executing getImageInfoByTenantIdAndKey [{}] [{}]", tenantId, key); return findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, key); } @Override public PageData getImagesByTenantId(TenantId tenantId, PageLink pageLink) { + log.trace("Executing getImagesByTenantId [{}]", tenantId); TbResourceInfoFilter filter = TbResourceInfoFilter.builder() .tenantId(tenantId) .resourceTypes(Set.of(ResourceType.IMAGE)) @@ -212,6 +217,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic @Override public PageData getAllImagesByTenantId(TenantId tenantId, PageLink pageLink) { + log.trace("Executing getAllImagesByTenantId [{}]", tenantId); TbResourceInfoFilter filter = TbResourceInfoFilter.builder() .tenantId(tenantId) .resourceTypes(Set.of(ResourceType.IMAGE)) @@ -221,11 +227,13 @@ public class BaseImageService extends BaseResourceService implements ImageServic @Override public byte[] getImageData(TenantId tenantId, TbResourceId imageId) { + log.trace("Executing getImageData [{}] [{}]", tenantId, imageId); return resourceDao.getResourceData(tenantId, imageId); } @Override public byte[] getImagePreview(TenantId tenantId, TbResourceId imageId) { + log.trace("Executing getImagePreview [{}] [{}]", tenantId, imageId); return resourceDao.getResourcePreview(tenantId, imageId); } @@ -260,12 +268,14 @@ public class BaseImageService extends BaseResourceService implements ImageServic @Override public TbResourceInfo findSystemOrTenantImageByEtag(TenantId tenantId, String etag) { + log.trace("Executing findSystemOrTenantImageByEtag [{}] [{}]", tenantId, etag); 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 @Override public boolean replaceBase64WithImageUrl(HasImage entity, String type) { + log.trace("Executing replaceBase64WithImageUrl [{}] [{}] [{}]", entity.getTenantId(), type, entity.getName()); String imageName = "\"" + entity.getName() + "\" "; if (entity.getTenantId() == null || entity.getTenantId().isSysTenantId()) { imageName += "system "; @@ -280,6 +290,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic @Transactional(propagation = Propagation.NOT_SUPPORTED)// 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()); String prefix = "\"" + entity.getName() + "\" "; if (entity.getTenantId() == null || entity.getTenantId().isSysTenantId()) { prefix += "system "; @@ -305,6 +316,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic @Transactional(propagation = Propagation.NOT_SUPPORTED)// 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()); String prefix = "\"" + entity.getTitle() + "\" dashboard"; var result = base64ToImageUrl(entity.getTenantId(), prefix + " image", entity.getImage()); boolean updated = result.isUpdated(); @@ -492,17 +504,20 @@ public class BaseImageService extends BaseResourceService implements ImageServic @Override public void inlineImage(HasImage entity) { + log.trace("Executing inlineImage [{}] [{}] [{}]", entity.getTenantId(), entity.getClass().getSimpleName(), entity.getName()); entity.setImage(inlineImage(entity.getTenantId(), "image", entity.getImage())); } @Override public void inlineImages(Dashboard dashboard) { + log.trace("Executing inlineImage [{}] [Dashboard] [{}]", dashboard.getTenantId(), dashboard.getId()); inlineImage(dashboard); inlineIntoJson(dashboard.getTenantId(), dashboard.getConfiguration()); } @Override public void inlineImages(WidgetTypeDetails widgetTypeDetails) { + log.trace("Executing inlineImage [{}] [WidgetTypeDetails] [{}]", widgetTypeDetails.getTenantId(), widgetTypeDetails.getId()); inlineImage(widgetTypeDetails); inlineIntoJson(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor()); } From 7f5a2df7dc8eea17ce8c2d0e789fd890fbf73947 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 1 Dec 2023 15:17:10 +0200 Subject: [PATCH 03/10] Default system images --- .../install/ThingsboardInstallService.java | 1 + .../service/install/InstallScripts.java | 19 +++++++++++++++++++ .../service/install/update/ImagesUpdater.java | 9 +++++++++ 3 files changed, 29 insertions(+) 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 218f0a9153..6143398b73 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -324,6 +324,7 @@ public class ThingsboardInstallService { // systemDataLoaderService.loadSystemPlugins(); // systemDataLoaderService.loadSystemRules(); installScripts.loadSystemLwm2mResources(); + installScripts.createSystemImages(); if (loadDemo) { log.info("Loading demo data..."); 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 4947d1a338..295ceb8970 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,7 @@ package org.thingsboard.server.service.install; import com.fasterxml.jackson.databind.JsonNode; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -57,6 +58,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Function; +import java.util.stream.Stream; import static org.thingsboard.server.utils.LwM2mObjectModelUtils.toLwm2mResource; @@ -302,11 +304,28 @@ public class InstallScripts { public void updateImages() { imagesUpdater.updateWidgetsBundlesImages(); imagesUpdater.updateWidgetTypesImages(); + createSystemImages(); + imagesUpdater.updateDashboardsImages(); imagesUpdater.updateDeviceProfilesImages(); imagesUpdater.updateAssetProfilesImages(); } + @SneakyThrows + public void createSystemImages() { + Stream dashboardsFiles = Files.list(Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR)); + try (dashboardsFiles) { + dashboardsFiles.forEach(file -> { + try { + Dashboard dashboard = JacksonUtil.OBJECT_MAPPER.readValue(file.toFile(), Dashboard.class); + imagesUpdater.createSystemImages(dashboard); + } catch (Exception e) { + log.error("Failed to create system images for default dashboard {}", file.getFileName(), e); + } + }); + } + } + public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception { Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR); loadDashboardsFromDir(tenantId, customerId, dashboardsDir); diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java index 399aabaa8d..737f25cd60 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.install.update; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.HasImage; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -62,6 +63,14 @@ public class ImagesUpdater { updateImages(dashboardsIds, "dashboard", imageService::replaceBase64WithImageUrl, dashboardDao); } + public void createSystemImages(Dashboard defaultDashboard) { + defaultDashboard.setTenantId(TenantId.SYS_TENANT_ID); + boolean created = imageService.replaceBase64WithImageUrl(defaultDashboard); + if (created) { + log.info("Created system images for default dashboard '{}'", defaultDashboard.getTitle()); + } + } + public void updateDeviceProfilesImages() { log.info("Updating device profiles images..."); var deviceProfiles = new PageDataIterable<>(deviceProfileDao::findAll, 256); From 1db101a9c366fee7a7f8cd54668c4866f6b06310 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 1 Dec 2023 15:48:24 +0200 Subject: [PATCH 04/10] Call loadSystemImages on each upgrade --- .../thingsboard/server/install/ThingsboardInstallService.java | 3 ++- .../thingsboard/server/service/install/InstallScripts.java | 4 +--- 2 files changed, 3 insertions(+), 4 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 6143398b73..d206eaea69 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -282,6 +282,7 @@ public class ThingsboardInstallService { dataUpdateService.upgradeRuleNodes(); systemDataLoaderService.loadSystemWidgets(); installScripts.loadSystemLwm2mResources(); + installScripts.loadSystemImages(); } log.info("Upgrade finished successfully!"); @@ -324,7 +325,7 @@ public class ThingsboardInstallService { // systemDataLoaderService.loadSystemPlugins(); // systemDataLoaderService.loadSystemRules(); installScripts.loadSystemLwm2mResources(); - installScripts.createSystemImages(); + installScripts.loadSystemImages(); if (loadDemo) { log.info("Loading demo data..."); 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 295ceb8970..1866ad4738 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 @@ -304,15 +304,13 @@ public class InstallScripts { public void updateImages() { imagesUpdater.updateWidgetsBundlesImages(); imagesUpdater.updateWidgetTypesImages(); - createSystemImages(); - imagesUpdater.updateDashboardsImages(); imagesUpdater.updateDeviceProfilesImages(); imagesUpdater.updateAssetProfilesImages(); } @SneakyThrows - public void createSystemImages() { + public void loadSystemImages() { Stream dashboardsFiles = Files.list(Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR)); try (dashboardsFiles) { dashboardsFiles.forEach(file -> { From 5b98fa9302c90503654b19ac8f187b792848f02d Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 1 Dec 2023 15:52:17 +0200 Subject: [PATCH 05/10] Remove 'Image' from Resources library --- ui-ngx/src/app/shared/models/resource.models.ts | 9 +++------ ui-ngx/src/assets/locale/locale.constant-en_US.json | 3 +-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index b2b55ef9de..fdaa89aee5 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -24,8 +24,7 @@ export enum ResourceType { LWM2M_MODEL = 'LWM2M_MODEL', PKCS_12 = 'PKCS_12', JKS = 'JKS', - JS_MODULE = 'JS_MODULE', - IMAGE = 'IMAGE' + JS_MODULE = 'JS_MODULE' } export const ResourceTypeMIMETypes = new Map( @@ -33,8 +32,7 @@ export const ResourceTypeMIMETypes = new Map( [ResourceType.LWM2M_MODEL, 'application/xml,text/xml'], [ResourceType.PKCS_12, 'application/x-pkcs12'], [ResourceType.JKS, 'application/x-java-keystore'], - [ResourceType.JS_MODULE, 'text/javascript,application/javascript'], - [ResourceType.IMAGE, 'image/*'] + [ResourceType.JS_MODULE, 'text/javascript,application/javascript'] ] ); @@ -52,8 +50,7 @@ export const ResourceTypeTranslationMap = new Map( [ResourceType.LWM2M_MODEL, 'resource.type.lwm2m-model'], [ResourceType.PKCS_12, 'resource.type.pkcs-12'], [ResourceType.JKS, 'resource.type.jks'], - [ResourceType.JS_MODULE, 'resource.type.js-module'], - [ResourceType.IMAGE, 'resource.type.image'] + [ResourceType.JS_MODULE, 'resource.type.js-module'] ] ); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 22f8c72654..d49bf8dcac 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -3692,8 +3692,7 @@ "jks": "JKS", "js-module": "JS module", "lwm2m-model": "LWM2M model", - "pkcs-12": "PKCS #12", - "image": "Image" + "pkcs-12": "PKCS #12" } }, "rulechain": { From 3233d1b97e51b480cc4ee3f9196490f7e6a73df6 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 1 Dec 2023 16:18:53 +0200 Subject: [PATCH 06/10] SKIP_IMAGES_MIGRATION upgrade option --- .../server/install/ThingsboardInstallService.java | 7 ++++++- .../thingsboard/server/service/install/InstallScripts.java | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) 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 d206eaea69..3fc68b9cc3 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -271,6 +271,12 @@ 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(); + } else { + log.info("Skipping images migration. Run the upgrade with fromVersion as '3.6.2-images' to migrate"); + } //TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache break; default: @@ -282,7 +288,6 @@ public class ThingsboardInstallService { dataUpdateService.upgradeRuleNodes(); systemDataLoaderService.loadSystemWidgets(); installScripts.loadSystemLwm2mResources(); - installScripts.loadSystemImages(); } 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 1866ad4738..598c3c8965 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 @@ -311,6 +311,7 @@ public class InstallScripts { @SneakyThrows public void loadSystemImages() { + log.info("Loading system images..."); Stream dashboardsFiles = Files.list(Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR)); try (dashboardsFiles) { dashboardsFiles.forEach(file -> { From a97ddcdeca17e8a7b888a793edd4411b594ca17b Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 1 Dec 2023 17:59:56 +0200 Subject: [PATCH 07/10] Update logging levels --- .../server/service/install/update/ImagesUpdater.java | 2 +- .../thingsboard/server/dao/resource/BaseImageService.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java index 737f25cd60..6f3210ba90 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java @@ -67,7 +67,7 @@ public class ImagesUpdater { defaultDashboard.setTenantId(TenantId.SYS_TENANT_ID); boolean created = imageService.replaceBase64WithImageUrl(defaultDashboard); if (created) { - log.info("Created system images for default dashboard '{}'", defaultDashboard.getTitle()); + log.debug("Created system images for default dashboard '{}'", defaultDashboard.getTitle()); } } 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 b5ee8b4211..ad3934f76e 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 @@ -24,6 +24,7 @@ import lombok.Data; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; 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; @@ -454,7 +455,11 @@ public class BaseImageService extends BaseResourceService implements ImageServic try { imageInfo = saveImage(image); } catch (Exception e) { - log.warn("[{}][{}] Failed to replace Base64 with image url: {}", tenantId, name, StringUtils.abbreviate(data, 50), e); + if (log.isDebugEnabled()) { // printing stacktrace + log.warn("[{}][{}] Failed to replace Base64 with image url for {}", tenantId, name, StringUtils.abbreviate(data, 50), e); + } else { + log.warn("[{}][{}] Failed to replace Base64 with image url for {}: {}", tenantId, name, StringUtils.abbreviate(data, 50), ExceptionUtils.getMessage(e)); + } return UpdateResult.of(false, data); } } From 605e9987fbe22e683e4ef18c625fcbb73c648dd3 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 4 Dec 2023 12:14:02 +0200 Subject: [PATCH 08/10] Refactor resources validation --- .../server/service/resource/DefaultTbResourceService.java | 6 ++++++ .../org/thingsboard/server/dao/resource/TbResourceDao.java | 2 ++ .../org/thingsboard/server/dao/service/DataValidator.java | 2 +- .../dao/service/validator/ResourceDataValidator.java | 7 ++++++- .../server/dao/sql/resource/JpaTbResourceDao.java | 5 +++++ .../server/dao/sql/resource/TbResourceRepository.java | 3 +++ 6 files changed, 23 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 2cdbd8d7e2..2705092b04 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -53,6 +53,9 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements @Override public TbResource save(TbResource resource, User user) throws ThingsboardException { + if (resource.getResourceType() == ResourceType.IMAGE) { + throw new IllegalArgumentException("Image resource type is not supported"); + } ActionType actionType = resource.getId() == null ? ActionType.ADDED : ActionType.UPDATED; TenantId tenantId = resource.getTenantId(); try { @@ -74,6 +77,9 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements @Override public void delete(TbResource tbResource, User user) { + if (tbResource.getResourceType() == ResourceType.IMAGE) { + throw new IllegalArgumentException("Image resource type is not supported"); + } TbResourceId resourceId = tbResource.getId(); TenantId tenantId = tbResource.getTenantId(); try { diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java index 8d0ce7fb35..0a8e215618 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java @@ -46,4 +46,6 @@ public interface TbResourceDao extends Dao, TenantEntityWithDataDao, byte[] getResourcePreview(TenantId tenantId, TbResourceId resourceId); + long getResourceSize(TenantId tenantId, TbResourceId resourceId); + } 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 3b514f9fda..961fffcdc0 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 @@ -161,7 +161,7 @@ public abstract class DataValidator> { } static void validateQueueNameOrTopic(String value, String fieldName) { - if (StringUtils.isEmpty(value) || value.trim().length() == 0 ) { + if (StringUtils.isEmpty(value) || value.trim().length() == 0) { throw new DataValidationException(String.format("Queue %s should be specified!", fieldName)); } if (!QUEUE_PATTERN.matcher(value).matches()) { 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 37e3f3cb84..26d8ab75fb 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 @@ -90,7 +90,12 @@ public class ResourceDataValidator extends DataValidator { throw new IllegalArgumentException("Resource exceeds the maximum size of " + maxResourceSize + " bytes"); } long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); - validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length, TB_RESOURCE); + 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 (StringUtils.isEmpty(resource.getFileName())) { throw new DataValidationException("Resource file name should be specified!"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java index 370b21a90e..2326d2ffe4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java @@ -104,6 +104,11 @@ public class JpaTbResourceDao extends JpaAbstractDao Date: Mon, 4 Dec 2023 14:13:57 +0200 Subject: [PATCH 09/10] Skip device and asset profiles with no images on upgrade --- .../server/service/install/update/ImagesUpdater.java | 4 ++-- .../org/thingsboard/server/dao/asset/AssetProfileDao.java | 2 +- .../org/thingsboard/server/dao/device/DeviceProfileDao.java | 2 +- .../server/dao/sql/asset/AssetProfileRepository.java | 3 ++- .../thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java | 4 ++-- .../server/dao/sql/device/DeviceProfileRepository.java | 2 ++ .../server/dao/sql/device/JpaDeviceProfileDao.java | 4 ++-- 7 files changed, 12 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java index 6f3210ba90..e711bf2682 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java @@ -73,13 +73,13 @@ public class ImagesUpdater { public void updateDeviceProfilesImages() { log.info("Updating device profiles images..."); - var deviceProfiles = new PageDataIterable<>(deviceProfileDao::findAll, 256); + var deviceProfiles = new PageDataIterable<>(deviceProfileDao::findAllWithImages, 256); updateImages(deviceProfiles, "device profile", imageService::replaceBase64WithImageUrl, deviceProfileDao); } public void updateAssetProfilesImages() { log.info("Updating asset profiles images..."); - var assetProfiles = new PageDataIterable<>(assetProfileDao::findAll, 256); + var assetProfiles = new PageDataIterable<>(assetProfileDao::findAllWithImages, 256); updateImages(assetProfiles, "asset profile", imageService::replaceBase64WithImageUrl, assetProfileDao); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java index 14e023edcd..87e9177d66 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileDao.java @@ -45,6 +45,6 @@ public interface AssetProfileDao extends Dao, ExportableEntityDao< AssetProfile findByName(TenantId tenantId, String profileName); - PageData findAll(PageLink pageLink); + PageData findAllWithImages(PageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java index c5ca8c054d..7bca5ebf8f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java @@ -47,6 +47,6 @@ public interface DeviceProfileDao extends Dao, ExportableEntityDa DeviceProfile findByName(TenantId tenantId, String profileName); - PageData findAll(PageLink pageLink); + PageData findAllWithImages(PageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java index 6c66363ed1..a9acef8608 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetProfileRepository.java @@ -23,7 +23,6 @@ import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.asset.AssetProfileInfo; import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.AssetProfileEntity; -import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity; import java.util.List; import java.util.UUID; @@ -70,4 +69,6 @@ public interface AssetProfileRepository extends JpaRepository findByImageLink(@Param("imageLink") String imageLink, Pageable page); + Page findAllByImageNotNull(Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java index 98bdca1cc9..b57e5c0c40 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetProfileDao.java @@ -99,8 +99,8 @@ public class JpaAssetProfileDao extends JpaAbstractDao findAll(PageLink pageLink) { - return DaoUtil.toPageData(assetProfileRepository.findAll(DaoUtil.toPageable(pageLink))); + public PageData findAllWithImages(PageLink pageLink) { + return DaoUtil.toPageData(assetProfileRepository.findAllByImageNotNull(DaoUtil.toPageable(pageLink))); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java index 996e29f81f..a9baf60e60 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java @@ -81,4 +81,6 @@ public interface DeviceProfileRepository extends JpaRepository findAllByImageNotNull(Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index 33d41b892c..12a91e3cf7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -117,8 +117,8 @@ public class JpaDeviceProfileDao extends JpaAbstractDao findAll(PageLink pageLink) { - return DaoUtil.toPageData(deviceProfileRepository.findAll(DaoUtil.toPageable(pageLink))); + public PageData findAllWithImages(PageLink pageLink) { + return DaoUtil.toPageData(deviceProfileRepository.findAllByImageNotNull(DaoUtil.toPageable(pageLink))); } @Override From 477a823f2d3296e342f0550a29be6c5395f4ec56 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Mon, 4 Dec 2023 16:43:16 +0200 Subject: [PATCH 10/10] Tests improvements --- .../server/controller/ImageControllerTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java index e37471da04..1fcf9abb57 100644 --- a/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/ImageControllerTest.java @@ -17,8 +17,10 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; import com.google.common.hash.Hashing; +import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpMethod; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; @@ -31,6 +33,7 @@ import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.dao.sql.resource.TbResourceRepository; import java.util.Base64; import java.util.List; @@ -45,11 +48,19 @@ public class ImageControllerTest extends AbstractControllerTest { private static final byte[] JPEG_IMAGE = Base64.getDecoder().decode("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"); private static final byte[] SVG_IMAGE = Base64.getDecoder().decode("PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="); + @Autowired + private TbResourceRepository resourceRepository; + @Before public void beforeEach() throws Exception { loginTenantAdmin(); } + @After + public void afterEach() { + resourceRepository.deleteAll(); + } + @Test public void testUploadPngImage() throws Exception { String filename = "my_png_image.png";