Browse Source

Move resource url from id to url; add etag to export data

pull/11873/head
ViacheslavKlimov 2 years ago
parent
commit
155b22accf
  1. 2
      application/src/main/java/org/thingsboard/server/controller/ImageController.java
  2. 17
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java
  3. 17
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  4. 2
      application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java
  5. 4
      application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java
  6. 4
      application/src/test/java/org/thingsboard/server/service/entitiy/dashboard/DashboardSyncServiceTest.java
  7. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java
  8. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java
  9. 1
      common/data/src/main/java/org/thingsboard/server/common/data/ResourceExportData.java
  10. 22
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java
  11. 2
      common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java
  12. 27
      common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java
  13. 78
      dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java
  14. 68
      dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java

2
application/src/main/java/org/thingsboard/server/controller/ImageController.java

@ -197,7 +197,7 @@ public class ImageController extends BaseController {
@Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key) throws Exception {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ);
return tbImageService.exportImage(imageInfo);
return imageService.exportImage(imageInfo);
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")

17
application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java

@ -186,23 +186,6 @@ public class DefaultTbImageService extends AbstractTbEntityService implements Tb
}
}
@Override
public ResourceExportData exportImage(TbResourceInfo imageInfo) {
ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class);
byte[] data = imageService.getImageData(imageInfo.getTenantId(), imageInfo.getId());
return ResourceExportData.builder()
.mediaType(descriptor.getMediaType())
.fileName(imageInfo.getFileName())
.title(imageInfo.getTitle())
.type(ResourceType.IMAGE)
.subType(imageInfo.getResourceSubType())
.resourceKey(imageInfo.getResourceKey())
.isPublic(imageInfo.isPublic())
.publicResourceKey(imageInfo.getPublicResourceKey())
.data(Base64.getEncoder().encodeToString(data))
.build();
}
@Override
public TbResourceInfo importImage(ResourceExportData imageData, boolean checkExisting, SecurityUser user) throws Exception {
TbResource image = new TbResource();

17
application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java

@ -172,29 +172,16 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
return resources.values().stream()
.map(resourceInfo -> {
if (resourceInfo.getResourceType() == ResourceType.IMAGE) {
ResourceExportData imageExportData = tbImageService.exportImage(resourceInfo);
ResourceExportData imageExportData = imageService.exportImage(resourceInfo);
imageExportData.setResourceKey(null); // so that the image is not updated by resource key on import
return imageExportData;
} else {
return exportResource(resourceInfo);
return resourceService.exportResource(resourceInfo);
}
})
.toList();
}
private ResourceExportData exportResource(TbResourceInfo resourceInfo) {
byte[] data = resourceService.getResourceData(resourceInfo.getTenantId(), resourceInfo.getId());
return ResourceExportData.builder()
.mediaType(resourceInfo.getResourceType().getMediaType())
.fileName(resourceInfo.getFileName())
.title(resourceInfo.getTitle())
.type(resourceInfo.getResourceType())
.subType(resourceInfo.getResourceSubType())
.resourceKey(resourceInfo.getResourceKey())
.data(Base64.getEncoder().encodeToString(data))
.build();
}
private TbResourceInfo importResource(ResourceExportData exportData, boolean checkExisting, SecurityUser user) throws ThingsboardException {
if (exportData.getType() == ResourceType.IMAGE || exportData.getSubType() == ResourceSubType.IMAGE
|| exportData.getSubType() == ResourceSubType.SCADA_SYMBOL) {

2
application/src/main/java/org/thingsboard/server/service/resource/TbImageService.java

@ -37,8 +37,6 @@ public interface TbImageService {
void evictETags(ImageCacheKey imageCacheKey);
ResourceExportData exportImage(TbResourceInfo imageInfo);
TbResourceInfo importImage(ResourceExportData imageData, boolean checkExisting, SecurityUser user) throws Exception;
}

4
application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java

@ -618,7 +618,7 @@ public class DashboardControllerTest extends AbstractControllerTest {
+ Base64.getEncoder().encodeToString(imageInfo.getResourceSubType().name().getBytes()) + ":"
+ imageInfo.getEtag() + ";data:image/png;base64,");
String resourceRef = exportedDashboard.getConfiguration().get("widgets").get("xxx").get("config")
.get("actions").get("elementClick").get(0).get("customResources").get(0).get("url").get("id").asText();
.get("actions").get("elementClick").get(0).get("customResources").get(0).get("url").asText();
assertThat(resourceRef).isEqualTo("tb-resource:" + Base64.getEncoder().encodeToString(resourceInfo.getResourceType().name().getBytes())
+ ":" + resourceInfo.getEtag());
@ -627,10 +627,12 @@ public class DashboardControllerTest extends AbstractControllerTest {
assertThat(resources.get(ResourceType.IMAGE)).singleElement().satisfies(exportedImage -> {
assertThat(exportedImage.getFileName()).isEqualTo(imageInfo.getResourceKey());
assertThat(exportedImage.getData()).isEqualTo(Base64.getEncoder().encodeToString(ImageControllerTest.PNG_IMAGE));
assertThat(exportedImage.getEtag()).isEqualTo(imageInfo.getEtag());
});
assertThat(resources.get(ResourceType.JS_MODULE)).singleElement().satisfies(exportedJsModule -> {
assertThat(exportedJsModule.getFileName()).isEqualTo(resource.getResourceKey());
assertThat(exportedJsModule.getData()).isEqualTo(Base64.getEncoder().encodeToString(resourceData));
assertThat(exportedJsModule.getEtag()).isEqualTo(resourceInfo.getEtag());
});
doDelete("/api/dashboard/" + dashboard.getId()).andExpect(status().isOk());

4
application/src/test/java/org/thingsboard/server/service/entitiy/dashboard/DashboardSyncServiceTest.java

@ -65,6 +65,10 @@ public class DashboardSyncServiceTest extends AbstractControllerTest {
*
* TODO:
* update system widgets with the new structure
*
* TODO CONSIDER
* leave image/resource link as is. when importing - keep track of imported resources (if the resource key changed) (map of resourceKey -> importedResourceKey).
* then resolve and update resource links if resource key is new (idx added)
* */
// FIXME: need to update resource key all the time???? because etag is changed. same for images....

3
common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.resource;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.HasImage;
import org.thingsboard.server.common.data.ResourceExportData;
import org.thingsboard.server.common.data.ResourceSubType;
import org.thingsboard.server.common.data.TbImageDeleteResult;
import org.thingsboard.server.common.data.TbResource;
@ -47,6 +48,8 @@ public interface ImageService {
byte[] getImagePreview(TenantId tenantId, TbResourceId imageId);
ResourceExportData exportImage(TbResourceInfo imageInfo);
TbImageDeleteResult deleteImage(TbResourceInfo imageInfo, boolean force);
String calculateImageEtag(byte[] imageData);

3
common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.resource;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.ResourceExportData;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
@ -42,6 +43,8 @@ public interface ResourceService extends EntityDaoService {
byte[] getResourceData(TenantId tenantId, TbResourceId resourceId);
ResourceExportData exportResource(TbResourceInfo resourceInfo);
TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId);
TbResourceInfo findResourceInfoByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey);

1
common/data/src/main/java/org/thingsboard/server/common/data/ResourceExportData.java

@ -32,6 +32,7 @@ import lombok.extern.slf4j.Slf4j;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResourceExportData {
private String etag;
private String title;
private ResourceType type;
private ResourceSubType subType;

22
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java

@ -15,11 +15,16 @@
*/
package org.thingsboard.server.common.data.widget;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.thingsboard.server.common.data.id.WidgetTypeId;
import java.util.Optional;
@Data
public class WidgetType extends BaseWidgetType {
@ -43,4 +48,21 @@ public class WidgetType extends BaseWidgetType {
this.descriptor = widgetType.getDescriptor();
}
@JsonIgnore
public JsonNode getDefaultConfig() {
return Optional.ofNullable(descriptor.get("defaultConfig"))
.filter(JsonNode::isTextual).map(JsonNode::asText)
.map(json -> {
try {
return mapper.readTree(json);
} catch (JsonProcessingException e) {
return null;
}
}).orElse(null);
}
public void setDefaultConfig(JsonNode defaultConfig) {
((ObjectNode) descriptor).put("defaultConfig", defaultConfig.toString());
}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetTypeDetails.java

@ -33,7 +33,7 @@ import java.util.List;
@Data
@EqualsAndHashCode(callSuper = true)
@JsonPropertyOrder({"fqn", "name", "deprecated", "image", "description", "descriptor", "externalId"})
@JsonPropertyOrder({"fqn", "name", "deprecated", "image", "description", "descriptor", "externalId", "resources"})
public class WidgetTypeDetails extends WidgetType implements HasName, HasTenantId, HasImage, ExportableEntity<WidgetTypeId> {
@Schema(description = "Relative or external image URL. Replaced with image data URL (Base64) in case of relative URL and 'inlineImages' option enabled.")

27
common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java

@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
@ -469,6 +470,22 @@ public class JacksonUtil {
}
public static void replaceAllByMapping(JsonNode jsonNode, Map<String, String> mapping, Map<String, String> templateParams, BiFunction<String, String, String> processor) {
replaceByMapping(jsonNode, mapping, templateParams, (name, value) -> {
if (value.isTextual()) {
return new TextNode(processor.apply(name, value.asText()));
} 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, processor.apply(arrayElementName, array.get(i).asText()));
}
return array;
}
return value;
});
}
public static void replaceByMapping(JsonNode jsonNode, Map<String, String> mapping, Map<String, String> templateParams, BiFunction<String, JsonNode, JsonNode> processor) {
for (var entry : mapping.entrySet()) {
String expression = entry.getValue();
Queue<JsonPathProcessingTask> tasks = new LinkedList<>();
@ -515,15 +532,7 @@ public class JacksonUtil {
for (var replacement : task.getVariables().entrySet()) {
name = name.replace("$" + replacement.getKey(), Strings.nullToEmpty(replacement.getValue()));
}
if (node.isObject() && value.isTextual()) {
((ObjectNode) node).put(token, processor.apply(name, value.asText()));
} 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, processor.apply(arrayElementName, array.get(i).asText()));
}
}
((ObjectNode) node).set(token, processor.apply(name, value));
} else {
if (StringUtils.isNotEmpty(variableName)) {
tasks.add(task.next(value, variableName, variableValue));

78
dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java

@ -33,6 +33,7 @@ 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.ResourceExportData;
import org.thingsboard.server.common.data.ResourceSubType;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbImageDeleteResult;
@ -62,7 +63,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
@ -75,7 +75,6 @@ import static org.apache.commons.lang3.ArrayUtils.get;
public class BaseImageService extends BaseResourceService implements ImageService {
private static final int MAX_ENTITIES_TO_FIND = 10;
private static final String DEFAULT_CONFIG_TAG = "defaultConfig";
public static Map<String, String> DASHBOARD_BASE64_MAPPING = new HashMap<>();
public static Map<String, String> WIDGET_TYPE_BASE64_MAPPING = new HashMap<>();
@ -232,6 +231,24 @@ public class BaseImageService extends BaseResourceService implements ImageServic
return resourceDao.getResourcePreview(tenantId, imageId);
}
@Override
public ResourceExportData exportImage(TbResourceInfo imageInfo) {
ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class);
byte[] data = getImageData(imageInfo.getTenantId(), imageInfo.getId());
return ResourceExportData.builder()
.mediaType(descriptor.getMediaType())
.fileName(imageInfo.getFileName())
.title(imageInfo.getTitle())
.type(ResourceType.IMAGE)
.subType(imageInfo.getResourceSubType())
.resourceKey(imageInfo.getResourceKey())
.isPublic(imageInfo.isPublic())
.publicResourceKey(imageInfo.getPublicResourceKey())
.data(Base64.getEncoder().encodeToString(data))
.etag(descriptor.getEtag())
.build();
}
@Override
public TbImageDeleteResult deleteImage(TbResourceInfo imageInfo, boolean force) {
var tenantId = imageInfo.getTenantId();
@ -299,13 +316,10 @@ public class BaseImageService extends BaseResourceService implements ImageServic
entity.setImage(result.getValue());
boolean updated = result.isUpdated();
if (entity.getDescriptor().isObject()) {
ObjectNode descriptor = (ObjectNode) entity.getDescriptor();
JsonNode defaultConfig = Optional.ofNullable(descriptor.get(DEFAULT_CONFIG_TAG))
.filter(JsonNode::isTextual).map(JsonNode::asText)
.map(JacksonUtil::toJsonNode).orElse(null);
JsonNode defaultConfig = entity.getDefaultConfig();
if (defaultConfig != null) {
updated |= base64ToImageUrlUsingMapping(entity.getTenantId(), WIDGET_TYPE_BASE64_MAPPING, Collections.singletonMap("prefix", prefix), defaultConfig);
descriptor.put(DEFAULT_CONFIG_TAG, defaultConfig.toString());
entity.setDefaultConfig(defaultConfig);
}
}
updated |= base64ToImageUrlRecursively(entity.getTenantId(), prefix, entity.getDescriptor());
@ -341,7 +355,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic
return base64ToImageUrl(tenantId, name, data, false);
}
private static final Pattern TB_IMAGE_METADATA_PATTERN = Pattern.compile("^tb-image:([^;]+);data:(.*);.*");
public static final Pattern TB_IMAGE_METADATA_PATTERN = Pattern.compile("^tb-image:([^;]+);data:(.*);.*");
private UpdateResult base64ToImageUrl(TenantId tenantId, String name, String data, boolean strict) {
if (StringUtils.isBlank(data)) {
@ -352,7 +366,7 @@ public class BaseImageService extends BaseResourceService implements ImageServic
String resourceName = null;
String resourceSubType = null;
String etag = null;
String mediaType;
String mediaType = null;
var matcher = TB_IMAGE_METADATA_PATTERN.matcher(data);
if (matcher.matches()) {
String[] metadata = matcher.group(1).split(":");
@ -361,6 +375,8 @@ public class BaseImageService extends BaseResourceService implements ImageServic
resourceSubType = decode(get(metadata, 2));
etag = get(metadata, 3);
mediaType = matcher.group(2);
} else if (data.startsWith("tb-image:")) {
etag = StringUtils.substringAfter(data, "tb-image:");
} else if (data.startsWith(DataConstants.TB_IMAGE_PREFIX + "data:image/") || (!strict && data.startsWith("data:image/"))) {
mediaType = StringUtils.substringBetween(data, "data:", ";base64");
} else {
@ -370,6 +386,9 @@ public class BaseImageService extends BaseResourceService implements ImageServic
String base64Data = StringUtils.substringAfter(data, "base64,");
byte[] imageData = StringUtils.isNotEmpty(base64Data) ? Base64.getDecoder().decode(base64Data) : null;
if (StringUtils.isBlank(etag)) {
if (imageData == null) {
return UpdateResult.of(false, data);
}
etag = calculateEtag(imageData);
}
var imageInfo = findSystemOrTenantImageByEtag(tenantId, etag);
@ -454,14 +473,10 @@ public class BaseImageService extends BaseResourceService implements ImageServic
inlineImage(widgetTypeDetails, images);
ObjectNode descriptor = (ObjectNode) widgetTypeDetails.getDescriptor();
inlineIntoJson(widgetTypeDetails.getTenantId(), descriptor, images);
if (descriptor.has(DEFAULT_CONFIG_TAG) && descriptor.get(DEFAULT_CONFIG_TAG).isTextual()) {
try {
var defaultConfig = JacksonUtil.toJsonNode(descriptor.get(DEFAULT_CONFIG_TAG).asText());
inlineIntoJson(widgetTypeDetails.getTenantId(), defaultConfig, images);
descriptor.put(DEFAULT_CONFIG_TAG, JacksonUtil.toString(defaultConfig));
} catch (Exception e) {
log.debug("[{}][{}] Failed to process default config: ", widgetTypeDetails.getTenantId(), widgetTypeDetails.getId(), e);
}
JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig();
if (defaultConfig != null) {
inlineIntoJson(widgetTypeDetails.getTenantId(), defaultConfig, images);
widgetTypeDetails.setDefaultConfig(defaultConfig);
}
return images;
}
@ -501,28 +516,25 @@ public class BaseImageService extends BaseResourceService implements ImageServic
private String inlineImage(TenantId tenantId, String path, String url, boolean addTbImagePrefix, List<TbResourceInfo> processedImages) {
return inlineImage(tenantId, path, url, (key, imageInfo) -> {
String tbImagePrefix = "";
boolean addData = true;
ImageDescriptor descriptor = getImageDescriptor(imageInfo, key.isPreview());
String value = "";
if (addTbImagePrefix) {
tbImagePrefix = "tb-image:" + encode(imageInfo.getResourceKey()) + ":"
+ encode(imageInfo.getName()) + ":"
+ encode(imageInfo.getResourceSubType().name()) + ":"
+ imageInfo.getEtag() + ";";
value = "tb-image:";
if (processedImages != null && !key.isPreview()) {
addData = false;
if (processedImages != null && !key.isPreview()) { // images are stored separately
processedImages.add(imageInfo);
value += descriptor.getEtag();
return value;
}
}
byte[] data;
if (addData) {
data = key.isPreview() ? getImagePreview(tenantId, imageInfo.getId()) : getImageData(tenantId, imageInfo.getId());
} else {
data = null;
value += encode(imageInfo.getResourceKey()) + ":"
+ encode(imageInfo.getName()) + ":"
+ encode(imageInfo.getResourceSubType().name()) + ":"
+ imageInfo.getEtag() + ";";
}
ImageDescriptor descriptor = getImageDescriptor(imageInfo, key.isPreview());
return tbImagePrefix + "data:" + descriptor.getMediaType() + ";base64," + encode(data);
byte[] data = key.isPreview() ? getImagePreview(tenantId, imageInfo.getId()) : getImageData(tenantId, imageInfo.getId());
return value + "data:" + descriptor.getMediaType() + ";base64," + encode(data);
});
}

68
dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.resource;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.google.common.hash.Hashing;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.RequiredArgsConstructor;
@ -33,6 +34,7 @@ import org.thingsboard.server.cache.resourceInfo.ResourceInfoEvictEvent;
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.ResourceExportData;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
@ -81,11 +83,13 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
private ImageService imageService;
private static final Map<String, String> DASHBOARD_RESOURCES_MAPPING = Map.of(
"widgets.*.config.actions.*.*.customResources.*.url.id", ""
"widgets.*.config.actions.*.*.customResources.*.url", ""
);
private static final Map<String, String> WIDGET_RESOURCES_MAPPING = Map.of(
"resources.*.url.id", ""
"resources.*.url", ""
);
private static final Map<String, String> WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING = Map.of(
"actions.*.*.customResources.*.url", ""
);
@Override
@ -180,6 +184,21 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
return resourceDao.getResourceData(tenantId, resourceId);
}
@Override
public ResourceExportData exportResource(TbResourceInfo resourceInfo) {
byte[] data = getResourceData(resourceInfo.getTenantId(), resourceInfo.getId());
return ResourceExportData.builder()
.mediaType(resourceInfo.getResourceType().getMediaType())
.fileName(resourceInfo.getFileName())
.title(resourceInfo.getTitle())
.type(resourceInfo.getResourceType())
.subType(resourceInfo.getResourceSubType())
.resourceKey(resourceInfo.getResourceKey())
.data(Base64.getEncoder().encodeToString(data))
.etag(resourceInfo.getEtag())
.build();
}
@Override
public TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId) {
log.trace("Executing findResourceInfoById [{}] [{}]", tenantId, resourceId);
@ -298,7 +317,13 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
@Override
public boolean replaceResourcesUsageWithUrls(WidgetTypeDetails widgetTypeDetails) {
return replaceResourcesUsageWithUrls(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING);
boolean updated = replaceResourcesUsageWithUrls(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING);
JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig();
if (defaultConfig != null) {
updated |= replaceResourcesUsageWithUrls(widgetTypeDetails.getTenantId(), defaultConfig, WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING);
widgetTypeDetails.setDefaultConfig(defaultConfig);
}
return updated;
}
private boolean replaceResourcesUsageWithUrls(TenantId tenantId, JsonNode jsonNode, Map<String, String> mapping) {
@ -314,7 +339,7 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
if (metadata.length < 2) {
return value;
}
ResourceType resourceType = ResourceType.valueOf(decode(metadata[0]));
ResourceType resourceType = ResourceType.valueOf(metadata[0]);
String etag = metadata[1];
resourceInfo = findSystemOrTenantResourceByEtag(tenantId, resourceType, etag);
@ -350,7 +375,13 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
@Override
public List<TbResourceInfo> replaceResourcesUrlsWithTags(WidgetTypeDetails widgetTypeDetails) {
return replaceResourcesUrlsWithTags(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING);
List<TbResourceInfo> resources = replaceResourcesUrlsWithTags(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING);
JsonNode defaultConfig = widgetTypeDetails.getDefaultConfig();
if (defaultConfig != null) {
resources.addAll(replaceResourcesUrlsWithTags(widgetTypeDetails.getTenantId(), defaultConfig, WIDGET_DEFAULT_CONFIG_RESOURCES_MAPPING));
widgetTypeDetails.setDefaultConfig(defaultConfig);
}
return resources;
}
private List<TbResourceInfo> replaceResourcesUrlsWithTags(TenantId tenantId, JsonNode jsonNode, Map<String, String> mapping) {
@ -377,7 +408,7 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
TbResourceInfo resourceInfo = findResourceInfoByTenantIdAndKey(resourceTenantId, resourceType, resourceKey);
if (resourceInfo != null) {
resources.add(resourceInfo);
return "tb-resource:" + String.join(":", encode(resourceType.name()), resourceInfo.getEtag());
return "tb-resource:" + String.join(":", resourceType.name(), resourceInfo.getEtag());
} else {
return value;
}
@ -386,12 +417,25 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
}
private void processResources(JsonNode jsonNode, Map<String, String> mapping, UnaryOperator<String> processor) {
JacksonUtil.replaceAllByMapping(jsonNode, mapping, Collections.emptyMap(), (name, value) -> {
if (StringUtils.isBlank(value)) {
return value;
JacksonUtil.replaceByMapping(jsonNode, mapping, Collections.emptyMap(), (name, urlNode) -> {
String value = null;
if (urlNode.isTextual()) { // link is in the right place
value = urlNode.asText();
} else {
JsonNode id = urlNode.get("id"); // old structure is used
if (id != null && id.isTextual()) {
value = id.asText();
}
}
String newValue = processor.apply(value);
log.trace("Replaced '{}' with '{}'", value, newValue);
if (StringUtils.isNotBlank(value)) {
value = processor.apply(value);
} else {
value = "";
}
JsonNode newValue = new TextNode(value);
log.trace("Replaced '{}' with '{}'", urlNode, newValue);
return newValue;
});
}

Loading…
Cancel
Save