diff --git a/application/pom.xml b/application/pom.xml
index 17b179c767..ef57037448 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
thingsboard
application
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index 8470566af3..2d868a1135 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.ListenableFuture;
import jakarta.mail.MessagingException;
+import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolation;
import lombok.Getter;
@@ -28,6 +29,7 @@ import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@@ -182,7 +184,9 @@ import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService;
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
+import java.io.IOException;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -194,6 +198,7 @@ import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
+import java.util.zip.GZIPOutputStream;
import static org.thingsboard.server.common.data.StringUtils.isNotEmpty;
import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD;
@@ -1008,6 +1013,22 @@ public abstract class BaseController {
}
}
+ protected void compressResponseWithGzipIFAccepted(String acceptEncodingHeader, HttpServletResponse response, byte[] content) throws IOException {
+ if (StringUtils.isNotEmpty(acceptEncodingHeader) && acceptEncodingHeader.contains("gzip")) {
+ response.setHeader(HttpHeaders.CONTENT_ENCODING, "gzip");
+ response.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
+ try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(response.getOutputStream())) {
+ gzipOutputStream.write(content);
+ gzipOutputStream.finish();
+ }
+ } else {
+ try (ServletOutputStream outputStream = response.getOutputStream()) {
+ outputStream.write(content);
+ outputStream.flush();
+ }
+ }
+ }
+
protected ResponseEntity response(HttpStatus status) {
return ResponseEntity.status(status).build();
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
index e3e793014d..e50484bed3 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
@@ -22,8 +22,10 @@ import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -31,6 +33,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@@ -67,6 +70,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.DASHBOARD_ID_PARAM_DESCRIPTION;
@@ -149,17 +153,20 @@ public class DashboardController extends BaseController {
)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/dashboard/{dashboardId}")
- public Dashboard getDashboardById(@Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
+ public void getDashboardById(@Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId,
@Parameter(description = INCLUDE_RESOURCES_DESCRIPTION)
- @RequestParam(value = INCLUDE_RESOURCES, required = false) boolean includeResources) throws ThingsboardException {
+ @RequestParam(value = INCLUDE_RESOURCES, required = false) boolean includeResources,
+ @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader,
+ HttpServletResponse response) throws Exception {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
Dashboard dashboard = checkDashboardId(dashboardId, Operation.READ);
if (includeResources) {
dashboard.setResources(tbResourceService.exportResources(dashboard, getCurrentUser()));
}
- return dashboard;
+ response.setContentType(APPLICATION_JSON_VALUE);
+ compressResponseWithGzipIFAccepted(acceptEncodingHeader, response, JacksonUtil.writeValueAsBytes(dashboard));
}
@ApiOperation(value = "Create Or Update Dashboard (saveDashboard)",
@@ -171,11 +178,15 @@ public class DashboardController extends BaseController {
TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@PostMapping(value = "/dashboard")
- public Dashboard saveDashboard(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the dashboard.")
- @RequestBody Dashboard dashboard) throws Exception {
+ public void saveDashboard(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the dashboard.")
+ @RequestBody Dashboard dashboard,
+ @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader,
+ HttpServletResponse response) throws Exception {
dashboard.setTenantId(getTenantId());
checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD);
- return tbDashboardService.save(dashboard, getCurrentUser());
+ var savedDashboard = tbDashboardService.save(dashboard, getCurrentUser());
+ response.setContentType(APPLICATION_JSON_VALUE);
+ compressResponseWithGzipIFAccepted(acceptEncodingHeader, response, JacksonUtil.writeValueAsBytes(savedDashboard));
}
@ApiOperation(value = "Delete the Dashboard (deleteDashboard)",
@@ -408,12 +419,13 @@ public class DashboardController extends BaseController {
"If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. "
+ DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
- @RequestMapping(value = "/dashboard/home", method = RequestMethod.GET)
- @ResponseBody
- public HomeDashboard getHomeDashboard() throws ThingsboardException {
+ @GetMapping(value = "/dashboard/home")
+ public void getHomeDashboard(@RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader,
+ HttpServletResponse response) throws Exception {
SecurityUser securityUser = getCurrentUser();
+ response.setContentType(APPLICATION_JSON_VALUE);
if (securityUser.isSystemAdmin()) {
- return null;
+ return;
}
User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId());
JsonNode additionalInfo = user.getAdditionalInfo();
@@ -431,7 +443,9 @@ public class DashboardController extends BaseController {
homeDashboard = extractHomeDashboardFromAdditionalInfo(additionalInfo);
}
}
- return homeDashboard;
+ if (homeDashboard != null) {
+ compressResponseWithGzipIFAccepted(acceptEncodingHeader, response, JacksonUtil.writeValueAsBytes(homeDashboard));
+ }
}
@ApiOperation(value = "Get Home Dashboard Info (getHomeDashboardInfo)",
diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java
index cd1116a420..5a7bd19d8e 100644
--- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java
@@ -61,7 +61,9 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
+import java.io.ByteArrayOutputStream;
import java.util.concurrent.TimeUnit;
+import java.util.zip.GZIPOutputStream;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
@@ -70,6 +72,7 @@ import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_INC
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
+import static org.thingsboard.server.dao.util.ImageUtils.mediaTypeToFileExtension;
@Slf4j
@RestController
@@ -179,15 +182,17 @@ public class ImageController extends BaseController {
@PathVariable String type,
@Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key,
- @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception {
- return downloadIfChanged(type, key, etag, false);
+ @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag,
+ @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader) throws Exception {
+ return downloadIfChanged(type, key, etag, acceptEncodingHeader, false);
}
@GetMapping(value = "/api/images/public/{publicResourceKey}", produces = "image/*")
public ResponseEntity downloadPublicImage(@PathVariable String publicResourceKey,
- @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception {
+ @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag,
+ @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader) throws Exception {
ImageCacheKey cacheKey = ImageCacheKey.forPublicImage(publicResourceKey);
- return downloadIfChanged(cacheKey, etag, () -> imageService.getPublicImageInfoByKey(publicResourceKey));
+ return downloadIfChanged(cacheKey, etag, acceptEncodingHeader, () -> imageService.getPublicImageInfoByKey(publicResourceKey));
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@@ -213,8 +218,9 @@ public class ImageController extends BaseController {
@PathVariable String type,
@Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key,
- @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception {
- return downloadIfChanged(type, key, etag, true);
+ @RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag,
+ @RequestHeader(name = HttpHeaders.ACCEPT_ENCODING, required = false) String acceptEncodingHeader) throws Exception {
+ return downloadIfChanged(type, key, etag, acceptEncodingHeader, true);
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@@ -268,12 +274,12 @@ public class ImageController extends BaseController {
return (result.isSuccess() ? ResponseEntity.ok() : ResponseEntity.badRequest()).body(result);
}
- private ResponseEntity downloadIfChanged(String type, String key, String etag, boolean preview) throws Exception {
+ private ResponseEntity downloadIfChanged(String type, String key, String etag, String acceptEncodingHeader, boolean preview) throws Exception {
ImageCacheKey cacheKey = ImageCacheKey.forImage(getTenantId(type), key, preview);
- return downloadIfChanged(cacheKey, etag, () -> checkImageInfo(type, key, Operation.READ));
+ return downloadIfChanged(cacheKey, etag, acceptEncodingHeader, () -> checkImageInfo(type, key, Operation.READ));
}
- private ResponseEntity downloadIfChanged(ImageCacheKey cacheKey, String etag, ThrowingSupplier imageInfoSupplier) throws Exception {
+ private ResponseEntity downloadIfChanged(ImageCacheKey cacheKey, String etag, String acceptEncodingHeader, ThrowingSupplier imageInfoSupplier) throws Exception {
if (StringUtils.isNotEmpty(etag)) {
etag = StringUtils.remove(etag, '\"'); // etag is wrapped in double quotes due to HTTP specification
if (etag.equals(tbImageService.getETag(cacheKey))) {
@@ -294,7 +300,6 @@ public class ImageController extends BaseController {
tbImageService.putETag(cacheKey, descriptor.getEtag());
var result = ResponseEntity.ok()
.header("Content-Type", descriptor.getMediaType())
- .contentLength(data.length)
.eTag(descriptor.getEtag());
if (!cacheKey.isPublic()) {
result
@@ -308,7 +313,19 @@ public class ImageController extends BaseController {
} else {
result.cacheControl(CacheControl.noCache());
}
- return result.body(new ByteArrayResource(data));
+ var responseData = data;
+ if (mediaTypeToFileExtension(descriptor.getMediaType()).equals("svg") &&
+ StringUtils.isNotEmpty(acceptEncodingHeader) && acceptEncodingHeader.contains("gzip")) {
+ result.header(HttpHeaders.CONTENT_ENCODING, "gzip");
+ var outputStream = new ByteArrayOutputStream();
+ try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream)) {
+ gzipOutputStream.write(data);
+ gzipOutputStream.finish();
+ }
+ responseData = outputStream.toByteArray();
+ }
+ result.contentLength(responseData.length);
+ return result.body(new ByteArrayResource(responseData));
}
private TbResourceInfo checkImageInfo(String imageType, String key, Operation operation) throws ThingsboardException {
diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
index e055c6d6ae..72c339c635 100644
--- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
@@ -477,7 +477,7 @@ public class NotificationController extends BaseController {
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/notification/deliveryMethods")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
- public Set getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
+ public List getAvailableDeliveryMethods(@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
return notificationCenter.getAvailableDeliveryMethods(user.getTenantId());
}
diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java b/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java
index 00797da449..da2b7276dd 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/update/ResourcesUpdater.java
@@ -101,7 +101,7 @@ public class ResourcesUpdater {
for (DashboardId dashboardId : dashboards) {
executor.submit(() -> {
Dashboard dashboard = dashboardService.findDashboardById(TenantId.SYS_TENANT_ID, dashboardId);
- boolean updated = resourceService.updateResourcesUsage(dashboard); // will convert resources ids to new structure
+ boolean updated = resourceService.updateResourcesUsage(dashboard.getTenantId(), dashboard); // will convert resources ids to new structure
if (updated) {
dashboardService.saveDashboard(dashboard);
updatedCount.incrementAndGet();
@@ -130,7 +130,7 @@ public class ResourcesUpdater {
for (WidgetTypeId widgetTypeId : widgets) {
executor.submit(() -> {
WidgetTypeDetails widgetTypeDetails = widgetTypeService.findWidgetTypeDetailsById(TenantId.SYS_TENANT_ID, widgetTypeId);
- boolean updated = resourceService.updateResourcesUsage(widgetTypeDetails);
+ boolean updated = resourceService.updateResourcesUsage(widgetTypeDetails.getTenantId(), widgetTypeDetails);
if (updated) {
widgetTypeService.saveWidgetType(widgetTypeDetails);
updatedCount.incrementAndGet();
diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
index 311fe6cb2c..af63256f0f 100644
--- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
+++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java
@@ -417,7 +417,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
}
@Override
- public Set getAvailableDeliveryMethods(TenantId tenantId) {
+ public List getAvailableDeliveryMethods(TenantId tenantId) {
return channels.values().stream()
.filter(channel -> {
try {
@@ -428,7 +428,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
}
})
.map(NotificationChannel::getDeliveryMethod)
- .collect(Collectors.toSet());
+ .sorted().toList();
}
@Override
diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
index 90ad3e38dd..e3a79a8ba5 100644
--- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
+++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java
@@ -46,7 +46,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
-import java.util.function.Function;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -132,12 +132,12 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
@Override
public List exportResources(Dashboard dashboard, SecurityUser user) throws ThingsboardException {
- return exportResources(dashboard, imageService::getUsedImages, resourceService::getUsedResources, user);
+ return exportResources(() -> imageService.getUsedImages(dashboard), () -> resourceService.getUsedResources(user.getTenantId(), dashboard), user);
}
@Override
public List exportResources(WidgetTypeDetails widgetTypeDetails, SecurityUser user) throws ThingsboardException {
- return exportResources(widgetTypeDetails, imageService::getUsedImages, resourceService::getUsedResources, user);
+ return exportResources(() -> imageService.getUsedImages(widgetTypeDetails), () -> resourceService.getUsedResources(user.getTenantId(), widgetTypeDetails), user);
}
@Override
@@ -153,13 +153,12 @@ public class DefaultTbResourceService extends AbstractTbEntityService implements
}
}
- private List exportResources(T entity,
- Function> imagesProcessor,
- Function> resourcesProcessor,
+ private List exportResources(Supplier> imagesProcessor,
+ Supplier> resourcesProcessor,
SecurityUser user) throws ThingsboardException {
List resources = new ArrayList<>();
- resources.addAll(imagesProcessor.apply(entity));
- resources.addAll(resourcesProcessor.apply(entity));
+ resources.addAll(imagesProcessor.get());
+ resources.addAll(resourcesProcessor.get());
for (TbResourceInfo resourceInfo : resources) {
accessControlService.checkPermission(user, Resource.TB_RESOURCE, Operation.READ, resourceInfo.getId(), resourceInfo);
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java
index 88dd8f241c..ffbf95d990 100644
--- a/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/DashboardControllerTest.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -602,13 +603,14 @@ public class DashboardControllerTest extends AbstractControllerTest {
dashboard.setTitle("My dashboard");
dashboard.setConfiguration(JacksonUtil.newObjectNode()
.put("someImage", "tb-image;/api/images/tenant/" + imageInfo.getResourceKey())
- .set("widgets", JacksonUtil.toJsonNode("""
+ .set("widgets", JacksonUtil.toJsonNode("""
{"xxx":
{"config":{"actions":{"elementClick":[
{"customResources":[{"url":{"entityType":"TB_RESOURCE","id":
"tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js"},"isModule":true},
{"url":"tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js","isModule":true}]}]}}}}
- """)));
+ """))
+ .put("someResource", "tb-resource;/api/resource/js_module/tenant/gateway-management-extension.js"));
dashboard = doPost("/api/dashboard", dashboard, Dashboard.class);
Dashboard exportedDashboard = doGet("/api/dashboard/" + dashboard.getUuidId() + "?includeResources=true", Dashboard.class);
@@ -637,12 +639,18 @@ public class DashboardControllerTest extends AbstractControllerTest {
doPost("/api/resource", resource, TbResourceInfo.class);
Dashboard importedDashboard = doPost("/api/dashboard", exportedDashboard, Dashboard.class);
+ String newResourceKey = "gateway-management-extension_(1).js";
+
imageRef = importedDashboard.getConfiguration().get("someImage").asText();
assertThat(imageRef).isEqualTo("tb-image;/api/images/tenant/" + imageInfo.getResourceKey());
- resourceRef = importedDashboard.getConfiguration().get("widgets").get("xxx").get("config")
- .get("actions").get("elementClick").get(0).get("customResources").get(0).get("url").asText();
- String newResourceKey = "gateway-management-extension_(1).js";
- assertThat(resourceRef).isEqualTo("tb-resource;/api/resource/js_module/tenant/" + newResourceKey);
+
+ List resourcesRefs = new ArrayList<>();
+ resourcesRefs.add(importedDashboard.getConfiguration().get("widgets").get("xxx").get("config")
+ .get("actions").get("elementClick").get(0).get("customResources").get(0).get("url").asText());
+ resourcesRefs.add(importedDashboard.getConfiguration().get("someResource").asText());
+ assertThat(resourcesRefs).allSatisfy(ref -> {
+ assertThat(ref).isEqualTo("tb-resource;/api/resource/js_module/tenant/" + newResourceKey);
+ });
TbResourceInfo importedImageInfo = doGet("/api/images/tenant/" + imageInfo.getResourceKey() + "/info", TbResourceInfo.class);
assertThat(importedImageInfo.getEtag()).isEqualTo(imageInfo.getEtag());
diff --git a/common/actor/pom.xml b/common/actor/pom.xml
index cc19cc949f..cbbce12336 100644
--- a/common/actor/pom.xml
+++ b/common/actor/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/cache/pom.xml b/common/cache/pom.xml
index 1ecb95c6c5..a4f7dd4f7b 100644
--- a/common/cache/pom.xml
+++ b/common/cache/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/cluster-api/pom.xml b/common/cluster-api/pom.xml
index bd7dc214cf..4bbe65d99f 100644
--- a/common/cluster-api/pom.xml
+++ b/common/cluster-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/coap-server/pom.xml b/common/coap-server/pom.xml
index 401d1e94fd..32da47ca08 100644
--- a/common/coap-server/pom.xml
+++ b/common/coap-server/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/dao-api/pom.xml b/common/dao-api/pom.xml
index 62c1e420c2..7d48a63903 100644
--- a/common/dao-api/pom.xml
+++ b/common/dao-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java
index 0e2acb2e4e..f7f4b1ce4b 100644
--- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java
+++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java
@@ -80,13 +80,13 @@ public interface ResourceService extends EntityDaoService {
TbResourceInfo findSystemOrTenantResourceByEtag(TenantId tenantId, ResourceType resourceType, String etag);
- boolean updateResourcesUsage(Dashboard dashboard);
+ boolean updateResourcesUsage(TenantId tenantId, Dashboard dashboard);
- boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails);
+ boolean updateResourcesUsage(TenantId tenantId, WidgetTypeDetails widgetTypeDetails);
- Collection getUsedResources(Dashboard dashboard);
+ Collection getUsedResources(TenantId tenantId, Dashboard dashboard);
- Collection getUsedResources(WidgetTypeDetails widgetTypeDetails);
+ Collection getUsedResources(TenantId tenantId, WidgetTypeDetails widgetTypeDetails);
TbResource createOrUpdateSystemResource(ResourceType resourceType, String resourceKey, byte[] data);
diff --git a/common/data/pom.xml b/common/data/pom.xml
index b206f0b173..e6eca1aa93 100644
--- a/common/data/pom.xml
+++ b/common/data/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java
index d025aab3d5..fed271cbbe 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/widget/WidgetType.java
@@ -50,7 +50,8 @@ public class WidgetType extends BaseWidgetType {
@JsonIgnore
public JsonNode getDefaultConfig() {
- return Optional.ofNullable(descriptor.get("defaultConfig"))
+ return Optional.ofNullable(descriptor)
+ .map(descriptor -> descriptor.get("defaultConfig"))
.filter(JsonNode::isTextual).map(JsonNode::asText)
.map(json -> {
try {
diff --git a/common/edge-api/pom.xml b/common/edge-api/pom.xml
index 31d90de948..68e647642d 100644
--- a/common/edge-api/pom.xml
+++ b/common/edge-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/message/pom.xml b/common/message/pom.xml
index ac56095e7e..0614c94bc4 100644
--- a/common/message/pom.xml
+++ b/common/message/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/pom.xml b/common/pom.xml
index c905e0eee9..8bd1904d72 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
thingsboard
common
diff --git a/common/proto/pom.xml b/common/proto/pom.xml
index 4409d6b97f..fded7298e4 100644
--- a/common/proto/pom.xml
+++ b/common/proto/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/queue/pom.xml b/common/queue/pom.xml
index 7485294f6e..fab4ff3989 100644
--- a/common/queue/pom.xml
+++ b/common/queue/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/script/pom.xml b/common/script/pom.xml
index 88e35e8429..1aad1dd39e 100644
--- a/common/script/pom.xml
+++ b/common/script/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/script/remote-js-client/pom.xml b/common/script/remote-js-client/pom.xml
index 7309e6af51..38a14cd8f3 100644
--- a/common/script/remote-js-client/pom.xml
+++ b/common/script/remote-js-client/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
script
org.thingsboard.common.script
diff --git a/common/script/script-api/pom.xml b/common/script/script-api/pom.xml
index 08ef60713e..4210f1f528 100644
--- a/common/script/script-api/pom.xml
+++ b/common/script/script-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
script
org.thingsboard.common.script
diff --git a/common/stats/pom.xml b/common/stats/pom.xml
index 31fbc72dcc..231811144a 100644
--- a/common/stats/pom.xml
+++ b/common/stats/pom.xml
@@ -22,7 +22,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/transport/coap/pom.xml b/common/transport/coap/pom.xml
index d4528944e0..0bd4c0d6e3 100644
--- a/common/transport/coap/pom.xml
+++ b/common/transport/coap/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
transport
org.thingsboard.common.transport
diff --git a/common/transport/http/pom.xml b/common/transport/http/pom.xml
index 054fc3cbbe..5a0dd9e4ff 100644
--- a/common/transport/http/pom.xml
+++ b/common/transport/http/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
transport
org.thingsboard.common.transport
diff --git a/common/transport/lwm2m/pom.xml b/common/transport/lwm2m/pom.xml
index 54232ac104..ddb9d7a399 100644
--- a/common/transport/lwm2m/pom.xml
+++ b/common/transport/lwm2m/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
transport
org.thingsboard.common.transport
diff --git a/common/transport/mqtt/pom.xml b/common/transport/mqtt/pom.xml
index e672f7a5dd..45d228f225 100644
--- a/common/transport/mqtt/pom.xml
+++ b/common/transport/mqtt/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
transport
org.thingsboard.common.transport
diff --git a/common/transport/pom.xml b/common/transport/pom.xml
index c87fa8c2bf..6c4310151d 100644
--- a/common/transport/pom.xml
+++ b/common/transport/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/transport/snmp/pom.xml b/common/transport/snmp/pom.xml
index 78665bfde2..a2b9dd7c84 100644
--- a/common/transport/snmp/pom.xml
+++ b/common/transport/snmp/pom.xml
@@ -21,7 +21,7 @@
org.thingsboard.common
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
transport
diff --git a/common/transport/transport-api/pom.xml b/common/transport/transport-api/pom.xml
index 7d221c48bf..8f01cab08b 100644
--- a/common/transport/transport-api/pom.xml
+++ b/common/transport/transport-api/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard.common
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
transport
org.thingsboard.common.transport
diff --git a/common/util/pom.xml b/common/util/pom.xml
index ad3bbaba58..194724b70f 100644
--- a/common/util/pom.xml
+++ b/common/util/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/common/version-control/pom.xml b/common/version-control/pom.xml
index f37139b3c5..8e3fceff99 100644
--- a/common/version-control/pom.xml
+++ b/common/version-control/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
common
org.thingsboard.common
diff --git a/dao/pom.xml b/dao/pom.xml
index 1dac54c142..c1f5a1f62b 100644
--- a/dao/pom.xml
+++ b/dao/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.9.0-SNAPSHOT
+ 3.9.0-RC
thingsboard
dao
diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
index b547de1eca..2e9e37577c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
@@ -162,18 +162,19 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb
dashboardValidator.validate(dashboard, DashboardInfo::getTenantId);
}
try {
+ TenantId tenantId = dashboard.getTenantId();
if (CollectionUtils.isNotEmpty(dashboard.getResources())) {
- resourceService.importResources(dashboard.getTenantId(), dashboard.getResources());
+ resourceService.importResources(tenantId, dashboard.getResources());
}
imageService.updateImagesUsage(dashboard);
- resourceService.updateResourcesUsage(dashboard);
+ resourceService.updateResourcesUsage(tenantId, dashboard);
- var saved = dashboardDao.save(dashboard.getTenantId(), dashboard);
+ var saved = dashboardDao.save(tenantId, dashboard);
publishEvictEvent(new DashboardTitleEvictEvent(saved.getId()));
- eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(saved.getTenantId())
+ eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId)
.entityId(saved.getId()).created(dashboard.getId() == null).build());
if (dashboard.getId() == null) {
- countService.publishCountEntityEvictEvent(saved.getTenantId(), EntityType.DASHBOARD);
+ countService.publishCountEntityEvictEvent(tenantId, EntityType.DASHBOARD);
}
return saved;
} catch (Exception e) {
diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java
index 683c323ab5..452bd7ccf7 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java
@@ -64,7 +64,7 @@ public class OAuth2ClientServiceImpl extends AbstractEntityService implements OA
@Override
public List findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType(String pkgName, PlatformType platformType) {
- log.trace("Executing findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType pkgName=[{}] platformType=[{}]",pkgName, platformType);
+ log.trace("Executing findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType pkgName=[{}] platformType=[{}]", pkgName, platformType);
return oauth2ClientDao.findEnabledByPkgNameAndPlatformType(pkgName, platformType)
.stream()
.map(OAuth2Utils::toClientLoginInfo)
diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java
index 22aa2ae748..9395aae6c7 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java
@@ -57,6 +57,7 @@ import org.thingsboard.server.dao.service.Validator;
import org.thingsboard.server.dao.service.validator.ResourceDataValidator;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
@@ -401,18 +402,33 @@ public class BaseResourceService extends AbstractCachedEntityService links = getResourcesLinks(dashboard.getResources());
- return updateResourcesUsage(dashboard.getTenantId(), dashboard.getConfiguration(), DASHBOARD_RESOURCES_MAPPING, links);
+ return updateResourcesUsage(tenantId, List.of(dashboard.getConfiguration()), List.of(DASHBOARD_RESOURCES_MAPPING), links);
}
@Override
- public boolean updateResourcesUsage(WidgetTypeDetails widgetTypeDetails) {
+ public boolean updateResourcesUsage(TenantId tenantId, WidgetTypeDetails widgetTypeDetails) {
Map links = getResourcesLinks(widgetTypeDetails.getResources());
- boolean updated = updateResourcesUsage(widgetTypeDetails.getTenantId(), widgetTypeDetails.getDescriptor(), WIDGET_RESOURCES_MAPPING, links);
+ List jsonNodes = new ArrayList<>(2);
+ List