16 changed files with 825 additions and 17 deletions
@ -0,0 +1,180 @@ |
|||||
|
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.RequiredArgsConstructor; |
||||
|
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.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.util.MediaTypeUtils; |
||||
|
import org.thingsboard.server.common.data.widget.WidgetTypeDetails; |
||||
|
import org.thingsboard.server.common.data.widget.WidgetsBundle; |
||||
|
import org.thingsboard.server.dao.resource.ResourceService; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Optional; |
||||
|
|
||||
|
@Component |
||||
|
//@Profile("install")
|
||||
|
@Slf4j |
||||
|
@RequiredArgsConstructor |
||||
|
public class ImagesUpdater { |
||||
|
|
||||
|
private final ResourceService resourceService; |
||||
|
|
||||
|
|
||||
|
// TODO: solution templates
|
||||
|
|
||||
|
|
||||
|
public void updateWidgetImages(WidgetTypeDetails widgetTypeDetails) { |
||||
|
String imageName = widgetTypeDetails.getName() + " - image"; |
||||
|
String imageKey = widgetTypeDetails.getFqn(); |
||||
|
String imageLink = saveImage(imageName, imageKey, widgetTypeDetails.getImage()); |
||||
|
widgetTypeDetails.setImage(imageLink); |
||||
|
|
||||
|
JsonNode defaultConfig = JacksonUtil.toJsonNode(widgetTypeDetails.getDescriptor().get("defaultConfig").asText()); |
||||
|
defaultConfig = updateWidgetConfig(TenantId.SYS_TENANT_ID, defaultConfig, imageName, imageKey, imageKey); |
||||
|
((ObjectNode) widgetTypeDetails.getDescriptor()).put("defaultConfig", defaultConfig.toString()); |
||||
|
} |
||||
|
|
||||
|
public void updateWidgetsBundleImages(WidgetsBundle widgetsBundle) { |
||||
|
String imageLink = saveImage(widgetsBundle.getTitle(), widgetsBundle.getAlias(), widgetsBundle.getImage()); |
||||
|
widgetsBundle.setImage(imageLink); |
||||
|
} |
||||
|
|
||||
|
public boolean updateDashboardImages(Dashboard dashboard) { |
||||
|
String imageNamePrefix = dashboard.getTitle(); |
||||
|
String imageKeyPrefix = "dashboard_" + dashboard.getUuidId(); |
||||
|
boolean updated = false; |
||||
|
|
||||
|
String imageLink = saveImage(dashboard.getTenantId(), imageNamePrefix + " - image", imageKeyPrefix + ".image", dashboard.getImage(), null); |
||||
|
dashboard.setImage(imageLink); |
||||
|
|
||||
|
for (ObjectNode widgetConfig : dashboard.getWidgetsConfig()) { |
||||
|
String alias = widgetConfig.get("bundleAlias").asText() + "." + widgetConfig.get("typeAlias").asText(); |
||||
|
String widgetName = widgetConfig.get("config").get("title").asText(); |
||||
|
updateWidgetConfig(dashboard.getTenantId(), widgetConfig.get("config"), |
||||
|
imageNamePrefix + " - " + widgetName + " widget", |
||||
|
imageKeyPrefix + ".widget." + alias, alias); |
||||
|
} |
||||
|
|
||||
|
return updated; |
||||
|
} |
||||
|
|
||||
|
private JsonNode updateWidgetConfig(TenantId tenantId, JsonNode widgetConfig, |
||||
|
String imageNamePrefix, String imageKeyPrefix, |
||||
|
String fqn) { |
||||
|
ObjectNode widgetSettings = (ObjectNode) widgetConfig.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 " + (i + 1); |
||||
|
String imageKey = imageKeyPrefix + ".marker_image_" + (i + 1); |
||||
|
String imageLink = saveImage(tenantId, imageName, imageKey, markerImages.get(i).asText(), fqn + ".marker_image_"); |
||||
|
markerImages.set(i, imageLink); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
String mapImage = getText(widgetSettings, "mapImageUrl"); |
||||
|
if (mapImage != null) { |
||||
|
String imageName = imageNamePrefix + " - map image"; |
||||
|
String imageKeySuffix = ".map_image"; |
||||
|
String imageKey = imageKeyPrefix + imageKeySuffix; |
||||
|
String imageLink = saveImage(tenantId, imageName, imageKey, mapImage, fqn + imageKeySuffix); |
||||
|
widgetSettings.put("mapImageUrl", imageLink); |
||||
|
} |
||||
|
|
||||
|
String backgroundImage = getText(widgetSettings, "backgroundImageUrl"); |
||||
|
if (backgroundImage != null) { |
||||
|
String imageName = imageNamePrefix + " - background image"; |
||||
|
String imageKeySuffix = ".background_image"; |
||||
|
String imageKey = imageKeyPrefix + imageKeySuffix; |
||||
|
String imageLink = saveImage(tenantId, imageName, imageKey, backgroundImage, fqn + imageKeySuffix); |
||||
|
widgetSettings.put("backgroundImageUrl", imageLink); |
||||
|
} |
||||
|
|
||||
|
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"; |
||||
|
String imageKeySuffix = ".background_image"; |
||||
|
String imageKey = imageKeyPrefix + imageKeySuffix; |
||||
|
String imageLink = saveImage(tenantId, imageName, imageKey, imageBase64, fqn + imageKeySuffix); |
||||
|
backgroundConfig.set("imageBase64", null); |
||||
|
backgroundConfig.put("imageUrl", imageLink); |
||||
|
backgroundConfig.put("type", "imageUrl"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return widgetConfig; |
||||
|
} |
||||
|
|
||||
|
private String getText(JsonNode jsonNode, String field) { |
||||
|
return Optional.ofNullable(jsonNode.get(field)) |
||||
|
.filter(JsonNode::isTextual) |
||||
|
.map(JsonNode::asText).orElse(null); |
||||
|
} |
||||
|
|
||||
|
private String saveImage(String title, String key, String data) { |
||||
|
return saveImage(TenantId.SYS_TENANT_ID, title, key, data, null); |
||||
|
} |
||||
|
|
||||
|
private String saveImage(TenantId tenantId, String title, String key, String data, |
||||
|
String existingImageQuery) { |
||||
|
if (data == null) { |
||||
|
return null; |
||||
|
} |
||||
|
String base64Data = StringUtils.substringAfter(data, "base64,"); |
||||
|
if (base64Data.isEmpty()) { |
||||
|
return data; |
||||
|
} |
||||
|
String imageMediaType = StringUtils.substringBetween(data, "data:", ";base64"); |
||||
|
String extension = MediaTypeUtils.getFileExtension(imageMediaType); |
||||
|
key += "." + extension; |
||||
|
|
||||
|
TbResourceInfo resourceInfo = resourceService.findResourceInfoByTenantIdAndKey(tenantId, ResourceType.IMAGE, key); |
||||
|
if (resourceInfo == null && !tenantId.isSysTenantId() && existingImageQuery != null) { |
||||
|
List<TbResourceInfo> existing = resourceService.findByTenantIdAndDataAndKeyStartingWith(TenantId.SYS_TENANT_ID, base64Data, existingImageQuery); |
||||
|
if (!existing.isEmpty()) { |
||||
|
resourceInfo = existing.get(0); |
||||
|
if (existing.size() > 1) { |
||||
|
log.warn("Found more than one system image resources for key {}", existingImageQuery); |
||||
|
} |
||||
|
log.info("Using system image {} for {}", resourceInfo.getLink(), key); |
||||
|
return resourceInfo.getLink(); |
||||
|
} |
||||
|
} |
||||
|
TbResource resource; |
||||
|
if (resourceInfo == null) { |
||||
|
resource = new TbResource(); |
||||
|
resource.setTenantId(tenantId); |
||||
|
resource.setResourceType(ResourceType.IMAGE); |
||||
|
resource.setResourceKey(key); |
||||
|
} else if (tenantId.isSysTenantId()) { |
||||
|
resource = new TbResource(resourceInfo); |
||||
|
} else { |
||||
|
return resourceInfo.getLink(); |
||||
|
} |
||||
|
resource.setTitle(title); |
||||
|
resource.setFileName(key); |
||||
|
resource.setMediaType(imageMediaType); |
||||
|
resource.setBase64Data(base64Data); |
||||
|
resource = resourceService.saveResource(resource); |
||||
|
log.info("[{}] {} image '{}' {} ({})", tenantId, resourceInfo == null ? "Created" : "Updated", |
||||
|
resource.getTitle(), resource.getResourceKey(), resource.getLink()); |
||||
|
return resource.getLink(); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
package org.thingsboard.server.common.data.util; |
||||
|
|
||||
|
import lombok.AccessLevel; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
import org.springframework.util.MimeTypeUtils; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
|
||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE) |
||||
|
public class MediaTypeUtils { |
||||
|
|
||||
|
private static final Map<String, String> mappings = Map.of( |
||||
|
"jpeg", "jpg", |
||||
|
"svg+xml", "svg" |
||||
|
); |
||||
|
|
||||
|
public static String getFileExtension(String mimeType) { |
||||
|
String subtype = MimeTypeUtils.parseMimeType(mimeType).getSubtype(); |
||||
|
return mappings.getOrDefault(subtype, subtype); |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue