From 0d62b2890c03ae469ef8ae6c3cb3a41859745a90 Mon Sep 17 00:00:00 2001 From: Yevhen Bondarenko <56396344+YevhenBondarenko@users.noreply.github.com> Date: Wed, 24 Mar 2021 11:03:13 +0200 Subject: [PATCH] [3.3] added tests for resources (#4282) * added TbResourceService test * fix ResourceService test * created TbResourceControllerSqlTest * fixed BaseTbResourceServiceTest --- .../controller/TbResourceController.java | 33 +- .../service/install/InstallScripts.java | 1 + .../install/SqlDatabaseUpgradeService.java | 1 + .../BaseTbResourceControllerTest.java | 284 ++++++++++++++ .../sql/TbResourceControllerSqlTest.java | 23 ++ .../dao/resource/TbResourceService.java | 4 +- .../server/common/data/TbResource.java | 4 + .../server/common/data/TbResourceInfo.java | 5 +- .../data/transport/resource/Resource.java | 37 -- .../server/dao/model/ModelConstants.java | 1 + .../dao/model/sql/TbResourceEntity.java | 19 +- .../dao/model/sql/TbResourceInfoEntity.java | 9 +- .../dao/resource/BaseTbResourceService.java | 125 +++++-- .../dao/resource/TbResourceInfoDao.java | 4 +- .../sql/resource/JpaTbResourceInfoDao.java | 16 +- .../resource/TbResourceInfoRepository.java | 19 +- .../resources/sql/schema-entities-hsql.sql | 1 + .../main/resources/sql/schema-entities.sql | 1 + .../dao/service/AbstractServiceTest.java | 4 + .../service/BaseTbResourceServiceTest.java | 350 ++++++++++++++++++ .../service/sql/TbResourceServiceSqlTest.java | 23 ++ 21 files changed, 869 insertions(+), 95 deletions(-) create mode 100644 application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java create mode 100644 application/src/test/java/org/thingsboard/server/controller/sql/TbResourceControllerSqlTest.java delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/Resource.java create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/BaseTbResourceServiceTest.java create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/sql/TbResourceServiceSqlTest.java diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java index 4336ef85fe..6e2267b184 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java @@ -16,6 +16,10 @@ package org.thingsboard.server.controller; import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; @@ -31,11 +35,13 @@ import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.lwm2m.LwM2mObject; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.resource.TbResourceService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; +import java.util.Base64; import java.util.List; @Slf4j @@ -52,6 +58,27 @@ public class TbResourceController extends BaseController { this.resourceService = resourceService; } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/resource/{resourceId}/download", method = RequestMethod.GET) + @ResponseBody + public ResponseEntity downloadResource(@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException { + checkParameter(RESOURCE_ID, strResourceId); + try { + TbResourceId resourceId = new TbResourceId(toUUID(strResourceId)); + TbResource tbResource = checkResourceId(resourceId, Operation.READ); + + ByteArrayResource resource = new ByteArrayResource(Base64.getDecoder().decode(tbResource.getData().getBytes())); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + tbResource.getFileName()) + .header("x-filename", tbResource.getFileName()) + .contentLength(resource.contentLength()) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); + } catch (Exception e) { + throw handleException(e); + } + } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/resource/info/{resourceId}", method = RequestMethod.GET) @ResponseBody @@ -103,7 +130,11 @@ public class TbResourceController extends BaseController { @RequestParam(required = false) String sortOrder) throws ThingsboardException { try { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(resourceService.findResourcesByTenantId(getTenantId(), pageLink)); + if (Authority.SYS_ADMIN.equals(getCurrentUser().getAuthority())) { + return checkNotNull(resourceService.findTenantResourcesByTenantId(getTenantId(), pageLink)); + } else { + return checkNotNull(resourceService.findAllTenantResourcesByTenantId(getTenantId(), pageLink)); + } } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 64f4847abf..fb7634118b 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -205,6 +205,7 @@ public class InstallScripts { try { byte[] fileBytes = Files.readAllBytes(path); TbResource resource = new TbResource(); + resource.setFileName(path.getFileName().toString()); resource.setTenantId(TenantId.SYS_TENANT_ID); resource.setResourceType(ResourceType.LWM2M_MODEL); resource.setData(Base64.getEncoder().encodeToString(fileBytes)); diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index cc6a335a66..6b946bad52 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -458,6 +458,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService "resource_type varchar(32) NOT NULL, " + "resource_key varchar(255) NOT NULL, " + "search_text varchar(255), " + + "file_name varchar(255) NOT NULL, " + "data varchar, " + "CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key)" + ");"); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java new file mode 100644 index 0000000000..31c77f2830 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTbResourceControllerTest.java @@ -0,0 +1,284 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller; + +import com.fasterxml.jackson.core.type.TypeReference; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.security.Authority; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +public abstract class BaseTbResourceControllerTest extends AbstractControllerTest { + + private IdComparator idComparator = new IdComparator<>(); + + private static final String DEFAULT_FILE_NAME = "test.jks"; + + private Tenant savedTenant; + private User tenantAdmin; + + @Before + public void beforeTest() throws Exception { + loginSysAdmin(); + + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + savedTenant = doPost("/api/tenant", tenant, Tenant.class); + Assert.assertNotNull(savedTenant); + + tenantAdmin = new User(); + tenantAdmin.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin.setTenantId(savedTenant.getId()); + tenantAdmin.setEmail("tenant2@thingsboard.org"); + tenantAdmin.setFirstName("Joe"); + tenantAdmin.setLastName("Downs"); + + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + } + + @After + public void afterTest() throws Exception { + loginSysAdmin(); + + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) + .andExpect(status().isOk()); + } + + @Test + public void testSaveTbResource() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My first resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + + TbResource savedResource = doPost("/api/resource", resource, TbResource.class); + + Assert.assertNotNull(savedResource); + Assert.assertNotNull(savedResource.getId()); + Assert.assertTrue(savedResource.getCreatedTime() > 0); + Assert.assertEquals(savedTenant.getId(), savedResource.getTenantId()); + Assert.assertEquals(resource.getTitle(), savedResource.getTitle()); + Assert.assertEquals(DEFAULT_FILE_NAME, savedResource.getFileName()); + Assert.assertEquals(DEFAULT_FILE_NAME, savedResource.getResourceKey()); + Assert.assertEquals(resource.getData(), savedResource.getData()); + + savedResource.setTitle("My new resource"); + + doPost("/api/resource", savedResource, TbResource.class); + + TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class); + Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle()); + } + + @Test + public void testUpdateTbResourceFromDifferentTenant() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My first resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + + TbResource savedResource = doPost("/api/resource", resource, TbResource.class); + + loginDifferentTenant(); + doPost("/api/resource", savedResource, TbResource.class, status().isForbidden()); + deleteDifferentTenant(); + } + + @Test + public void testFindTbResourceById() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My first resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + + TbResource savedResource = doPost("/api/resource", resource, TbResource.class); + + TbResource foundResource = doGet("/api/resource/" + savedResource.getId().getId().toString(), TbResource.class); + Assert.assertNotNull(foundResource); + Assert.assertEquals(savedResource, foundResource); + } + + @Test + public void testDeleteTbResource() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My first resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + + TbResource savedResource = doPost("/api/resource", resource, TbResource.class); + + doDelete("/api/resource/" + savedResource.getId().getId().toString()) + .andExpect(status().isOk()); + + doGet("/api/resource/" + savedResource.getId().getId().toString()) + .andExpect(status().isNotFound()); + } + + @Test + public void testFindTenantTbResources() throws Exception { + List resources = new ArrayList<>(); + for (int i = 0; i < 173; i++) { + TbResource resource = new TbResource(); + resource.setTitle("Resource" + i); + resource.setResourceType(ResourceType.JKS); + resource.setFileName(i + DEFAULT_FILE_NAME); + resource.setData("Test Data"); + resources.add(new TbResourceInfo(doPost("/api/resource", resource, TbResource.class))); + } + List loadedResources = new ArrayList<>(); + PageLink pageLink = new PageLink(24); + PageData pageData; + do { + pageData = doGetTypedWithPageLink("/api/resource?", + new TypeReference>() { + }, pageLink); + loadedResources.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(resources, idComparator); + Collections.sort(loadedResources, idComparator); + + Assert.assertEquals(resources, loadedResources); + } + + @Test + public void testFindSystemTbResources() throws Exception { + loginSysAdmin(); + + List resources = new ArrayList<>(); + for (int i = 0; i < 173; i++) { + TbResource resource = new TbResource(); + resource.setTitle("Resource" + i); + resource.setResourceType(ResourceType.JKS); + resource.setFileName(i + DEFAULT_FILE_NAME); + resource.setData("Test Data"); + resources.add(new TbResourceInfo(doPost("/api/resource", resource, TbResource.class))); + } + List loadedResources = new ArrayList<>(); + PageLink pageLink = new PageLink(24); + PageData pageData; + do { + pageData = doGetTypedWithPageLink("/api/resource?", + new TypeReference>() { + }, pageLink); + loadedResources.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(resources, idComparator); + Collections.sort(loadedResources, idComparator); + + Assert.assertEquals(resources, loadedResources); + + for (TbResourceInfo resource : resources) { + doDelete("/api/resource/" + resource.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new PageLink(27); + loadedResources.clear(); + do { + pageData = doGetTypedWithPageLink("/api/resource?", + new TypeReference>() { + }, pageLink); + loadedResources.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + Assert.assertTrue(loadedResources.isEmpty()); + } + + @Test + public void testFindSystemAndTenantTbResources() throws Exception { + List systemResources = new ArrayList<>(); + List expectedResources = new ArrayList<>(); + for (int i = 0; i < 73; i++) { + TbResource resource = new TbResource(); + resource.setTitle("Resource" + i); + resource.setResourceType(ResourceType.JKS); + resource.setFileName(i + DEFAULT_FILE_NAME); + resource.setData("Test Data"); + expectedResources.add(new TbResourceInfo(doPost("/api/resource", resource, TbResource.class))); + } + + loginSysAdmin(); + + for (int i = 0; i < 173; i++) { + TbResource resource = new TbResource(); + resource.setTitle("Resource" + i); + resource.setResourceType(ResourceType.JKS); + resource.setFileName(i + DEFAULT_FILE_NAME); + resource.setData("Test Data"); + TbResourceInfo savedResource = new TbResourceInfo(doPost("/api/resource", resource, TbResource.class)); + systemResources.add(savedResource); + if (i >= 73) { + expectedResources.add(savedResource); + } + } + + login(tenantAdmin.getEmail(), "testPassword1"); + + List loadedResources = new ArrayList<>(); + PageLink pageLink = new PageLink(24); + PageData pageData; + do { + pageData = doGetTypedWithPageLink("/api/resource?", + new TypeReference>() { + }, pageLink); + loadedResources.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(expectedResources, idComparator); + Collections.sort(loadedResources, idComparator); + + Assert.assertEquals(expectedResources, loadedResources); + + loginSysAdmin(); + + for (TbResourceInfo resource : systemResources) { + doDelete("/api/resource/" + resource.getId().getId().toString()) + .andExpect(status().isOk()); + } + } +} diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/TbResourceControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/TbResourceControllerSqlTest.java new file mode 100644 index 0000000000..dd2441e8f2 --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/sql/TbResourceControllerSqlTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.controller.sql; + +import org.thingsboard.server.controller.BaseTbResourceControllerTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class TbResourceControllerSqlTest extends BaseTbResourceControllerTest { +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/TbResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/TbResourceService.java index e6d09c0f4f..0093b43dfe 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/TbResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/TbResourceService.java @@ -38,7 +38,9 @@ public interface TbResourceService { TbResourceInfo findResourceInfoById(TenantId tenantId, TbResourceId resourceId); - PageData findResourcesByTenantId(TenantId tenantId, PageLink pageLink); + PageData findAllTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink); + + PageData findTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink); List findLwM2mObject(TenantId tenantId, String sortOrder, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java index 57d74d95cf..33e94b2e0c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResource.java @@ -27,6 +27,8 @@ public class TbResource extends TbResourceInfo { private static final long serialVersionUID = 7379609705527272306L; + private String fileName; + private String data; public TbResource() { @@ -61,6 +63,8 @@ public class TbResource extends TbResourceInfo { builder.append(getResourceType()); builder.append(", resourceKey="); builder.append(getResourceKey()); + builder.append(", fileName="); + builder.append(fileName); builder.append(", data="); builder.append(data); builder.append("]"); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java index eda6a5ffc9..d72982aeca 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TbResourceInfo.java @@ -45,12 +45,13 @@ public class TbResourceInfo extends SearchTextBased implements Has this.tenantId = resourceInfo.getTenantId(); this.title = resourceInfo.getTitle(); this.resourceType = resourceInfo.getResourceType(); - this.resourceKey = resourceInfo.resourceKey; + this.resourceKey = resourceInfo.getResourceKey(); + this.searchText = resourceInfo.getSearchText(); } @Override public String getSearchText() { - return searchText; + return searchText != null ? searchText : title; } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/Resource.java b/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/Resource.java deleted file mode 100644 index c29b704b04..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/transport/resource/Resource.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright © 2016-2021 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.thingsboard.server.common.data.transport.resource; - -import lombok.Data; -import org.thingsboard.server.common.data.HasTenantId; -import org.thingsboard.server.common.data.id.TenantId; - -@Data -public class Resource implements HasTenantId { - private TenantId tenantId; - private ResourceType resourceType; - private String resourceId; - private String value; - - @Override - public String toString() { - return "Resource{" + - "tenantId=" + tenantId + - ", resourceType=" + resourceType + - ", resourceId='" + resourceId + '\'' + - '}'; - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 6a078cf36c..3afae3c081 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -464,6 +464,7 @@ public class ModelConstants { public static final String RESOURCE_TYPE_COLUMN = "resource_type"; public static final String RESOURCE_KEY_COLUMN = "resource_key"; public static final String RESOURCE_TITLE_COLUMN = TITLE_PROPERTY; + public static final String RESOURCE_FILE_NAME_COLUMN = "file_name"; public static final String RESOURCE_DATA_COLUMN = "data"; /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java index 8d14d4e6cb..5b22b6e012 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceEntity.java @@ -30,6 +30,7 @@ import javax.persistence.Table; import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_DATA_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_KEY_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; @@ -58,6 +59,9 @@ public class TbResourceEntity extends BaseSqlEntity implements Searc @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; + @Column(name = RESOURCE_FILE_NAME_COLUMN) + private String fileName; + @Column(name = RESOURCE_DATA_COLUMN) private String data; @@ -65,32 +69,37 @@ public class TbResourceEntity extends BaseSqlEntity implements Searc } public TbResourceEntity(TbResource resource) { - this.id = resource.getUuidId(); + if (resource.getId() != null) { + this.id = resource.getId().getId(); + } this.createdTime = resource.getCreatedTime(); - this.tenantId = resource.getTenantId().getId(); + if (resource.getTenantId() != null) { + this.tenantId = resource.getTenantId().getId(); + } this.title = resource.getTitle(); this.resourceType = resource.getResourceType().name(); this.resourceKey = resource.getResourceKey(); this.searchText = resource.getSearchText(); + this.fileName = resource.getFileName(); this.data = resource.getData(); } @Override public TbResource toData() { - TbResource resource = new TbResource(); - resource.setId(new TbResourceId(id)); + TbResource resource = new TbResource(new TbResourceId(id)); resource.setCreatedTime(createdTime); resource.setTenantId(new TenantId(tenantId)); resource.setTitle(title); resource.setResourceType(ResourceType.valueOf(resourceType)); resource.setResourceKey(resourceKey); resource.setSearchText(searchText); + resource.setFileName(fileName); resource.setData(data); return resource; } @Override public String getSearchTextSource() { - return title; + return this.searchText; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java index c91a5dd9ae..78c36861c4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/TbResourceInfoEntity.java @@ -61,8 +61,10 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen } public TbResourceInfoEntity(TbResourceInfo resource) { - this.setUuid(resource.getId().getId()); - this.setCreatedTime(resource.getCreatedTime()); + if (resource.getId() != null) { + this.id = resource.getId().getId(); + } + this.createdTime = resource.getCreatedTime(); this.tenantId = resource.getTenantId().getId(); this.title = resource.getTitle(); this.resourceType = resource.getResourceType().name(); @@ -72,8 +74,7 @@ public class TbResourceInfoEntity extends BaseSqlEntity implemen @Override public TbResourceInfo toData() { - TbResourceInfo resource = new TbResourceInfo(); - resource.setId(new TbResourceId(id)); + TbResourceInfo resource = new TbResourceInfo(new TbResourceId(id)); resource.setCreatedTime(createdTime); resource.setTenantId(new TenantId(tenantId)); resource.setTitle(title); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseTbResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseTbResourceService.java index 9504962fe3..91de57c849 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseTbResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseTbResourceService.java @@ -16,14 +16,17 @@ package org.thingsboard.server.dao.resource; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.model.DDFFileParser; import org.eclipse.leshan.core.model.DefaultDDFFileValidator; import org.eclipse.leshan.core.model.InvalidDDFFileException; import org.eclipse.leshan.core.model.ObjectModel; +import org.hibernate.exception.ConstraintViolationException; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.lwm2m.LwM2mInstance; @@ -32,8 +35,11 @@ import org.thingsboard.server.common.data.lwm2m.LwM2mResource; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TenantDao; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -41,6 +47,7 @@ import java.util.ArrayList; import java.util.Base64; import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; @@ -55,39 +62,59 @@ public class BaseTbResourceService implements TbResourceService { public static final String INCORRECT_RESOURCE_ID = "Incorrect resourceId "; private final TbResourceDao resourceDao; private final TbResourceInfoDao resourceInfoDao; + private final TenantDao tenantDao; private final DDFFileParser ddfFileParser; - public BaseTbResourceService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao) { + public BaseTbResourceService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, TenantDao tenantDao) { this.resourceDao = resourceDao; this.resourceInfoDao = resourceInfoDao; + this.tenantDao = tenantDao; this.ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator()); } @Override - public TbResource saveResource(TbResource tbResource) throws InvalidDDFFileException, IOException { - log.trace("Executing saveResource [{}]", tbResource); - if (ResourceType.LWM2M_MODEL.equals(tbResource.getResourceType())) { + public TbResource saveResource(TbResource resource) throws InvalidDDFFileException, IOException { + log.trace("Executing saveResource [{}]", resource); + if (StringUtils.isEmpty(resource.getData())) { + throw new DataValidationException("Resource data should be specified!"); + } + if (ResourceType.LWM2M_MODEL.equals(resource.getResourceType())) { List objectModels = - ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(tbResource.getData())), tbResource.getSearchText()); + ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getData())), resource.getSearchText()); if (!objectModels.isEmpty()) { ObjectModel objectModel = objectModels.get(0); + String resourceKey = objectModel.id + LWM2M_SEPARATOR_KEY + objectModel.getVersion(); String name = objectModel.name; - tbResource.setResourceKey(resourceKey); - tbResource.setTitle(name); - tbResource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name); + resource.setResourceKey(resourceKey); + resource.setTitle(name); + resource.setSearchText(resourceKey + LWM2M_SEPARATOR_SEARCH_TEXT + name); + } else { + throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText())); + } + } else { + resource.setResourceKey(resource.getFileName()); + } + + resourceValidator.validate(resource, TbResourceInfo::getTenantId); + + try { + return resourceDao.save(resource.getTenantId(), resource); + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("resource_unq_key")) { + String field = ResourceType.LWM2M_MODEL.equals(resource.getResourceType()) ? "resourceKey" : "fileName"; + throw new DataValidationException("Resource with such " + field + " already exists!"); } else { - throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", tbResource.getSearchText())); + throw t; } } - validate(tbResource); - return resourceDao.save(tbResource.getTenantId(), tbResource); + } @Override public TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceKey) { log.trace("Executing getResource [{}] [{}] [{}]", tenantId, resourceType, resourceKey); - validate(tenantId, resourceType, resourceKey); return resourceDao.getResource(tenantId, resourceType, resourceKey); } @@ -113,10 +140,17 @@ public class BaseTbResourceService implements TbResourceService { } @Override - public PageData findResourcesByTenantId(TenantId tenantId, PageLink pageLink) { - log.trace("Executing findByTenantId [{}]", tenantId); + public PageData findAllTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink) { + log.trace("Executing findAllTenantResourcesByTenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + return resourceInfoDao.findAllTenantResourcesByTenantId(tenantId.getId(), pageLink); + } + + @Override + public PageData findTenantResourcesByTenantId(TenantId tenantId, PageLink pageLink) { + log.trace("Executing findTenantResourcesByTenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - return resourceInfoDao.findTbResourcesByTenantId(tenantId.getId(), pageLink); + return resourceInfoDao.findTenantResourcesByTenantId(tenantId.getId(), pageLink); } @Override @@ -154,26 +188,6 @@ public class BaseTbResourceService implements TbResourceService { tenantResourcesRemover.removeEntities(tenantId, tenantId); } - protected void validate(TbResource resource) { - if (resource == null) { - throw new DataValidationException("Resource should be specified!"); - } - if (resource.getData() == null) { - throw new DataValidationException("Resource value should be specified!"); - } - validate(resource.getTenantId(), resource.getResourceType(), resource.getResourceKey()); - } - - protected void validate(TenantId tenantId, ResourceType resourceType, String resourceId) { - if (resourceType == null) { - throw new DataValidationException("Resource type should be specified!"); - } - if (resourceId == null) { - throw new DataValidationException("Resource id should be specified!"); - } - validateId(tenantId, "Incorrect tenantId "); - } - private LwM2mObject toLwM2mObject(TbResource resource) { try { DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator()); @@ -218,6 +232,37 @@ public class BaseTbResourceService implements TbResourceService { return "DESC".equals(sortOrder) ? comparator.reversed() : comparator; } + private DataValidator resourceValidator = new DataValidator<>() { + + @Override + protected void validateDataImpl(TenantId tenantId, TbResource resource) { + if (StringUtils.isEmpty(resource.getTitle())) { + throw new DataValidationException("Resource title should be specified!"); + } + if (resource.getResourceType() == null) { + throw new DataValidationException("Resource type should be specified!"); + } + if (StringUtils.isEmpty(resource.getFileName())) { + throw new DataValidationException("Resource file name should be specified!"); + } + if (StringUtils.isEmpty(resource.getResourceKey())) { + throw new DataValidationException("Resource key should be specified!"); + } + if (resource.getTenantId() == null) { + resource.setTenantId(new TenantId(ModelConstants.NULL_UUID)); + } + if (!resource.getTenantId().getId().equals(ModelConstants.NULL_UUID)) { + Tenant tenant = tenantDao.findById(tenantId, resource.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Resource is referencing to non-existent tenant!"); + } + } + if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && toLwM2mObject(resource) == null) { + throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getSearchText())); + } + } + }; + private PaginatedRemover tenantResourcesRemover = new PaginatedRemover<>() { @@ -231,4 +276,14 @@ public class BaseTbResourceService implements TbResourceService { deleteResource(tenantId, new TbResourceId(entity.getUuidId())); } }; + + protected Optional extractConstraintViolationException(Exception t) { + if (t instanceof ConstraintViolationException) { + return Optional.of((ConstraintViolationException) t); + } else if (t.getCause() instanceof ConstraintViolationException) { + return Optional.of((ConstraintViolationException) (t.getCause())); + } else { + return Optional.empty(); + } + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java index d0c83b0b46..f54e53b849 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceInfoDao.java @@ -24,6 +24,8 @@ import java.util.UUID; public interface TbResourceInfoDao extends Dao { - PageData findTbResourcesByTenantId(UUID tenantId, PageLink pageLink); + PageData findAllTenantResourcesByTenantId(UUID tenantId, PageLink pageLink); + + PageData findTenantResourcesByTenantId(UUID tenantId, PageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java index 9759cf9e02..9e4aac6a70 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceInfoDao.java @@ -49,11 +49,21 @@ public class JpaTbResourceInfoDao extends JpaAbstractSearchTextDao findTbResourcesByTenantId(UUID tenantId, PageLink pageLink) { + public PageData findAllTenantResourcesByTenantId(UUID tenantId, PageLink pageLink) { return DaoUtil.toPageData(resourceInfoRepository - .findByTenantId( + .findAllTenantResourcesByTenantId( tenantId, TenantId.NULL_UUID, Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); } + DaoUtil.toPageable(pageLink))); + } + + @Override + public PageData findTenantResourcesByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.toPageData(resourceInfoRepository + .findTenantResourcesByTenantId( + tenantId, + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java index 5df36ce285..db1c5273c8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/TbResourceInfoRepository.java @@ -26,8 +26,8 @@ import java.util.UUID; public interface TbResourceInfoRepository extends CrudRepository { - @Query("SELECT tr FROM TbResourceInfoEntity tr WHERE tr.tenantId = :tenantId " + - "AND LOWER(tr.searchText) LIKE LOWER(CONCAT(:searchText, '%'))" + + @Query("SELECT tr FROM TbResourceInfoEntity tr WHERE " + + "LOWER(tr.searchText) LIKE LOWER(CONCAT(:searchText, '%'))" + "AND (tr.tenantId = :tenantId " + "OR (tr.tenantId = :systemAdminId " + "AND NOT EXISTS " + @@ -35,8 +35,15 @@ public interface TbResourceInfoRepository extends CrudRepository findByTenantId(@Param("tenantId") UUID tenantId, - @Param("systemAdminId") UUID sysadminId, - @Param("searchText") String searchText, - Pageable pageable); + Page findAllTenantResourcesByTenantId(@Param("tenantId") UUID tenantId, + @Param("systemAdminId") UUID sysadminId, + @Param("searchText") String searchText, + Pageable pageable); + + @Query("SELECT ri FROM TbResourceInfoEntity ri WHERE " + + "ri.tenantId = :tenantId " + + "AND LOWER(ri.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") + Page findTenantResourcesByTenantId(@Param("tenantId") UUID tenantId, + @Param("searchText") String searchText, + Pageable pageable); } diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 404c6a20c4..3fc69ff536 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -433,6 +433,7 @@ CREATE TABLE IF NOT EXISTS resource ( resource_type varchar(32) NOT NULL, resource_key varchar(255) NOT NULL, search_text varchar(255), + file_name varchar(255) NOT NULL, data varchar, CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) ); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 006866edd8..56f0d580eb 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -459,6 +459,7 @@ CREATE TABLE IF NOT EXISTS resource ( resource_type varchar(32) NOT NULL, resource_key varchar(255) NOT NULL, search_text varchar(255), + file_name varchar(255) NOT NULL, data varchar, CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) ); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index 9d04cfc86c..bd19ceb026 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -52,6 +52,7 @@ import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.TbResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.tenant.TenantProfileService; @@ -142,6 +143,9 @@ public abstract class AbstractServiceTest { @Autowired protected DeviceProfileService deviceProfileService; + @Autowired + protected TbResourceService resourceService; + class IdComparator implements Comparator { @Override public int compare(D o1, D o2) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseTbResourceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseTbResourceServiceTest.java new file mode 100644 index 0000000000..d17fcc88ff --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseTbResourceServiceTest.java @@ -0,0 +1,350 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.ResourceType; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.exception.DataValidationException; + +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.List; + +public abstract class BaseTbResourceServiceTest extends AbstractServiceTest { + + private static final String LWM2M_TEST_MODEL = "\n" + + "\n" + + "My first resource\n" + + "\n" + + "0\n" + + "\n" + + "1.0\n" + + "Multiple\n" + + "Mandatory\n" + + "\n" + + "\n" + + "LWM2M\n" + + "\n" + + "Single\n" + + "Mandatory\n" + + "String\n" + + "0..255\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + ""; + + private static final String DEFAULT_FILE_NAME = "test.jks"; + + private IdComparator idComparator = new IdComparator<>(); + + private TenantId tenantId; + + @Before + public void before() { + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + Tenant savedTenant = tenantService.saveTenant(tenant); + Assert.assertNotNull(savedTenant); + tenantId = savedTenant.getId(); + } + + @After + public void after() { + tenantService.deleteTenant(tenantId); + } + + @Test + public void testSaveTbResource() throws Exception { + TbResource resource = new TbResource(); + resource.setTenantId(tenantId); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My first resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + + TbResource savedResource = resourceService.saveResource(resource); + + Assert.assertNotNull(savedResource); + Assert.assertNotNull(savedResource.getId()); + Assert.assertTrue(savedResource.getCreatedTime() > 0); + Assert.assertEquals(resource.getTenantId(), savedResource.getTenantId()); + Assert.assertEquals(resource.getTitle(), savedResource.getTitle()); + Assert.assertEquals(resource.getResourceKey(), savedResource.getResourceKey()); + Assert.assertEquals(resource.getData(), savedResource.getData()); + + savedResource.setTitle("My new resource"); + + resourceService.saveResource(savedResource); + TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); + Assert.assertEquals(foundResource.getTitle(), savedResource.getTitle()); + + resourceService.deleteResource(tenantId, savedResource.getId()); + } + + @Test + public void testSaveLwm2mTbResource() throws Exception { + TbResource resource = new TbResource(); + resource.setTenantId(tenantId); + resource.setResourceType(ResourceType.LWM2M_MODEL); + resource.setFileName("test_model.xml"); + resource.setData(Base64.getEncoder().encodeToString(LWM2M_TEST_MODEL.getBytes())); + + TbResource savedResource = resourceService.saveResource(resource); + + Assert.assertNotNull(savedResource); + Assert.assertNotNull(savedResource.getId()); + Assert.assertTrue(savedResource.getCreatedTime() > 0); + Assert.assertEquals(resource.getTenantId(), savedResource.getTenantId()); + Assert.assertEquals("My first resource", savedResource.getTitle()); + Assert.assertEquals("0_1.0", savedResource.getResourceKey()); + Assert.assertEquals(resource.getData(), savedResource.getData()); + + resourceService.deleteResource(tenantId, savedResource.getId()); + } + + @Test + public void testSaveTbResourceWithEmptyTenant() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + TbResource savedResource = resourceService.saveResource(resource); + + Assert.assertEquals(TenantId.SYS_TENANT_ID, savedResource.getTenantId()); + + resourceService.deleteResource(tenantId, savedResource.getId()); + } + + @Test(expected = DataValidationException.class) + public void testSaveTbResourceWithExistsFileName() throws Exception { + TbResource resource = new TbResource(); + resource.setTenantId(tenantId); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + + TbResource savedResource = resourceService.saveResource(resource); + + TbResource resource2 = new TbResource(); + resource.setTenantId(tenantId); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + + try { + resourceService.saveResource(resource2); + } finally { + resourceService.deleteResource(tenantId, savedResource.getId()); + } + } + + @Test(expected = DataValidationException.class) + public void testSaveTbResourceWithEmptyTitle() throws Exception { + TbResource resource = new TbResource(); + resource.setTenantId(tenantId); + resource.setResourceType(ResourceType.JKS); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + resourceService.saveResource(resource); + } + + @Test(expected = DataValidationException.class) + public void testSaveTbResourceWithInvalidTenant() throws Exception { + TbResource resource = new TbResource(); + resource.setTenantId(new TenantId(Uuids.timeBased())); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + resourceService.saveResource(resource); + } + + @Test + public void testFindResourceById() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + TbResource savedResource = resourceService.saveResource(resource); + + TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); + Assert.assertNotNull(foundResource); + Assert.assertEquals(savedResource, foundResource); + resourceService.deleteResource(tenantId, savedResource.getId()); + } + + @Test + public void testFindResourceByTenantIdAndResourceTypeAndResourceKey() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTenantId(tenantId); + resource.setTitle("My resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + TbResource savedResource = resourceService.saveResource(resource); + + TbResource foundResource = resourceService.getResource(tenantId, savedResource.getResourceType(), savedResource.getResourceKey()); + Assert.assertNotNull(foundResource); + Assert.assertEquals(savedResource, foundResource); + resourceService.deleteResource(tenantId, savedResource.getId()); + } + + @Test + public void testDeleteResource() throws Exception { + TbResource resource = new TbResource(); + resource.setResourceType(ResourceType.JKS); + resource.setTitle("My resource"); + resource.setFileName(DEFAULT_FILE_NAME); + resource.setData("Test Data"); + TbResource savedResource = resourceService.saveResource(resource); + + TbResource foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); + Assert.assertNotNull(foundResource); + resourceService.deleteResource(tenantId, savedResource.getId()); + foundResource = resourceService.findResourceById(tenantId, savedResource.getId()); + Assert.assertNull(foundResource); + } + + @Test + public void testFindTenantResourcesByTenantId() throws Exception { + Tenant tenant = new Tenant(); + tenant.setTitle("Test tenant"); + tenant = tenantService.saveTenant(tenant); + + TenantId tenantId = tenant.getId(); + + List resources = new ArrayList<>(); + for (int i = 0; i < 165; i++) { + TbResource resource = new TbResource(); + resource.setTenantId(tenantId); + resource.setTitle("Resource" + i); + resource.setResourceType(ResourceType.JKS); + resource.setFileName(i + DEFAULT_FILE_NAME); + resource.setData("Test Data"); + resources.add(new TbResourceInfo(resourceService.saveResource(resource))); + } + + List loadedResources = new ArrayList<>(); + PageLink pageLink = new PageLink(16); + PageData pageData; + do { + pageData = resourceService.findTenantResourcesByTenantId(tenantId, pageLink); + loadedResources.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(resources, idComparator); + Collections.sort(loadedResources, idComparator); + + Assert.assertEquals(resources, loadedResources); + + resourceService.deleteResourcesByTenantId(tenantId); + + pageLink = new PageLink(31); + pageData = resourceService.findTenantResourcesByTenantId(tenantId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertTrue(pageData.getData().isEmpty()); + + tenantService.deleteTenant(tenantId); + } + + @Test + public void testFindAllTenantResourcesByTenantId() throws Exception { + Tenant tenant = new Tenant(); + tenant.setTitle("Test tenant"); + tenant = tenantService.saveTenant(tenant); + + TenantId tenantId = tenant.getId(); + + List resources = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + TbResource resource = new TbResource(); + resource.setTenantId(TenantId.SYS_TENANT_ID); + resource.setTitle("System Resource" + i); + resource.setResourceType(ResourceType.JKS); + resource.setFileName(i + DEFAULT_FILE_NAME); + resource.setData("Test Data"); + TbResourceInfo tbResourceInfo = new TbResourceInfo(resourceService.saveResource(resource)); + if (i >= 50) { + resources.add(tbResourceInfo); + } + } + + for (int i = 0; i < 50; i++) { + TbResource resource = new TbResource(); + resource.setTenantId(tenantId); + resource.setTitle("Tenant Resource" + i); + resource.setResourceType(ResourceType.JKS); + resource.setFileName(i + DEFAULT_FILE_NAME); + resource.setData("Test Data"); + resources.add(new TbResourceInfo(resourceService.saveResource(resource))); + } + + List loadedResources = new ArrayList<>(); + PageLink pageLink = new PageLink(10); + PageData pageData; + do { + pageData = resourceService.findAllTenantResourcesByTenantId(tenantId, pageLink); + loadedResources.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageLink.nextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(resources, idComparator); + Collections.sort(loadedResources, idComparator); + + Assert.assertEquals(resources, loadedResources); + + resourceService.deleteResourcesByTenantId(tenantId); + + pageLink = new PageLink(100); + pageData = resourceService.findAllTenantResourcesByTenantId(tenantId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(pageData.getData().size(), 100); + + resourceService.deleteResourcesByTenantId(TenantId.SYS_TENANT_ID); + + pageLink = new PageLink(100); + pageData = resourceService.findAllTenantResourcesByTenantId(TenantId.SYS_TENANT_ID, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertTrue(pageData.getData().isEmpty()); + + tenantService.deleteTenant(tenantId); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/TbResourceServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/TbResourceServiceSqlTest.java new file mode 100644 index 0000000000..2af4881c84 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/TbResourceServiceSqlTest.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.service.sql; + +import org.thingsboard.server.dao.service.BaseTbResourceServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class TbResourceServiceSqlTest extends BaseTbResourceServiceTest { +}