Browse Source

Refactoring for resources import

pull/11873/head
ViacheslavKlimov 2 years ago
parent
commit
2236304344
  1. 1
      application/src/main/data/resources/js_modules/gateway-management-extension.js
  2. 8
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  3. 30
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  4. 56
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  5. 126
      application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java
  6. 34
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbImageService.java
  7. 37
      application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
  8. 4
      application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java
  9. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ImageService.java
  10. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java
  11. 4
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  12. 36
      dao/src/main/java/org/thingsboard/server/dao/resource/BaseImageService.java
  13. 74
      dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java
  14. 4
      dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java

1
application/src/main/data/resources/js_modules/gateway-management-extension.js

File diff suppressed because one or more lines are too long

8
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -145,7 +145,7 @@ public class ThingsboardInstallService {
case "3.8.1":
log.info("Upgrading ThingsBoard from version 3.8.1 to 3.9.0 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.8.1");
dataUpdateService.updateData("3.8.1");
installScripts.updateResourcesUsage();
//TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache
break;
default:
@ -155,10 +155,9 @@ public class ThingsboardInstallService {
entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry);
log.info("Updating system data...");
dataUpdateService.upgradeRuleNodes();
installScripts.loadSystemResources();
systemDataLoaderService.loadSystemWidgets();
installScripts.loadSystemLwm2mResources();
installScripts.loadSystemImages();
installScripts.loadSystemImagesAndResources();
if (installScripts.isUpdateImages()) {
installScripts.updateImages();
}
@ -197,7 +196,6 @@ public class ThingsboardInstallService {
systemDataLoaderService.createDefaultTenantProfiles();
systemDataLoaderService.createAdminSettings();
systemDataLoaderService.createRandomJwtSettings();
installScripts.loadSystemResources();
systemDataLoaderService.loadSystemWidgets();
systemDataLoaderService.createOAuth2Templates();
systemDataLoaderService.createQueues();
@ -206,7 +204,7 @@ public class ThingsboardInstallService {
// systemDataLoaderService.loadSystemPlugins();
// systemDataLoaderService.loadSystemRules();
installScripts.loadSystemLwm2mResources();
installScripts.loadSystemImages();
installScripts.loadSystemImagesAndResources();
if (loadDemo) {
log.info("Loading demo data...");

30
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -49,7 +49,7 @@ import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.util.ImageUtils;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.service.install.update.ImagesUpdater;
import org.thingsboard.server.service.install.update.ResourcesUpdater;
import java.io.IOException;
import java.io.UncheckedIOException;
@ -119,7 +119,7 @@ public class InstallScripts {
private ResourceService resourceService;
@Autowired
private ImagesUpdater imagesUpdater;
private ResourcesUpdater resourcesUpdater;
@Getter @Setter
private boolean updateImages = false;
@ -393,27 +393,30 @@ public class InstallScripts {
}
public void updateImages() {
imagesUpdater.updateWidgetsBundlesImages();
imagesUpdater.updateWidgetTypesImages();
imagesUpdater.updateDashboardsImages();
imagesUpdater.updateDeviceProfilesImages();
imagesUpdater.updateAssetProfilesImages();
resourcesUpdater.updateWidgetsBundlesImages();
resourcesUpdater.updateWidgetTypesImages();
resourcesUpdater.updateDashboardsImages();
resourcesUpdater.updateDeviceProfilesImages();
resourcesUpdater.updateAssetProfilesImages();
}
public void loadSystemImages() {
log.info("Loading system images...");
public void loadSystemImagesAndResources() {
log.info("Loading system images and resources...");
Stream<Path> dashboardsFiles = Stream.concat(listDir(Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR)),
listDir(Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, DASHBOARDS_DIR)));
try (dashboardsFiles) {
dashboardsFiles.forEach(file -> {
try {
Dashboard dashboard = JacksonUtil.OBJECT_MAPPER.readValue(file.toFile(), Dashboard.class);
imagesUpdater.createSystemImages(dashboard);
resourcesUpdater.createSystemImagesAndResources(dashboard);
} catch (Exception e) {
log.error("Failed to create system images for default dashboard {}", file.getFileName(), e);
}
});
}
Path resourcesDir = Path.of(getDataDir(), RESOURCES_DIR);
loadSystemResources(resourcesDir.resolve("dashboards"), ResourceType.DASHBOARD);
}
public void loadDashboards(TenantId tenantId, CustomerId customerId) {
@ -504,10 +507,9 @@ public class InstallScripts {
}
}
public void loadSystemResources() {
Path resourcesDir = Path.of(getDataDir(), RESOURCES_DIR);
loadSystemResources(resourcesDir.resolve("js_modules"), ResourceType.JS_MODULE);
loadSystemResources(resourcesDir.resolve("dashboards"), ResourceType.DASHBOARD);
public void updateResourcesUsage() {
resourcesUpdater.updateDashboardsResources();
resourcesUpdater.updateWidgetsResources();
}
private void loadSystemResources(Path dir, ResourceType resourceType) {

56
application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java

@ -27,20 +27,12 @@ import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.Customer;
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.TbResourceInfo;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TbResourceId;
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.query.DynamicValue;
import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.widget.WidgetTypeDetails;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
@ -56,13 +48,8 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.component.RuleNodeClassInfo;
import org.thingsboard.server.utils.TbNodeUpgradeUtils;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
@ -122,9 +109,6 @@ public class DefaultDataUpdateService implements DataUpdateService {
updateMaxRuleNodeExecsPerMessage();
updateGatewayRateLimits();
break;
case "3.8.1":
updateResourcesUsage();
break;
default:
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
}
@ -372,46 +356,6 @@ public class DefaultDataUpdateService implements DataUpdateService {
return false;
}
private void updateResourcesUsage() {
log.info("Updating resources usage in dashboards");
var dashboards = new PageDataIterable<>(dashboardService::findAllDashboardsIds, 512);
int totalCount = 0;
int updatedCount = 0;
for (DashboardId dashboardId : dashboards) {
Dashboard dashboard = dashboardService.findDashboardById(TenantId.SYS_TENANT_ID, dashboardId);
boolean updated = resourceService.updateResourcesUsage(dashboard);
if (updated) {
dashboardService.saveDashboard(dashboard);
updatedCount++;
}
totalCount++;
if (totalCount % 1000 == 0) {
log.info("Processed {} dashboards, updated {}", totalCount, updatedCount);
}
}
log.info("Updated {} dashboards", updatedCount);
log.info("Updating resources usage in widgets");
totalCount = 0;
updatedCount = 0;
var widgets = new PageDataIterable<>(widgetTypeService::findAllWidgetTypesIds, 512);
for (WidgetTypeId widgetTypeId : widgets) {
WidgetTypeDetails widgetTypeDetails = widgetTypeService.findWidgetTypeDetailsById(TenantId.SYS_TENANT_ID, widgetTypeId);
boolean updated = resourceService.updateResourcesUsage(widgetTypeDetails);
if (updated) {
widgetTypeService.saveWidgetType(widgetTypeDetails);
updatedCount++;
}
totalCount++;
if (totalCount % 200 == 0) {
log.info("Processed {} widgets, updated {}", totalCount, updatedCount);
}
}
log.info("Updated {} widgets", updatedCount);
}
public static boolean getEnv(String name, boolean defaultValue) {
String env = System.getenv(name);
if (env == null) {

126
application/src/main/java/org/thingsboard/server/service/install/update/ImagesUpdater.java → application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java

@ -15,38 +15,62 @@
*/
package org.thingsboard.server.service.install.update;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Iterators;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
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.HasImage;
import org.thingsboard.server.common.data.ResourceExportData;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.WidgetTypeId;
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.widget.WidgetTypeDetails;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.asset.AssetProfileDao;
import org.thingsboard.server.dao.dashboard.DashboardDao;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceProfileDao;
import org.thingsboard.server.dao.resource.ImageService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.tenant.TenantDao;
import org.thingsboard.server.dao.widget.WidgetTypeDao;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleDao;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
@Component
@RequiredArgsConstructor
@Slf4j
public class ImagesUpdater {
public class ResourcesUpdater {
private final ImageService imageService;
private final ResourceService resourceService;
private final WidgetsBundleDao widgetsBundleDao;
private final WidgetTypeDao widgetTypeDao;
private final WidgetTypeService widgetTypeService;
private final TenantDao tenantDao;
private final DashboardDao dashboardDao;
private final DashboardService dashboardService;
private final DeviceProfileDao deviceProfileDao;
private final AssetProfileDao assetProfileDao;
@ -67,12 +91,102 @@ public class ImagesUpdater {
updateImages("dashboard", dashboardDao::findIdsByTenantId, imageService::updateImagesUsage, dashboardDao);
}
public void createSystemImages(Dashboard defaultDashboard) {
public void createSystemImagesAndResources(Dashboard defaultDashboard) {
defaultDashboard.setTenantId(TenantId.SYS_TENANT_ID);
// TODO: test! also update dashboards
boolean created = imageService.updateImagesUsage(defaultDashboard);
if (created) {
log.debug("Created system images for default dashboard '{}'", defaultDashboard.getTitle());
if (CollectionUtils.isNotEmpty(defaultDashboard.getResources())) {
resourceService.importResources(defaultDashboard.getTenantId(), defaultDashboard.getResources());
}
imageService.updateImagesUsage(defaultDashboard);
log.debug("Created/updated system images and resources for default dashboard '{}'", defaultDashboard.getTitle());
}
public void updateDashboardsResources() {
log.info("Updating resources usage in dashboards");
var dashboards = new PageDataIterable<>(dashboardService::findAllDashboardsIds, 512);
int totalCount = 0;
int updatedCount = 0;
for (DashboardId dashboardId : dashboards) {
Dashboard dashboard = dashboardService.findDashboardById(TenantId.SYS_TENANT_ID, dashboardId);
boolean updated = resourceService.updateResourcesUsage(dashboard); // will convert resources ids to new structure
if (updated) {
dashboardService.saveDashboard(dashboard);
updatedCount++;
}
totalCount++;
if (totalCount % 1000 == 0) {
log.info("Processed {} dashboards, updated {}", totalCount, updatedCount);
}
}
log.info("Updated {} dashboards", updatedCount);
}
public void updateWidgetsResources() {
log.info("Updating resources usage in widgets");
int totalCount = 0;
int updatedCount = 0;
var widgets = new PageDataIterable<>(widgetTypeService::findAllWidgetTypesIds, 512);
for (WidgetTypeId widgetTypeId : widgets) {
WidgetTypeDetails widgetTypeDetails = widgetTypeService.findWidgetTypeDetailsById(TenantId.SYS_TENANT_ID, widgetTypeId);
boolean updated = resourceService.updateResourcesUsage(widgetTypeDetails);
if (updated) {
widgetTypeService.saveWidgetType(widgetTypeDetails);
updatedCount++;
}
totalCount++;
if (totalCount % 200 == 0) {
log.info("Processed {} widgets, updated {}", totalCount, updatedCount);
}
}
log.info("Updated {} widgets", updatedCount);
}
public void updateFiles() {
Path widgetsDirectory = Path.of("/home/viacheslav/Desktop/thingsboard-ce/application/src/main/data/json/system/widget_types");
try {
Files.list(widgetsDirectory).forEach(path -> {
WidgetTypeDetails widgetTypeDetails = JacksonUtil.readValue(path.toFile(), WidgetTypeDetails.class);
widgetTypeDetails.setTenantId(TenantId.SYS_TENANT_ID);
imageService.updateImagesUsage(widgetTypeDetails);
resourceService.updateResourcesUsage(widgetTypeDetails);
Map<TbResourceId, TbResourceInfo> resources = new HashMap<>();
for (TbResourceInfo imageInfo : imageService.getUsedImages(widgetTypeDetails)) {
resources.putIfAbsent(imageInfo.getId(), imageInfo);
}
for (TbResourceInfo resourceInfo : resourceService.getUsedResources(widgetTypeDetails)) {
resources.putIfAbsent(resourceInfo.getId(), resourceInfo);
}
widgetTypeDetails.setResources(resources.values().stream()
.map(resourceInfo -> {
if (resourceInfo.getResourceType() == ResourceType.IMAGE) {
ResourceExportData imageExportData = imageService.exportImage(resourceInfo);
imageExportData.setResourceKey(null); // so that the image is not updated by resource key on import
return imageExportData;
} else {
return resourceService.exportResource(resourceInfo);
}
})
.toList());
ObjectNode json = (ObjectNode) JacksonUtil.valueToTree(widgetTypeDetails);
Iterators.removeIf(json.fields(), field -> field.getValue() == null
|| field.getValue().isNull());
json.remove(List.of("id", "createdTime", "tenantId", "externalId", "version", "scada"));
JsonNode resourcesNode = json.remove("resources");
json.set("resources", resourcesNode);
try {
Files.writeString(path, json.toPrettyString());
log.info("UPDATED {}: {} resources used", path, resources.size());
} catch (IOException e) {
throw new RuntimeException(e);
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}

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

@ -25,8 +25,6 @@ import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.EntityType;
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;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
@ -45,7 +43,6 @@ import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@ -192,34 +189,11 @@ public class DefaultTbImageService extends AbstractTbEntityService implements Tb
image.setTenantId(user.getTenantId());
accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.CREATE, null, image);
byte[] data = Base64.getDecoder().decode(imageData.getData());
if (checkExisting) {
String etag = imageService.calculateImageEtag(data);
TbResourceInfo existingImage = imageService.findSystemOrTenantImageByEtag(user.getTenantId(), etag);
if (existingImage != null) {
return existingImage;
}
}
image.setFileName(imageData.getFileName());
if (isNotEmpty(imageData.getTitle())) {
image.setTitle(imageData.getTitle());
} else {
image.setTitle(imageData.getFileName());
}
if (imageData.getSubType() != null) {
image.setResourceSubType(imageData.getSubType());
} else {
image.setResourceSubType(ResourceSubType.IMAGE);
image = imageService.toImage(user.getTenantId(), imageData, checkExisting);
if (checkExisting && image.getId() != null) {
accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.READ, image.getId(), image);
return image;
}
image.setResourceType(ResourceType.IMAGE);
image.setResourceKey(imageData.getResourceKey());
image.setPublic(imageData.isPublic());
image.setPublicResourceKey(imageData.getPublicResourceKey());
ImageDescriptor descriptor = new ImageDescriptor();
descriptor.setMediaType(imageData.getMediaType());
image.setDescriptorValue(descriptor);
image.setData(data);
return save(image, user);
}

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

@ -21,7 +21,6 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.EntityType;
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.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
@ -44,7 +43,6 @@ import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
@ -52,7 +50,6 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.thingsboard.server.common.data.StringUtils.isNotEmpty;
import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.utils.LwM2mObjectModelUtils.toLwM2mObject;
@ -146,11 +143,15 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
@Override
public void importResources(List<ResourceExportData> resources, SecurityUser user) throws Exception {
for (ResourceExportData resourceData : resources) {
TbResource resource = new TbResource();
resource.setTenantId(user.getTenantId());
accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.CREATE, null, resource);
TbResourceInfo resourceInfo;
if (resourceData.getType() == ResourceType.IMAGE) {
resourceInfo = tbImageService.importImage(resourceData, true, user);
} else {
resourceInfo = importResource(resourceData, true, user);
resourceInfo = importResource(resourceData, user);
}
resourceData.setNewLink(resourceInfo.getLink());
}
@ -181,34 +182,16 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
.toList();
}
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) {
throw new IllegalArgumentException("Image import not supported");
}
private TbResourceInfo importResource(ResourceExportData resourceData, SecurityUser user) throws ThingsboardException {
TbResource resource = new TbResource();
resource.setTenantId(user.getTenantId());
accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.CREATE, null, resource);
byte[] data = Base64.getDecoder().decode(exportData.getData());
if (checkExisting) {
String etag = resourceService.calculateEtag(data);
TbResourceInfo existingResource = resourceService.findSystemOrTenantResourceByEtag(user.getTenantId(), exportData.getType(), etag);
if (existingResource != null) {
return existingResource;
}
}
resource.setFileName(exportData.getFileName());
if (isNotEmpty(exportData.getTitle())) {
resource.setTitle(exportData.getTitle());
} else {
resource.setTitle(exportData.getFileName());
TbResourceInfo resourceInfo = resourceService.toResource(user.getTenantId(), resourceData);
if (resourceInfo.getId() != null) {
accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.READ, resourceInfo.getId(), resourceInfo);
return resourceInfo;
}
resource.setResourceSubType(exportData.getSubType());
resource.setResourceType(exportData.getType());
resource.setResourceKey(exportData.getResourceKey());
resource.setData(data);
return save(resource, user);
}

4
application/src/test/java/org/thingsboard/server/service/install/InstallScriptsTest.java

@ -37,7 +37,7 @@ import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.usagerecord.ApiLimitService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.service.install.update.ImagesUpdater;
import org.thingsboard.server.service.install.update.ResourcesUpdater;
import java.nio.file.Path;
import java.util.Collections;
@ -67,7 +67,7 @@ class InstallScriptsTest {
@MockBean
ImageService imageService;
@MockBean
ImagesUpdater imagesUpdater;
ResourcesUpdater resourcesUpdater;
@SpyBean
InstallScripts installScripts;

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

@ -50,6 +50,8 @@ public interface ImageService {
ResourceExportData exportImage(TbResourceInfo imageInfo);
TbResource toImage(TenantId tenantId, ResourceExportData imageData, boolean checkExisting);
TbImageDeleteResult deleteImage(TbResourceInfo imageInfo, boolean force);
String calculateImageEtag(byte[] imageData);

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

@ -45,6 +45,10 @@ public interface ResourceService extends EntityDaoService {
ResourceExportData exportResource(TbResourceInfo resourceInfo);
TbResource toResource(TenantId tenantId, ResourceExportData exportData);
void importResources(TenantId tenantId, List<ResourceExportData> resources);
TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId);
TbResourceInfo findResourceInfoByTenantIdAndKey(TenantId tenantId, ResourceType resourceType, String resourceKey);

4
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.dashboard;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@ -161,6 +162,9 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
dashboardValidator.validate(dashboard, DashboardInfo::getTenantId);
}
try {
if (CollectionUtils.isNotEmpty(dashboard.getResources())) {
resourceService.importResources(dashboard.getTenantId(), dashboard.getResources());
}
imageService.updateImagesUsage(dashboard);
resourceService.updateResourcesUsage(dashboard);

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

@ -68,6 +68,7 @@ import java.util.function.BiFunction;
import java.util.regex.Pattern;
import static org.apache.commons.lang3.ArrayUtils.get;
import static org.thingsboard.server.common.data.StringUtils.isNotEmpty;
@Service
@Slf4j
@ -248,6 +249,41 @@ public class BaseImageService extends BaseResourceService implements ImageServic
.build();
}
@Override
public TbResource toImage(TenantId tenantId, ResourceExportData imageData, boolean checkExisting) {
byte[] data = Base64.getDecoder().decode(imageData.getData());
if (checkExisting) {
String etag = calculateImageEtag(data);
TbResourceInfo existingImage = findSystemOrTenantImageByEtag(tenantId, etag);
if (existingImage != null) {
return new TbResource(existingImage);
}
}
TbResource image = new TbResource();
image.setTenantId(tenantId);
image.setFileName(imageData.getFileName());
if (isNotEmpty(imageData.getTitle())) {
image.setTitle(imageData.getTitle());
} else {
image.setTitle(imageData.getFileName());
}
if (imageData.getSubType() != null) {
image.setResourceSubType(imageData.getSubType());
} else {
image.setResourceSubType(ResourceSubType.IMAGE);
}
image.setResourceType(ResourceType.IMAGE);
image.setResourceKey(imageData.getResourceKey());
image.setPublic(imageData.isPublic());
image.setPublicResourceKey(imageData.getPublicResourceKey());
ImageDescriptor descriptor = new ImageDescriptor();
descriptor.setMediaType(imageData.getMediaType());
image.setDescriptorValue(descriptor);
image.setData(data);
return image;
}
@Override
public TbImageDeleteResult deleteImage(TbResourceInfo imageInfo, boolean force) {
var tenantId = imageInfo.getTenantId();

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

@ -36,6 +36,7 @@ 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.ResourceSubType;
import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo;
@ -68,6 +69,7 @@ import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.UnaryOperator;
import static org.thingsboard.server.common.data.StringUtils.isNotEmpty;
import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID;
import static org.thingsboard.server.dao.service.Validator.validateId;
@ -201,6 +203,74 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
.build();
}
@Override
public void importResources(TenantId tenantId, List<ResourceExportData> resources) {
for (ResourceExportData resourceData : resources) {
if (resourceData.getNewLink() != null) {
continue; // already imported
}
TbResource resource;
if (resourceData.getType() == ResourceType.IMAGE) {
resource = imageService.toImage(tenantId, resourceData, true);
if (resource.getData() != null) {
imageService.saveImage(resource);
}
} else {
resource = toResource(tenantId, resourceData);
if (resource.getData() != null) {
saveResource(resource);
}
}
resourceData.setNewLink(resource.getLink());
}
}
@Override
public TbResource toResource(TenantId tenantId, ResourceExportData exportData) {
if (exportData.getType() == ResourceType.IMAGE || exportData.getSubType() == ResourceSubType.IMAGE
|| exportData.getSubType() == ResourceSubType.SCADA_SYMBOL) {
throw new IllegalArgumentException("Image import not supported");
}
byte[] data = Base64.getDecoder().decode(exportData.getData());
String etag = calculateEtag(data);
TbResourceInfo existingResource;
boolean updateData = false;
if (!tenantId.isSysTenantId()) {
existingResource = findSystemOrTenantResourceByEtag(tenantId, exportData.getType(), etag);
} else {
existingResource = findResourceInfoByTenantIdAndKey(tenantId, exportData.getType(), exportData.getResourceKey());
updateData = true; // we overwrite system resource instead of creating new
}
if (existingResource != null) {
TbResource resource = new TbResource(existingResource);
if (updateData && !etag.equals(resource.getEtag())) {
resource.setData(data);
log.info("[{}] Updating existing resource {}", tenantId, existingResource.getLink());
} else {
log.info("[{}] Using existing resource {}", tenantId, existingResource.getLink());
}
return resource;
}
TbResource resource = new TbResource();
resource.setTenantId(tenantId);
resource.setFileName(exportData.getFileName());
if (isNotEmpty(exportData.getTitle())) {
resource.setTitle(exportData.getTitle());
} else {
resource.setTitle(exportData.getFileName());
}
resource.setResourceSubType(exportData.getSubType());
resource.setResourceType(exportData.getType());
resource.setResourceKey(exportData.getResourceKey());
resource.setData(data);
log.info("[{}] Creating resource {}", tenantId, resource.getResourceKey());
return resource;
}
@Override
public TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId) {
log.trace("Executing findResourceInfoById [{}] [{}]", tenantId, resourceId);
@ -462,7 +532,9 @@ public class BaseResourceService extends AbstractCachedEntityService<ResourceInf
if (resourceType == ResourceType.DASHBOARD) {
Dashboard dashboard = JacksonUtil.fromString(data, Dashboard.class);
dashboard.setTenantId(TenantId.SYS_TENANT_ID);
if (CollectionUtils.isNotEmpty(dashboard.getResources())) {
importResources(dashboard.getTenantId(), dashboard.getResources());
}
imageService.updateImagesUsage(dashboard);
updateResourcesUsage(dashboard);

4
dao/src/main/java/org/thingsboard/server/dao/widget/WidgetTypeServiceImpl.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.widget;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@ -98,6 +99,9 @@ public class WidgetTypeServiceImpl implements WidgetTypeService {
log.trace("Executing saveWidgetType [{}]", widgetTypeDetails);
widgetTypeValidator.validate(widgetTypeDetails, WidgetType::getTenantId);
try {
if (CollectionUtils.isNotEmpty(widgetTypeDetails.getResources())) {
resourceService.importResources(widgetTypeDetails.getTenantId(), widgetTypeDetails.getResources());
}
imageService.updateImagesUsage(widgetTypeDetails);
resourceService.updateResourcesUsage(widgetTypeDetails);

Loading…
Cancel
Save