Images upgrade; minor refactoring

This commit is contained in:
ViacheslavKlimov 2023-11-30 11:47:40 +02:00
parent 5337943747
commit ddfa55ba6e
27 changed files with 226 additions and 525 deletions

View File

@ -112,6 +112,8 @@ public class ThingsboardInstallService {
} else if ("3.0.1-cassandra".equals(upgradeFromVersion)) {
log.info("Migrating ThingsBoard latest timeseries data from cassandra to SQL database ...");
latestMigrateService.migrate();
} else if (upgradeFromVersion.equals("3.6.2-images")) {
installScripts.updateImages();
} else {
switch (upgradeFromVersion) {
case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
@ -279,7 +281,6 @@ public class ThingsboardInstallService {
log.info("Updating system data...");
dataUpdateService.upgradeRuleNodes();
systemDataLoaderService.loadSystemWidgets();
// installScripts.migrateTenantImages();
installScripts.loadSystemLwm2mResources();
}
log.info("Upgrade finished successfully!");

View File

@ -27,12 +27,9 @@ import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
@ -302,47 +299,12 @@ public class InstallScripts {
}
}
public void migrateTenantImages() {
var widgetsBundles = new PageDataIterable<>(widgetsBundleService::findAllWidgetsBundles, 100);
for (WidgetsBundle widgetsBundle : widgetsBundles) {
try {
boolean updated = imagesUpdater.updateWidgetsBundle(widgetsBundle);
if (updated) {
widgetsBundleService.saveWidgetsBundle(widgetsBundle);
log.info("[{}][{}][{}] Migrated widgets bundle images", widgetsBundle.getTenantId(), widgetsBundle.getId(), widgetsBundle.getTitle());
}
} catch (Exception e) {
log.error("[{}][{}][{}] Failed to migrate widgets bundle images", widgetsBundle.getTenantId(), widgetsBundle.getId(), widgetsBundle.getTitle(), e);
}
}
var widgetTypes = new PageDataIterable<>(widgetTypeService::findAllWidgetTypesIds, 1024);
for (WidgetTypeId widgetTypeId : widgetTypes) {
WidgetTypeDetails widgetTypeDetails = widgetTypeService.findWidgetTypeDetailsById(TenantId.SYS_TENANT_ID, widgetTypeId);
try {
boolean updated = imagesUpdater.updateWidget(widgetTypeDetails);
if (updated) {
widgetTypeService.saveWidgetType(widgetTypeDetails);
log.info("[{}][{}][{}] Migrated widget type images", widgetTypeDetails.getTenantId(), widgetTypeDetails.getId(), widgetTypeDetails.getName());
}
} catch (Exception e) {
log.error("[{}][{}][{}] Failed to migrate widget type images", widgetTypeDetails.getTenantId(), widgetTypeDetails.getId(), widgetTypeDetails.getName(), e);
}
}
var dashboards = new PageDataIterable<>(dashboardService::findAllDashboardsIds, 1024);
for (DashboardId dashboardId : dashboards) {
Dashboard dashboard = dashboardService.findDashboardById(TenantId.SYS_TENANT_ID, dashboardId);
try {
boolean updated = imagesUpdater.updateDashboard(dashboard);
if (updated) {
dashboardService.saveDashboard(dashboard);
log.info("[{}][{}][{}] Migrated dashboard images", dashboard.getTenantId(), dashboardId, dashboard.getTitle());
}
} catch (Exception e) {
log.error("[{}][{}][{}] Failed to migrate dashboard images", dashboard.getTenantId(), dashboardId, dashboard.getTitle(), e);
}
}
public void updateImages() {
imagesUpdater.updateWidgetsBundlesImages();
imagesUpdater.updateWidgetTypesImages();
imagesUpdater.updateDashboardsImages();
imagesUpdater.updateDeviceProfilesImages();
imagesUpdater.updateAssetProfilesImages();
}
public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception {

View File

@ -15,226 +15,141 @@
*/
package org.thingsboard.server.service.install.update;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.asset.AssetProfileDao;
import org.thingsboard.server.dao.dashboard.DashboardDao;
import org.thingsboard.server.dao.device.DeviceProfileDao;
import org.thingsboard.server.dao.resource.ImageService;
import org.thingsboard.server.dao.util.ImageUtils;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.thingsboard.server.dao.widget.WidgetTypeDao;
import org.thingsboard.server.dao.widget.WidgetsBundleDao;
@Component
@Slf4j
@RequiredArgsConstructor
@Slf4j
public class ImagesUpdater {
private final ImageService imageService;
private final WidgetsBundleDao widgetsBundleDao;
private final WidgetTypeDao widgetTypeDao;
private final DashboardDao dashboardDao;
private final DeviceProfileDao deviceProfileDao;
private final AssetProfileDao assetProfileDao;
private static final String IMAGE_NAME_SUFFIX = " - image";
private static final String BACKGROUND_IMAGE_NAME_SUFFIX = " - background image";
private static final String BACKGROUND_IMAGE_KEY_SUFFIX = "#background_image";
private static final String MAP_IMAGE_NAME_SUFFIX = " - map image";
private static final String MAP_IMAGE_KEY_SUFFIX = "#map_image";
private static final String MARKER_IMAGE_NAME_SUFFIX = " - marker image ";
private static final String MARKER_IMAGE_KEY_SUFFIX = "#marker_image_";
public boolean updateDashboard(Dashboard dashboard) {
String imageKeyPrefix = "dashboard_" + dashboard.getUuidId();
String image = dashboard.getImage();
ImageSaveResult result = saveImage(dashboard.getTenantId(), dashboard.getTitle() + IMAGE_NAME_SUFFIX,
imageKeyPrefix + ".image", image, null);
dashboard.setImage(result.getLink());
boolean updated = result.isUpdated();
for (ObjectNode widgetConfig : dashboard.getWidgetsConfig()) {
String fqn;
if (widgetConfig.has("typeFullFqn")) {
fqn = StringUtils.substringAfter(widgetConfig.get("typeFullFqn").asText(), "."); // removing prefix ('system' or 'tenant')
} else {
fqn = widgetConfig.get("bundleAlias").asText() + "." + widgetConfig.get("typeAlias").asText();
}
String widgetName = widgetConfig.get("config").get("title").asText();
updated |= updateWidgetConfig(dashboard.getTenantId(), widgetConfig.get("config"),
dashboard.getTitle() + " - " + widgetName + " widget",
imageKeyPrefix + "." + fqn, fqn);
}
return updated;
}
public boolean updateWidgetsBundle(WidgetsBundle widgetsBundle) {
String bundleName = widgetsBundle.getTitle();
String bundleAlias = widgetsBundle.getAlias();
String image = widgetsBundle.getImage();
ImageSaveResult result = saveImage(widgetsBundle.getTenantId(), bundleName + IMAGE_NAME_SUFFIX, bundleAlias, image, bundleAlias);
String imageLink = result.getLink();
widgetsBundle.setImage(imageLink);
return result.isUpdated();
}
public boolean updateWidget(WidgetTypeDetails widgetType) {
String widgetName = widgetType.getName();
String widgetFqn = widgetType.getFqn();
boolean updated;
String previewImage = widgetType.getImage();
ImageSaveResult result = saveImage(widgetType.getTenantId(), widgetName + IMAGE_NAME_SUFFIX, widgetFqn, previewImage, widgetFqn);
updated = result.isUpdated();
widgetType.setImage(result.getLink());
JsonNode descriptor = widgetType.getDescriptor();
if (!descriptor.isObject()) {
return updated;
}
JsonNode defaultConfig = JacksonUtil.toJsonNode(descriptor.get("defaultConfig").asText());
updated |= updateWidgetConfig(widgetType.getTenantId(), defaultConfig, widgetName, widgetFqn, widgetFqn);
((ObjectNode) descriptor).put("defaultConfig", defaultConfig.toString());
return updated;
}
private boolean updateWidgetConfig(TenantId tenantId, JsonNode widgetConfigJson, String imageNamePrefix, String imageKeyPrefix, String widgetFqn) {
boolean updated = false;
ObjectNode widgetSettings = (ObjectNode) widgetConfigJson.get("settings");
ArrayNode markerImages = (ArrayNode) widgetSettings.get("markerImages");
if (markerImages != null && !markerImages.isEmpty()) {
for (int i = 0; i < markerImages.size(); i++) {
String imageName = imageNamePrefix + MARKER_IMAGE_NAME_SUFFIX + (i + 1);
String imageKey = imageKeyPrefix + MARKER_IMAGE_KEY_SUFFIX + (i + 1);
ImageSaveResult result = saveImage(tenantId, imageName, imageKey, markerImages.get(i).asText(), widgetFqn);
markerImages.set(i, result.getLink());
updated |= result.isUpdated();
}
}
String mapImage = getText(widgetSettings, "mapImageUrl");
if (mapImage != null) {
String imageName = imageNamePrefix + MAP_IMAGE_NAME_SUFFIX;
String imageKey = imageKeyPrefix + MAP_IMAGE_KEY_SUFFIX;
ImageSaveResult result = saveImage(tenantId, imageName, imageKey, mapImage, widgetFqn);
widgetSettings.put("mapImageUrl", result.getLink());
updated |= result.isUpdated();
}
String backgroundImage = getText(widgetSettings, "backgroundImageUrl");
if (backgroundImage != null) {
String imageName = imageNamePrefix + BACKGROUND_IMAGE_NAME_SUFFIX;
String imageKey = imageKeyPrefix + BACKGROUND_IMAGE_KEY_SUFFIX;
ImageSaveResult result = saveImage(tenantId, imageName, imageKey, backgroundImage, widgetFqn);
widgetSettings.put("backgroundImageUrl", result.getLink());
updated |= result.isUpdated();
}
JsonNode backgroundConfigNode = widgetSettings.get("background");
if (backgroundConfigNode != null && backgroundConfigNode.isObject()) {
ObjectNode backgroundConfig = (ObjectNode) backgroundConfigNode;
if ("image".equals(getText(backgroundConfig, "type"))) {
String imageBase64 = getText(backgroundConfig, "imageBase64");
if (imageBase64 != null) {
String imageName = imageNamePrefix + BACKGROUND_IMAGE_NAME_SUFFIX;
String imageKey = imageKeyPrefix + BACKGROUND_IMAGE_KEY_SUFFIX;
ImageSaveResult result = saveImage(tenantId, imageName, imageKey, imageBase64, widgetFqn);
backgroundConfig.set("imageBase64", null);
backgroundConfig.put("imageUrl", result.getLink());
backgroundConfig.put("type", "imageUrl");
updated |= result.isUpdated();
public void updateWidgetsBundlesImages() {
log.info("Updating widgets bundles images...");
var widgetsBundles = new PageDataIterable<>(widgetsBundleDao::findAllWidgetsBundles, 128);
int updatedCount = 0;
int totalCount = 0;
for (WidgetsBundle widgetsBundle : widgetsBundles) {
totalCount++;
try {
boolean updated = imageService.replaceBase64WithImageUrl(widgetsBundle, "bundle");
if (updated) {
widgetsBundleDao.save(widgetsBundle.getTenantId(), widgetsBundle);
log.debug("[{}][{}][{}] Updated widgets bundle images", widgetsBundle.getTenantId(), widgetsBundle.getId(), widgetsBundle.getTitle());
updatedCount++;
}
} catch (Exception e) {
log.error("[{}][{}][{}] Failed to update widgets bundle images", widgetsBundle.getTenantId(), widgetsBundle.getId(), widgetsBundle.getTitle(), e);
}
}
return updated;
log.info("Updated {} widgets bundles out of {}", updatedCount, totalCount);
}
private ImageSaveResult saveImage(TenantId tenantId, String name, String key, String data,
String existingImageQuery) {
if (data == null) {
return new ImageSaveResult(null, false);
}
String base64Data = StringUtils.substringAfter(data, "base64,");
if (base64Data.isEmpty()) {
return new ImageSaveResult(data, false);
}
String imageMediaType = StringUtils.substringBetween(data, "data:", ";base64");
String extension = ImageUtils.mediaTypeToFileExtension(imageMediaType);
key += "." + extension;
byte[] imageData = Base64.getDecoder().decode(base64Data);
String imageLink = saveImage(tenantId, name, key, imageData, imageMediaType, existingImageQuery);
return new ImageSaveResult(imageLink, !imageLink.equals(data));
}
@SneakyThrows
private String saveImage(TenantId tenantId, String name, String key, byte[] imageData, String mediaType,
String existingImageQuery) {
TbResourceInfo imageInfo = imageService.getImageInfoByTenantIdAndKey(tenantId, key);
if (imageInfo == null && !tenantId.isSysTenantId() && existingImageQuery != null) {
List<TbResourceInfo> similarImages = imageService.findSimilarImagesByTenantIdAndKeyStartingWith(TenantId.SYS_TENANT_ID, imageData, existingImageQuery);
if (similarImages.isEmpty()) {
similarImages = imageService.findSimilarImagesByTenantIdAndKeyStartingWith(tenantId, imageData, existingImageQuery);
}
if (!similarImages.isEmpty()) {
imageInfo = similarImages.get(0);
if (similarImages.size() > 1) {
log.debug("Found more than one image resources for key {}: {}", existingImageQuery, similarImages);
public void updateWidgetTypesImages() {
log.info("Updating widget types images...");
var widgetTypes = new PageDataIterable<>(widgetTypeDao::findAllWidgetTypesIds, 1024);
int updatedCount = 0;
int totalCount = 0;
for (WidgetTypeId widgetTypeId : widgetTypes) {
totalCount++;
WidgetTypeDetails widgetTypeDetails = widgetTypeDao.findById(TenantId.SYS_TENANT_ID, widgetTypeId.getId());
try {
boolean updated = imageService.replaceBase64WithImageUrl(widgetTypeDetails);
if (updated) {
widgetTypeDao.save(widgetTypeDetails.getTenantId(), widgetTypeDetails);
log.debug("[{}][{}][{}] Updated widget type images", widgetTypeDetails.getTenantId(), widgetTypeDetails.getId(), widgetTypeDetails.getName());
updatedCount++;
}
String link = imageInfo.getLink();
log.info("[{}] Using image {} for {}", tenantId, link, key);
return link;
} catch (Exception e) {
log.error("[{}][{}][{}] Failed to update widget type images", widgetTypeDetails.getTenantId(), widgetTypeDetails.getId(), widgetTypeDetails.getName(), e);
}
}
TbResource image;
if (imageInfo == null) {
image = new TbResource();
image.setTenantId(tenantId);
image.setResourceType(ResourceType.IMAGE);
image.setResourceKey(key);
} else if (tenantId.isSysTenantId()) {
image = new TbResource(imageInfo);
} else {
return imageInfo.getLink();
log.info("Updated {} widget types out of {}", updatedCount, totalCount);
}
public void updateDashboardsImages() {
log.info("Updating dashboards images...");
var dashboards = new PageDataIterable<>(dashboardDao::findAllIds, 1024);
int updatedCount = 0;
int totalCount = 0;
for (DashboardId dashboardId : dashboards) {
totalCount++;
Dashboard dashboard = dashboardDao.findById(TenantId.SYS_TENANT_ID, dashboardId.getId());
try {
boolean updated = imageService.replaceBase64WithImageUrl(dashboard);
if (updated) {
dashboardDao.save(dashboard.getTenantId(), dashboard);
log.info("[{}][{}][{}] Updated dashboard images", dashboard.getTenantId(), dashboardId, dashboard.getTitle());
updatedCount++;
}
} catch (Exception e) {
log.error("[{}][{}][{}] Failed to update dashboard images", dashboard.getTenantId(), dashboardId, dashboard.getTitle(), e);
}
}
image.setTitle(name);
image.setFileName(key);
image.setDescriptor(JacksonUtil.newObjectNode()
.put("mediaType", mediaType));
image.setData(imageData);
TbResourceInfo savedImage = imageService.saveImage(image);
log.info("[{}] {} image '{}' ({})", tenantId, imageInfo == null ? "Created" : "Updated",
image.getTitle(), image.getResourceKey());
return savedImage.getLink();
log.info("Updated {} dashboards out of {}", updatedCount, totalCount);
}
private String getText(JsonNode jsonNode, String field) {
return Optional.ofNullable(jsonNode.get(field))
.filter(JsonNode::isTextual)
.map(JsonNode::asText).orElse(null);
public void updateDeviceProfilesImages() {
log.info("Updating device profiles images...");
var deviceProfiles = new PageDataIterable<>(deviceProfileDao::findAll, 256);
int updatedCount = 0;
int totalCount = 0;
for (DeviceProfile deviceProfile : deviceProfiles) {
totalCount++;
try {
boolean updated = imageService.replaceBase64WithImageUrl(deviceProfile, "device profile");
if (updated) {
deviceProfileDao.save(deviceProfile.getTenantId(), deviceProfile);
log.debug("[{}][{}][{}] Updated device profile images", deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile.getName());
updatedCount++;
}
} catch (Exception e) {
log.error("[{}][{}][{}] Failed to update device profile images", deviceProfile.getTenantId(), deviceProfile.getId(), deviceProfile.getName(), e);
}
}
log.info("Updated {} device profiles out of {}", updatedCount, totalCount);
}
@Data
public static class ImageSaveResult {
private final String link;
private final boolean updated;
public void updateAssetProfilesImages() {
log.info("Updating asset profiles images...");
var assetProfiles = new PageDataIterable<>(assetProfileDao::findAll, 256);
int updatedCount = 0;
int totalCount = 0;
for (AssetProfile assetProfile : assetProfiles) {
totalCount++;
try {
boolean updated = imageService.replaceBase64WithImageUrl(assetProfile, "asset profile");
if (updated) {
assetProfileDao.save(assetProfile.getTenantId(), assetProfile);
log.debug("[{}][{}][{}] Updated asset profile images", assetProfile.getTenantId(), assetProfile.getId(), assetProfile.getName());
updatedCount++;
}
} catch (Exception e) {
log.error("[{}][{}][{}] Failed to update asset profile images", assetProfile.getTenantId(), assetProfile.getId(), assetProfile.getName(), e);
}
}
log.info("Updated {} asset profiles out of {}", updatedCount, totalCount);
}
}

View File

@ -1,145 +0,0 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.install.update;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Base64Utils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.dao.util.ImageUtils;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
@Slf4j
public class SystemImagesMigrator {
private static final Path dataDir = Path.of(
"/home/*/thingsboard-ce/application/src/main/data"
);
private static final Path imagesDir = dataDir.resolve("images");
private static final Path widgetBundlesDir = dataDir.resolve("json").resolve("system").resolve("widget_bundles");
private static final Path widgetTypesDir = dataDir.resolve("json").resolve("system").resolve("widget_types");
private static final Path demoDashboardsDir = dataDir.resolve("json").resolve("demo").resolve("dashboards");
public static void main(String[] args) throws Exception {
Files.list(widgetTypesDir).forEach(file -> {
ObjectNode widgetTypeJson = (ObjectNode) JacksonUtil.toJsonNode(file.toFile());
updateWidget(widgetTypeJson);
saveJson(file, widgetTypeJson);
});
Files.list(widgetBundlesDir).forEach(file -> {
JsonNode widgetsBundleDescriptorJson = JacksonUtil.toJsonNode(file.toFile());
ObjectNode widgetsBundleJson = (ObjectNode) widgetsBundleDescriptorJson.get("widgetsBundle");
updateWidgetsBundle(widgetsBundleJson);
saveJson(file, widgetsBundleDescriptorJson);
});
Files.list(demoDashboardsDir).forEach(file -> {
ObjectNode dashboardJson = (ObjectNode) JacksonUtil.toJsonNode(file.toFile());
updateDashboard(dashboardJson);
saveJson(file, dashboardJson);
});
}
public static void updateWidgetsBundle(ObjectNode widgetsBundleJson) {
String imageLink = getText(widgetsBundleJson, "image");
widgetsBundleJson.put("image", inlineImage(imageLink, "widget_bundles"));
}
public static void updateWidget(ObjectNode widgetJson) {
String previewImageLink = widgetJson.get("image").asText();
widgetJson.put("image", inlineImage(previewImageLink, "widgets"));
ObjectNode descriptor = (ObjectNode) widgetJson.get("descriptor");
JsonNode defaultConfig = JacksonUtil.toJsonNode(descriptor.get("defaultConfig").asText());
updateWidgetConfig(defaultConfig, "widgets");
descriptor.put("defaultConfig", defaultConfig.toString());
}
public static void updateDashboard(ObjectNode dashboardJson) {
String image = getText(dashboardJson, "image");
dashboardJson.put("image", inlineImage(image, "dashboards"));
dashboardJson.get("configuration").get("widgets").elements().forEachRemaining(widgetConfig -> {
updateWidgetConfig(widgetConfig.get("config"), "dashboards");
});
}
private static void updateWidgetConfig(JsonNode widgetConfigJson, String directory) {
ObjectNode widgetSettings = (ObjectNode) widgetConfigJson.get("settings");
ArrayNode markerImages = (ArrayNode) widgetSettings.get("markerImages");
if (markerImages != null && !markerImages.isEmpty()) {
for (int i = 0; i < markerImages.size(); i++) {
markerImages.set(i, inlineImage(markerImages.get(i).asText(), directory));
}
}
String mapImage = getText(widgetSettings, "mapImageUrl");
if (mapImage != null) {
widgetSettings.put("mapImageUrl", inlineImage(mapImage, directory));
}
String backgroundImage = getText(widgetSettings, "backgroundImageUrl");
if (backgroundImage != null) {
widgetSettings.put("backgroundImageUrl", inlineImage(backgroundImage, directory));
}
JsonNode backgroundConfigNode = widgetSettings.get("background");
if (backgroundConfigNode != null && backgroundConfigNode.isObject()) {
ObjectNode backgroundConfig = (ObjectNode) backgroundConfigNode;
if ("imageUrl".equals(getText(backgroundConfig, "type"))) {
String imageLink = getText(backgroundConfig, "imageUrl");
if (imageLink != null && imageLink.startsWith("/api/images")) {
backgroundConfig.put("imageBase64", inlineImage(imageLink, directory));
backgroundConfig.set("imageUrl", null);
backgroundConfig.put("type", "image");
}
}
}
}
@SneakyThrows
private static String inlineImage(String url, String subDir) {
if (url != null && url.startsWith("/api/images")) {
String imageKey = StringUtils.substringAfterLast(url, "/");
Path file = imagesDir.resolve(subDir).resolve(imageKey);
String mediaType = ImageUtils.fileExtensionToMediaType(StringUtils.substringAfterLast(imageKey, "."));
return "data:" + mediaType + ";base64," + Base64Utils.encodeToString(Files.readAllBytes(file));
} else {
return url;
}
}
private static String getText(JsonNode jsonNode, String field) {
return Optional.ofNullable(jsonNode.get(field))
.filter(JsonNode::isTextual)
.map(JsonNode::asText).orElse(null);
}
@SneakyThrows
private static void saveJson(Path file, JsonNode json) {
Files.write(file, JacksonUtil.toPrettyString(json).getBytes(StandardCharsets.UTF_8));
}
}

View File

@ -29,7 +29,7 @@ import org.thingsboard.server.dao.entity.EntityDaoService;
import java.util.List;
public interface DashboardService extends EntityDaoService {
Dashboard findDashboardById(TenantId tenantId, DashboardId dashboardId);
ListenableFuture<Dashboard> findDashboardByIdAsync(TenantId tenantId, DashboardId dashboardId);
@ -74,6 +74,4 @@ public interface DashboardService extends EntityDaoService {
List<Dashboard> findTenantDashboardsByTitle(TenantId tenantId, String title);
PageData<DashboardId> findAllDashboardsIds(PageLink pageLink);
}

View File

@ -16,7 +16,6 @@
package org.thingsboard.server.dao.resource;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasImage;
import org.thingsboard.server.common.data.TbImageDeleteResult;
import org.thingsboard.server.common.data.TbResource;
@ -25,11 +24,8 @@ import org.thingsboard.server.common.data.id.TbResourceId;
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.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import java.util.List;
public interface ImageService {
TbResourceInfo saveImage(TbResource image);
@ -48,11 +44,9 @@ public interface ImageService {
TbImageDeleteResult deleteImage(TbResourceInfo imageInfo, boolean force);
List<TbResourceInfo> findSimilarImagesByTenantIdAndKeyStartingWith(TenantId tenantId, byte[] data, String imageKeyStartingWith);
TbResourceInfo findImageByTenantIdAndEtag(TenantId tenantId, String etag);
boolean replaceBase64WithImageUrl(HasImage hasImage, String title, String type);
boolean replaceBase64WithImageUrl(HasImage entity, String type);
boolean replaceBase64WithImageUrl(Dashboard dashboard);
boolean replaceBase64WithImageUrl(WidgetTypeDetails widgetType);

View File

@ -27,7 +27,6 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.EntityDaoService;
import java.util.List;
import java.util.Set;
public interface ResourceService extends EntityDaoService {
@ -55,8 +54,6 @@ public interface ResourceService extends EntityDaoService {
PageData<TbResource> findTenantResourcesByResourceTypeAndPageLink(TenantId tenantId, ResourceType lwm2mModel, PageLink pageLink);
Set<String> findResourceKeysByTenantIdResourceTypeAndKeyPrefix(TenantId tenantId, ResourceType resourceType, String key);
void deleteResource(TenantId tenantId, TbResourceId resourceId);
void deleteResource(TenantId tenantId, TbResourceId resourceId, boolean force);

View File

@ -62,6 +62,4 @@ public interface WidgetTypeService extends EntityDaoService {
void deleteWidgetTypesByTenantId(TenantId tenantId);
PageData<WidgetTypeId> findAllWidgetTypesIds(PageLink pageLink);
}

View File

@ -46,8 +46,6 @@ public interface WidgetsBundleService extends EntityDaoService {
List<WidgetsBundle> findAllTenantWidgetsBundlesByTenantId(TenantId tenantId);
PageData<WidgetsBundle> findAllWidgetsBundles(PageLink pageLink);
void deleteWidgetsBundlesByTenantId(TenantId tenantId);
}

View File

@ -15,7 +15,7 @@
*/
package org.thingsboard.server.common.data;
public interface HasImage extends HasTenantId {
public interface HasImage extends HasTenantId, HasName {
String getImage();

View File

@ -21,7 +21,6 @@ import org.thingsboard.server.common.data.id.AssetProfileId;
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.widget.WidgetsBundle;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.ExportableEntityDao;
import org.thingsboard.server.dao.ImageContainerDao;
@ -45,4 +44,7 @@ public interface AssetProfileDao extends Dao<AssetProfile>, ExportableEntityDao<
AssetProfileInfo findDefaultAssetProfileInfo(TenantId tenantId);
AssetProfile findByName(TenantId tenantId, String profileName);
PageData<AssetProfile> findAll(PageLink pageLink);
}

View File

@ -145,7 +145,7 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetPr
}
AssetProfile savedAssetProfile;
try {
imageService.replaceBase64WithImageUrl(assetProfile, assetProfile.getName(), "asset profile");
imageService.replaceBase64WithImageUrl(assetProfile, "asset profile");
savedAssetProfile = assetProfileDao.saveAndFlush(assetProfile.getTenantId(), assetProfile);
publishEvictEvent(new AssetProfileEvictEvent(savedAssetProfile.getTenantId(), savedAssetProfile.getName(),
oldAssetProfile != null ? oldAssetProfile.getName() : null, savedAssetProfile.getId(), savedAssetProfile.isDefault()));

View File

@ -371,11 +371,6 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
return dashboardDao.findByTenantIdAndTitle(tenantId.getId(), title);
}
@Override
public PageData<DashboardId> findAllDashboardsIds(PageLink pageLink) {
return dashboardDao.findAllIds(pageLink);
}
private final PaginatedRemover<TenantId, DashboardId> tenantDashboardsRemover = new PaginatedRemover<>() {
@Override

View File

@ -17,7 +17,6 @@ package org.thingsboard.server.dao.device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.asset.AssetProfileInfo;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -47,4 +46,7 @@ public interface DeviceProfileDao extends Dao<DeviceProfile>, ExportableEntityDa
DeviceProfile findByProvisionDeviceKey(String provisionDeviceKey);
DeviceProfile findByName(TenantId tenantId, String profileName);
PageData<DeviceProfile> findAll(PageLink pageLink);
}

View File

@ -182,7 +182,7 @@ public class DeviceProfileServiceImpl extends AbstractCachedEntityService<Device
}
DeviceProfile savedDeviceProfile;
try {
imageService.replaceBase64WithImageUrl(deviceProfile, deviceProfile.getName(), "device profile");
imageService.replaceBase64WithImageUrl(deviceProfile, "device profile");
savedDeviceProfile = deviceProfileDao.saveAndFlush(deviceProfile.getTenantId(), deviceProfile);
publishEvictEvent(new DeviceProfileEvictEvent(savedDeviceProfile.getTenantId(), savedDeviceProfile.getName(),
oldDeviceProfile != null ? oldDeviceProfile.getName() : null, savedDeviceProfile.getId(), savedDeviceProfile.isDefault(),

View File

@ -19,21 +19,20 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.hash.Hashing;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.RegexUtils;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasImage;
import org.thingsboard.server.common.data.ImageDescriptor;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TbImageDeleteResult;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
@ -66,7 +65,6 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.regex.Pattern;
@ -177,19 +175,16 @@ public class BaseImageService extends BaseResourceService implements ImageServic
String basename = StringUtils.substringBeforeLast(filename, ".");
String extension = StringUtils.substringAfterLast(filename, ".");
Pattern similarImagesPattern = Pattern.compile(
Pattern.quote(basename) + "_(\\d+)\\.?" + Pattern.quote(extension)
Set<String> existing = resourceInfoDao.findKeysByTenantIdAndResourceTypeAndResourceKeyPrefix(
tenantId, ResourceType.IMAGE, basename
);
int maxImageIdx = resourceInfoDao.findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(
tenantId, ResourceType.IMAGE, basename + "_").stream()
.map(key -> RegexUtils.getMatch(key, similarImagesPattern, 1))
.filter(Objects::nonNull).mapToInt(Integer::parseInt)
.max().orElse(0);
String uniqueKey = basename + "_" + (maxImageIdx + 1);
if (!extension.isEmpty()) {
uniqueKey += "." + extension;
String resourceKey = filename;
int idx = 1;
while (existing.contains(resourceKey)) {
resourceKey = basename + "_(" + idx + ")." + extension;
idx++;
}
return uniqueKey;
return resourceKey;
}
@Override
@ -259,47 +254,57 @@ public class BaseImageService extends BaseResourceService implements ImageServic
return result.success(success).build();
}
@Override
public List<TbResourceInfo> findSimilarImagesByTenantIdAndKeyStartingWith(TenantId tenantId, byte[] data, String imageKeyStartingWith) {
String etag = calculateEtag(data);
return resourceInfoDao.findByTenantIdAndEtagAndKeyStartingWith(tenantId, etag, imageKeyStartingWith);
}
@Override
public TbResourceInfo findImageByTenantIdAndEtag(TenantId tenantId, String etag) {
return resourceInfoDao.findByTenantIdAndEtag(tenantId, ResourceType.IMAGE, etag);
}
@Override
public boolean replaceBase64WithImageUrl(HasImage entity, String title, String type) {
entity.setImage(base64ToImageUrl(entity.getTenantId(), "\"" + title + "\" " + type + " image", entity.getImage()));
return true; //TODO: should return true only if something is changed.
public boolean replaceBase64WithImageUrl(HasImage entity, String type) {
String imageName = "\"" + entity.getName() + "\" ";
if (entity.getTenantId() == null || entity.getTenantId().isSysTenantId()) {
imageName += "system ";
}
imageName = imageName + type + " image";
UpdateResult result = base64ToImageUrl(entity.getTenantId(), imageName, entity.getImage());
entity.setImage(result.getValue());
return result.isUpdated();
}
@Override
public boolean replaceBase64WithImageUrl(WidgetTypeDetails entity) {
String prefix = "\"" + entity.getName() + "\" widget"; // TODO: "system widget" if SYS TENANT ID;
entity.setImage(base64ToImageUrl(entity.getTenantId(), prefix + " image", entity.getImage()));
String prefix = "\"" + entity.getName() + "\" ";
if (entity.getTenantId() == null || entity.getTenantId().isSysTenantId()) {
prefix += "system ";
}
prefix += "widget";
UpdateResult result = base64ToImageUrl(entity.getTenantId(), prefix + " image", entity.getImage());
entity.setImage(result.getValue());
boolean updated = result.isUpdated();
if (entity.getDescriptor().isObject()) {
ObjectNode descriptor = (ObjectNode) entity.getDescriptor();
JsonNode defaultConfig = JacksonUtil.toJsonNode(descriptor.get("defaultConfig").asText());
base64ToImageUrlUsingMapping(entity.getTenantId(), WIDGET_TYPE_BASE64_MAPPING, Collections.singletonMap("prefix", prefix), defaultConfig);
updated |= base64ToImageUrlUsingMapping(entity.getTenantId(), WIDGET_TYPE_BASE64_MAPPING, Collections.singletonMap("prefix", prefix), defaultConfig);
descriptor.put("defaultConfig", defaultConfig.toString());
}
base64ToImageUrlRecursively(entity.getTenantId(), prefix, entity.getDescriptor());
return true; //TODO: should return true only if something is changed.
updated |= base64ToImageUrlRecursively(entity.getTenantId(), prefix, entity.getDescriptor());
return updated;
}
@Override
public boolean replaceBase64WithImageUrl(Dashboard entity) {
String prefix = "\"" + entity.getTitle() + "\" dashboard";
entity.setImage(base64ToImageUrl(entity.getTenantId(), prefix + " image", entity.getImage()));
base64ToImageUrlUsingMapping(entity.getTenantId(), DASHBOARD_BASE64_MAPPING, Collections.singletonMap("prefix", prefix), entity.getConfiguration());
base64ToImageUrlRecursively(entity.getTenantId(), prefix, entity.getConfiguration());
return true; //TODO: should return true only if something is changed.
var result = base64ToImageUrl(entity.getTenantId(), prefix + " image", entity.getImage());
boolean updated = result.isUpdated();
entity.setImage(result.getValue());
updated |= base64ToImageUrlUsingMapping(entity.getTenantId(), DASHBOARD_BASE64_MAPPING, Collections.singletonMap("prefix", prefix), entity.getConfiguration());
updated |= base64ToImageUrlRecursively(entity.getTenantId(), prefix, entity.getConfiguration());
return updated;
}
private void base64ToImageUrlUsingMapping(TenantId tenantId, Map<String, String> mapping, Map<String, String> templateParams, JsonNode configuration) {
private boolean base64ToImageUrlUsingMapping(TenantId tenantId, Map<String, String> mapping, Map<String, String> templateParams, JsonNode configuration) {
boolean updated = false;
for (var entry : mapping.entrySet()) {
String expression = entry.getValue();
Queue<JsonPathProcessingTask> tasks = new LinkedList<>();
@ -330,8 +335,8 @@ public class BaseImageService extends BaseResourceService implements ImageServic
String variableName = null;
String variableValue = null;
if (token.contains("[$")) {
variableName = org.apache.commons.lang3.StringUtils.substringBetween(token, "[$", "]");
token = org.apache.commons.lang3.StringUtils.substringBefore(token, "[$");
variableName = StringUtils.substringBetween(token, "[$", "]");
token = StringUtils.substringBefore(token, "[$");
}
if (node.has(token)) {
JsonNode value = node.get(token);
@ -344,16 +349,20 @@ public class BaseImageService extends BaseResourceService implements ImageServic
name = name.replace("$" + replacements.getKey(), replacements.getValue());
}
if (node.isObject() && value.isTextual()) {
((ObjectNode) node).put(token, base64ToImageUrl(tenantId, name, value.asText()));
var result = base64ToImageUrl(tenantId, name, value.asText());
((ObjectNode) node).put(token, result.getValue());
updated |= result.isUpdated();
} else if (value.isArray()) {
ArrayNode array = (ArrayNode) value;
for (int i = 0; i < array.size(); i++) {
String arrayElementName = name.replace("$index", Integer.toString(i));
array.set(i, base64ToImageUrl(tenantId, arrayElementName, array.get(i).asText()));
UpdateResult result = base64ToImageUrl(tenantId, arrayElementName, array.get(i).asText());
array.set(i, result.getValue());
updated |= result.isUpdated();
}
}
} else {
if (org.thingsboard.server.common.data.StringUtils.isNotEmpty(variableName) && org.thingsboard.server.common.data.StringUtils.isNotEmpty(variableValue)) {
if (StringUtils.isNotEmpty(variableName) && StringUtils.isNotEmpty(variableValue)) {
tasks.add(task.next(value, variableName, variableValue));
} else {
tasks.add(task.next(value));
@ -363,17 +372,18 @@ public class BaseImageService extends BaseResourceService implements ImageServic
}
}
}
return updated;
}
private String base64ToImageUrl(TenantId tenantId, String name, String data) {
private UpdateResult base64ToImageUrl(TenantId tenantId, String name, String data) {
return base64ToImageUrl(tenantId, name, data, false);
}
private static final Pattern TB_IMAGE_METADATA_PATTERN = Pattern.compile("^tb-image:(.*):(.*);data:(.*);.*");
private String base64ToImageUrl(TenantId tenantId, String name, String data, boolean strict) {
private UpdateResult base64ToImageUrl(TenantId tenantId, String name, String data, boolean strict) {
if (StringUtils.isBlank(data)) {
return data;
return UpdateResult.of(false, data);
}
var matcher = TB_IMAGE_METADATA_PATTERN.matcher(data);
boolean matches = matcher.matches();
@ -385,67 +395,58 @@ public class BaseImageService extends BaseResourceService implements ImageServic
mdResourceName = new String(Base64Utils.decodeFromString(matcher.group(2)), StandardCharsets.UTF_8);
mdMediaType = matcher.group(3);
} else if (data.startsWith(DataConstants.TB_IMAGE_PREFIX + "data:image/") || (!strict && data.startsWith("data:image/"))) {
mdMediaType = org.apache.commons.lang3.StringUtils.substringBetween(data, "data:", ";base64");
mdMediaType = StringUtils.substringBetween(data, "data:", ";base64");
} else {
return data;
return UpdateResult.of(false, data);
}
String base64Data = org.apache.commons.lang3.StringUtils.substringAfter(data, "base64,");
String base64Data = StringUtils.substringAfter(data, "base64,");
String extension = ImageUtils.mediaTypeToFileExtension(mdMediaType);
byte[] imageData = Base64.getDecoder().decode(base64Data);
String etag = Hashing.sha256().hashBytes(imageData).toString();
String etag = calculateEtag(imageData);
var imageInfo = findImageByTenantIdAndEtag(tenantId, etag);
if (imageInfo == null) {
TbResource image = new TbResource();
image.setTenantId(tenantId);
image.setResourceType(ResourceType.IMAGE);
if (StringUtils.isBlank(mdResourceName)) {
mdResourceName = name;
}
image.setTitle(mdResourceName);
String fileName;
if (StringUtils.isBlank(mdResourceKey)) {
fileName = mdResourceName.toLowerCase()
//TODO: improve to list all the special characters via regexp or similar
.replace("'", "")
.replace("\"", "")
.replace(" ", "_")
.replace("/", "_");
mdResourceKey = fileName + "." + extension;
.replace("'", "").replace("\"", "")
.replace(" ", "_").replace("/", "_")
+ "." + extension;
} else {
fileName = mdResourceKey.split("\\.")[0];
fileName = mdResourceKey;
}
Set<String> existingKeys = findResourceKeysByTenantIdResourceTypeAndKeyPrefix(tenantId, ResourceType.IMAGE, mdResourceKey);
int idx = 1;
while (existingKeys.contains(mdResourceKey)) {
mdResourceKey = fileName + "_(" + idx + ")." + extension;
idx++;
}
image.setResourceKey(mdResourceKey);
image.setTitle(mdResourceName);
image.setFileName(mdResourceKey);
image.setFileName(fileName);
image.setDescriptor(JacksonUtil.newObjectNode().put("mediaType", mdMediaType));
image.setData(imageData);
imageInfo = saveImage(image);
}
return DataConstants.TB_IMAGE_PREFIX + imageInfo.getLink();
return UpdateResult.of(true, DataConstants.TB_IMAGE_PREFIX + imageInfo.getLink());
}
private void base64ToImageUrlRecursively(TenantId tenantId, String title, JsonNode root) {
private boolean base64ToImageUrlRecursively(TenantId tenantId, String title, JsonNode root) {
boolean updated = false;
Queue<JsonNodeProcessingTask> tasks = new LinkedList<>();
tasks.add(new JsonNodeProcessingTask(title, root));
while (!tasks.isEmpty()) {
JsonNodeProcessingTask task = tasks.poll();
JsonNode node = task.getNode();
String currentPath = org.thingsboard.server.common.data.StringUtils.isBlank(task.getPath()) ? "" : (task.getPath() + " ");
String currentPath = StringUtils.isBlank(task.getPath()) ? "" : (task.getPath() + " ");
if (node.isObject()) {
ObjectNode on = (ObjectNode) node;
for (Iterator<String> it = on.fieldNames(); it.hasNext(); ) {
String childName = it.next();
JsonNode childValue = on.get(childName);
if (childValue.isTextual()) {
on.put(childName, base64ToImageUrl(tenantId, currentPath + childName, childValue.asText(), true));
UpdateResult result = base64ToImageUrl(tenantId, currentPath + childName, childValue.asText(), true);
on.put(childName, result.getValue());
updated |= result.isUpdated();
} else if (childValue.isObject() || childValue.isArray()) {
tasks.add(new JsonNodeProcessingTask(currentPath + childName, childValue));
}
@ -461,6 +462,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic
}
}
}
return updated;
}
@Override
@ -486,7 +488,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic
while (!tasks.isEmpty()) {
JsonNodeProcessingTask task = tasks.poll();
JsonNode node = task.getNode();
String currentPath = org.thingsboard.server.common.data.StringUtils.isBlank(task.getPath()) ? "" : (task.getPath() + ".");
String currentPath = StringUtils.isBlank(task.getPath()) ? "" : (task.getPath() + ".");
if (node.isObject()) {
ObjectNode on = (ObjectNode) node;
for (Iterator<String> it = on.fieldNames(); it.hasNext(); ) {
@ -536,7 +538,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic
}
private ImageCacheKey getKeyFromUrl(TenantId tenantId, String url) {
if (org.thingsboard.server.common.data.StringUtils.isBlank(url)) {
if (StringUtils.isBlank(url)) {
return null;
}
TenantId imageTenantId = null;
@ -555,4 +557,10 @@ public class BaseImageService extends BaseResourceService implements ImageServic
}
return null;
}
@Data(staticConstructor = "of")
private static class UpdateResult {
private final boolean updated;
private final String value;
}
}

View File

@ -44,10 +44,8 @@ import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.service.validator.ResourceDataValidator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID;
import static org.thingsboard.server.dao.service.Validator.validateId;
@ -199,13 +197,6 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
return resourceDao.findResourcesByTenantIdAndResourceType(tenantId, resourceType, pageLink);
}
@Override
public Set<String> findResourceKeysByTenantIdResourceTypeAndKeyPrefix(TenantId tenantId, ResourceType resourceType, String key) {
log.trace("Executing findResourceKeysByTenantIdAndPrefix [{}][{}]", tenantId, key);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
return resourceDao.findResourceKeysByTenantIdResourceTypeAndKeyPrefix(tenantId, resourceType, key);
}
@Override
public void deleteResourcesByTenantId(TenantId tenantId) {
log.trace("Executing deleteResourcesByTenantId, tenantId [{}]", tenantId);

View File

@ -26,7 +26,6 @@ import org.thingsboard.server.dao.ExportableEntityDao;
import org.thingsboard.server.dao.TenantEntityWithDataDao;
import java.util.List;
import java.util.Set;
public interface TbResourceDao extends Dao<TbResource>, TenantEntityWithDataDao, ExportableEntityDao<TbResourceId, TbResource> {
@ -47,5 +46,4 @@ public interface TbResourceDao extends Dao<TbResource>, TenantEntityWithDataDao,
byte[] getResourcePreview(TenantId tenantId, TbResourceId resourceId);
Set<String> findResourceKeysByTenantIdResourceTypeAndKeyPrefix(TenantId tenantId, ResourceType resourceType, String keyPrefix);
}

View File

@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.Dao;
import java.util.List;
import java.util.Set;
public interface TbResourceInfoDao extends Dao<TbResourceInfo> {
@ -35,7 +36,7 @@ public interface TbResourceInfoDao extends Dao<TbResourceInfo> {
boolean existsByTenantIdAndResourceTypeAndResourceKey(TenantId tenantId, ResourceType resourceType, String resourceKey);
List<String> findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(TenantId tenantId, ResourceType resourceType, String resourceKeyQuery);
Set<String> findKeysByTenantIdAndResourceTypeAndResourceKeyPrefix(TenantId tenantId, ResourceType resourceType, String prefix);
List<TbResourceInfo> findByTenantIdAndEtagAndKeyStartingWith(TenantId tenantId, String etag, String query);

View File

@ -33,7 +33,6 @@ import org.thingsboard.server.dao.model.sql.AssetProfileEntity;
import org.thingsboard.server.dao.sql.JpaAbstractDao;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
@ -99,6 +98,11 @@ public class JpaAssetProfileDao extends JpaAbstractDao<AssetProfileEntity, Asset
return DaoUtil.getData(assetProfileRepository.findByTenantIdAndName(tenantId.getId(), profileName));
}
@Override
public PageData<AssetProfile> findAll(PageLink pageLink) {
return DaoUtil.toPageData(assetProfileRepository.findAll(DaoUtil.toPageable(pageLink)));
}
@Override
public AssetProfile findByTenantIdAndExternalId(UUID tenantId, UUID externalId) {
return DaoUtil.getData(assetProfileRepository.findByTenantIdAndExternalId(tenantId, externalId));

View File

@ -25,7 +25,6 @@ import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.asset.AssetProfileInfo;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
@ -37,7 +36,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
@ -118,6 +116,11 @@ public class JpaDeviceProfileDao extends JpaAbstractDao<DeviceProfileEntity, Dev
return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndName(tenantId.getId(), profileName));
}
@Override
public PageData<DeviceProfile> findAll(PageLink pageLink) {
return DaoUtil.toPageData(deviceProfileRepository.findAll(DaoUtil.toPageable(pageLink)));
}
@Override
public DeviceProfile findByTenantIdAndExternalId(UUID tenantId, UUID externalId) {
return DaoUtil.getData(deviceProfileRepository.findByTenantIdAndExternalId(tenantId, externalId));

View File

@ -31,9 +31,7 @@ import org.thingsboard.server.dao.resource.TbResourceDao;
import org.thingsboard.server.dao.sql.JpaAbstractDao;
import org.thingsboard.server.dao.util.SqlDao;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@Slf4j
@ -127,11 +125,6 @@ public class JpaTbResourceDao extends JpaAbstractDao<TbResourceEntity, TbResourc
.map(TbResourceId::new));
}
@Override
public Set<String> findResourceKeysByTenantIdResourceTypeAndKeyPrefix(TenantId tenantId, ResourceType resourceType, String keyPrefix) {
return new HashSet<>(resourceRepository.findResourceKeys(tenantId.getId(), resourceType.name(), keyPrefix + "%"));
}
@Override
public TbResourceId getExternalIdByInternal(TbResourceId internalId) {
return DaoUtil.toEntityId(resourceRepository.getExternalIdByInternal(internalId.getId()), TbResourceId::new);

View File

@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sql.resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.ResourceType;
@ -97,8 +96,8 @@ public class JpaTbResourceInfoDao extends JpaAbstractDao<TbResourceInfoEntity, T
}
@Override
public List<String> findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(TenantId tenantId, ResourceType resourceType, String resourceKeyQuery) {
return resourceInfoRepository.findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(tenantId.getId(), resourceType.name(), resourceKeyQuery);
public Set<String> findKeysByTenantIdAndResourceTypeAndResourceKeyPrefix(TenantId tenantId, ResourceType resourceType, String prefix) {
return resourceInfoRepository.findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(tenantId.getId(), resourceType.name(), prefix);
}
@Override

View File

@ -23,6 +23,7 @@ import org.springframework.data.repository.query.Param;
import org.thingsboard.server.dao.model.sql.TbResourceInfoEntity;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public interface TbResourceInfoRepository extends JpaRepository<TbResourceInfoEntity, UUID> {
@ -57,10 +58,10 @@ public interface TbResourceInfoRepository extends JpaRepository<TbResourceInfoEn
boolean existsByTenantIdAndResourceTypeAndResourceKey(UUID tenantId, String resourceType, String resourceKey);
@Query(value = "SELECT r.resource_key FROM resource r WHERE r.tenant_id = :tenantId AND r.resource_type = :resourceType " +
"AND starts_with(r.resource_key, :resourceKeyStartsWith)", nativeQuery = true)
List<String> findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(@Param("tenantId") UUID tenantId,
@Param("resourceType") String resourceType,
@Param("resourceKeyStartsWith") String resourceKeyStartsWith);
"AND starts_with(r.resource_key, :prefix)", nativeQuery = true)
Set<String> findKeysByTenantIdAndResourceTypeAndResourceKeyStartingWith(@Param("tenantId") UUID tenantId,
@Param("resourceType") String resourceType,
@Param("prefix") String prefix);
List<TbResourceInfoEntity> findByTenantIdAndEtagAndResourceKeyStartingWith(UUID tenantId, String etag, String query);

View File

@ -26,7 +26,7 @@ import org.thingsboard.server.dao.model.sql.TbResourceEntity;
import java.util.List;
import java.util.UUID;
public interface TbResourceRepository extends JpaRepository<TbResourceEntity, UUID> , ExportableEntityRepository<TbResourceEntity> {
public interface TbResourceRepository extends JpaRepository<TbResourceEntity, UUID>, ExportableEntityRepository<TbResourceEntity> {
TbResourceEntity findByTenantIdAndResourceTypeAndResourceKey(UUID tenantId, String resourceType, String resourceKey);
@ -94,8 +94,4 @@ public interface TbResourceRepository extends JpaRepository<TbResourceEntity, UU
@Query("SELECT id FROM TbResourceInfoEntity WHERE tenantId = :tenantId")
Page<UUID> findIdsByTenantId(@Param("tenantId") UUID tenantId, Pageable pageable);
@Query("SELECT resourceKey FROM TbResourceInfoEntity WHERE tenantId = :tenantId AND resourceType = :resourceType AND resourceKey LIKE :resourceKeyPrefix ")
List<String> findResourceKeys(@Param("tenantId")UUID tenantId,
@Param("resourceType") String resourceType,
@Param("resourceKeyPrefix") String resourceKeyPrefix);
}

View File

@ -226,11 +226,6 @@ public class WidgetTypeServiceImpl implements WidgetTypeService {
tenantWidgetTypeRemover.removeEntities(tenantId, tenantId);
}
@Override
public PageData<WidgetTypeId> findAllWidgetTypesIds(PageLink pageLink) {
return widgetTypeDao.findAllWidgetTypesIds(pageLink);
}
@Override
public Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) {
return Optional.ofNullable(findWidgetTypeById(tenantId, new WidgetTypeId(entityId.getId())));

View File

@ -75,7 +75,7 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService {
log.trace("Executing saveWidgetsBundle [{}]", widgetsBundle);
widgetsBundleValidator.validate(widgetsBundle, WidgetsBundle::getTenantId);
try {
imageService.replaceBase64WithImageUrl(widgetsBundle, widgetsBundle.getName(), "bundle");
imageService.replaceBase64WithImageUrl(widgetsBundle, "bundle");
WidgetsBundle result = widgetsBundleDao.save(widgetsBundle.getTenantId(), widgetsBundle);
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(result.getTenantId())
.entityId(result.getId()).added(widgetsBundle.getId() == null).build());
@ -172,11 +172,6 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService {
return widgetsBundles;
}
@Override
public PageData<WidgetsBundle> findAllWidgetsBundles(PageLink pageLink) {
return widgetsBundleDao.findAllWidgetsBundles(pageLink);
}
@Override
public void deleteWidgetsBundlesByTenantId(TenantId tenantId) {
log.trace("Executing deleteWidgetsBundlesByTenantId, tenantId [{}]", tenantId);