diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 24497d461f..dd43e1c4da 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -21,6 +21,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.TenantAssetType; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -136,13 +137,18 @@ public class AssetController extends BaseController { @ResponseBody public TextPageData getTenantAssets( @RequestParam int limit, + @RequestParam(required = false) String type, @RequestParam(required = false) String textSearch, @RequestParam(required = false) String idOffset, @RequestParam(required = false) String textOffset) throws ThingsboardException { try { TenantId tenantId = getCurrentUser().getTenantId(); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink)); + if (type != null && type.trim().length()>0) { + return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink)); + } } catch (Exception e) { throw handleException(e); } @@ -167,6 +173,7 @@ public class AssetController extends BaseController { public TextPageData getCustomerAssets( @PathVariable("customerId") String strCustomerId, @RequestParam int limit, + @RequestParam(required = false) String type, @RequestParam(required = false) String textSearch, @RequestParam(required = false) String idOffset, @RequestParam(required = false) String textOffset) throws ThingsboardException { @@ -176,7 +183,11 @@ public class AssetController extends BaseController { CustomerId customerId = new CustomerId(toUUID(strCustomerId)); checkCustomerId(customerId); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); + if (type != null && type.trim().length()>0) { + return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else { + return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink)); + } } catch (Exception e) { throw handleException(e); } @@ -231,4 +242,18 @@ public class AssetController extends BaseController { throw handleException(e); } } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/asset/types", method = RequestMethod.GET) + @ResponseBody + public List getAssetTypes() throws ThingsboardException { + try { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> assetTypes = assetService.findAssetTypesByTenantId(tenantId); + return checkNotNull(assetTypes.get()); + } catch (Exception e) { + throw handleException(e); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 7cd381c57a..8257767140 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -21,6 +21,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.TenantDeviceType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; @@ -166,13 +167,18 @@ public class DeviceController extends BaseController { @ResponseBody public TextPageData getTenantDevices( @RequestParam int limit, + @RequestParam(required = false) String type, @RequestParam(required = false) String textSearch, @RequestParam(required = false) String idOffset, @RequestParam(required = false) String textOffset) throws ThingsboardException { try { TenantId tenantId = getCurrentUser().getTenantId(); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink)); + if (type != null && type.trim().length()>0) { + return checkNotNull(deviceService.findDevicesByTenantIdAndType(tenantId, type, pageLink)); + } else { + return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink)); + } } catch (Exception e) { throw handleException(e); } @@ -197,6 +203,7 @@ public class DeviceController extends BaseController { public TextPageData getCustomerDevices( @PathVariable("customerId") String strCustomerId, @RequestParam int limit, + @RequestParam(required = false) String type, @RequestParam(required = false) String textSearch, @RequestParam(required = false) String idOffset, @RequestParam(required = false) String textOffset) throws ThingsboardException { @@ -206,7 +213,11 @@ public class DeviceController extends BaseController { CustomerId customerId = new CustomerId(toUUID(strCustomerId)); checkCustomerId(customerId); TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset); - return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink)); + if (type != null && type.trim().length()>0) { + return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink)); + } else { + return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink)); + } } catch (Exception e) { throw handleException(e); } @@ -261,4 +272,19 @@ public class DeviceController extends BaseController { throw handleException(e); } } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/device/types", method = RequestMethod.GET) + @ResponseBody + public List getDeviceTypes() throws ThingsboardException { + try { + SecurityUser user = getCurrentUser(); + TenantId tenantId = user.getTenantId(); + ListenableFuture> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId); + return checkNotNull(deviceTypes.get()); + } catch (Exception e) { + throw handleException(e); + } + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java index b9647e514d..435f9a64f3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java @@ -98,13 +98,15 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppC @IntegrationTest("server.port:0") public abstract class AbstractControllerTest { + protected static final String TEST_TENANT_NAME = "TEST TENANT"; + protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org"; private static final String SYS_ADMIN_PASSWORD = "sysadmin"; - protected static final String TENANT_ADMIN_EMAIL = "tenant@thingsboard.org"; + protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org"; private static final String TENANT_ADMIN_PASSWORD = "tenant"; - protected static final String CUSTOMER_USER_EMAIL = "customer@thingsboard.org"; + protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org"; private static final String CUSTOMER_USER_PASSWORD = "customer"; protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), @@ -147,7 +149,7 @@ public abstract class AbstractControllerTest { loginSysAdmin(); Tenant tenant = new Tenant(); - tenant.setTitle("Tenant"); + tenant.setTitle(TEST_TENANT_NAME); Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); Assert.assertNotNull(savedTenant); tenantId = savedTenant.getId(); diff --git a/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java new file mode 100644 index 0000000000..10a2ff7c8f --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java @@ -0,0 +1,659 @@ +/** + * Copyright © 2016-2017 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 static org.hamcrest.Matchers.containsString; +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang3.RandomStringUtils; +import org.thingsboard.server.common.data.*; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.TenantAssetType; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.page.TextPageData; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.model.ModelConstants; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.datastax.driver.core.utils.UUIDs; +import com.fasterxml.jackson.core.type.TypeReference; + +public class AssetControllerTest extends AbstractControllerTest { + + private IdComparator idComparator = new IdComparator<>(); + + 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 testSaveAsset() throws Exception { + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + + Assert.assertNotNull(savedAsset); + Assert.assertNotNull(savedAsset.getId()); + Assert.assertTrue(savedAsset.getCreatedTime() > 0); + Assert.assertEquals(savedTenant.getId(), savedAsset.getTenantId()); + Assert.assertNotNull(savedAsset.getCustomerId()); + Assert.assertEquals(NULL_UUID, savedAsset.getCustomerId().getId()); + Assert.assertEquals(asset.getName(), savedAsset.getName()); + + savedAsset.setName("My new asset"); + doPost("/api/asset", savedAsset, Asset.class); + + Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class); + Assert.assertEquals(foundAsset.getName(), savedAsset.getName()); + } + + @Test + public void testFindAssetById() throws Exception { + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class); + Assert.assertNotNull(foundAsset); + Assert.assertEquals(savedAsset, foundAsset); + } + + @Test + public void testFindAssetTypesByTenantId() throws Exception { + List assets = new ArrayList<>(); + for (int i=0;i<3;i++) { + Asset asset = new Asset(); + asset.setName("My asset B"+i); + asset.setType("typeB"); + assets.add(doPost("/api/asset", asset, Asset.class)); + } + for (int i=0;i<7;i++) { + Asset asset = new Asset(); + asset.setName("My asset C"+i); + asset.setType("typeC"); + assets.add(doPost("/api/asset", asset, Asset.class)); + } + for (int i=0;i<9;i++) { + Asset asset = new Asset(); + asset.setName("My asset A"+i); + asset.setType("typeA"); + assets.add(doPost("/api/asset", asset, Asset.class)); + } + List assetTypes = doGetTyped("/api/asset/types", + new TypeReference>(){}); + + Assert.assertNotNull(assetTypes); + Assert.assertEquals(3, assetTypes.size()); + Assert.assertEquals("typeA", assetTypes.get(0).getType()); + Assert.assertEquals("typeB", assetTypes.get(1).getType()); + Assert.assertEquals("typeC", assetTypes.get(2).getType()); + } + + @Test + public void testDeleteAsset() throws Exception { + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + + doDelete("/api/asset/"+savedAsset.getId().getId().toString()) + .andExpect(status().isOk()); + + doGet("/api/asset/"+savedAsset.getId().getId().toString()) + .andExpect(status().isNotFound()); + } + + @Test + public void testSaveAssetWithEmptyType() throws Exception { + Asset asset = new Asset(); + asset.setName("My asset"); + doPost("/api/asset", asset) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Asset type should be specified"))); + } + + @Test + public void testSaveAssetWithEmptyName() throws Exception { + Asset asset = new Asset(); + asset.setType("default"); + doPost("/api/asset", asset) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Asset name should be specified"))); + } + + @Test + public void testAssignUnassignAssetToCustomer() throws Exception { + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + + Customer customer = new Customer(); + customer.setTitle("My customer"); + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + + Asset assignedAsset = doPost("/api/customer/" + savedCustomer.getId().getId().toString() + + "/asset/" + savedAsset.getId().getId().toString(), Asset.class); + Assert.assertEquals(savedCustomer.getId(), assignedAsset.getCustomerId()); + + Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class); + Assert.assertEquals(savedCustomer.getId(), foundAsset.getCustomerId()); + + Asset unassignedAsset = + doDelete("/api/customer/asset/" + savedAsset.getId().getId().toString(), Asset.class); + Assert.assertEquals(ModelConstants.NULL_UUID, unassignedAsset.getCustomerId().getId()); + + foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class); + Assert.assertEquals(ModelConstants.NULL_UUID, foundAsset.getCustomerId().getId()); + } + + @Test + public void testAssignAssetToNonExistentCustomer() throws Exception { + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + + doPost("/api/customer/" + UUIDs.timeBased().toString() + + "/asset/" + savedAsset.getId().getId().toString()) + .andExpect(status().isNotFound()); + } + + @Test + public void testAssignAssetToCustomerFromDifferentTenant() throws Exception { + loginSysAdmin(); + + Tenant tenant2 = new Tenant(); + tenant2.setTitle("Different tenant"); + Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class); + Assert.assertNotNull(savedTenant2); + + User tenantAdmin2 = new User(); + tenantAdmin2.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin2.setTenantId(savedTenant2.getId()); + tenantAdmin2.setEmail("tenant3@thingsboard.org"); + tenantAdmin2.setFirstName("Joe"); + tenantAdmin2.setLastName("Downs"); + + tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1"); + + Customer customer = new Customer(); + customer.setTitle("Different customer"); + Customer savedCustomer = doPost("/api/customer", customer, Customer.class); + + login(tenantAdmin.getEmail(), "testPassword1"); + + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = doPost("/api/asset", asset, Asset.class); + + doPost("/api/customer/" + savedCustomer.getId().getId().toString() + + "/asset/" + savedAsset.getId().getId().toString()) + .andExpect(status().isForbidden()); + + loginSysAdmin(); + + doDelete("/api/tenant/"+savedTenant2.getId().getId().toString()) + .andExpect(status().isOk()); + } + + @Test + public void testFindTenantAssets() throws Exception { + List assets = new ArrayList<>(); + for (int i=0;i<178;i++) { + Asset asset = new Asset(); + asset.setName("Asset"+i); + asset.setType("default"); + assets.add(doPost("/api/asset", asset, Asset.class)); + } + List loadedAssets = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(23); + TextPageData pageData = null; + do { + pageData = doGetTypedWithPageLink("/api/tenant/assets?", + new TypeReference>(){}, pageLink); + loadedAssets.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assets, idComparator); + Collections.sort(loadedAssets, idComparator); + + Assert.assertEquals(assets, loadedAssets); + } + + @Test + public void testFindTenantAssetsByName() throws Exception { + String title1 = "Asset title 1"; + List assetsTitle1 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Asset asset = new Asset(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType("default"); + assetsTitle1.add(doPost("/api/asset", asset, Asset.class)); + } + String title2 = "Asset title 2"; + List assetsTitle2 = new ArrayList<>(); + for (int i=0;i<75;i++) { + Asset asset = new Asset(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType("default"); + assetsTitle2.add(doPost("/api/asset", asset, Asset.class)); + } + + List loadedAssetsTitle1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15, title1); + TextPageData pageData = null; + do { + pageData = doGetTypedWithPageLink("/api/tenant/assets?", + new TypeReference>(){}, pageLink); + loadedAssetsTitle1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsTitle1, idComparator); + Collections.sort(loadedAssetsTitle1, idComparator); + + Assert.assertEquals(assetsTitle1, loadedAssetsTitle1); + + List loadedAssetsTitle2 = new ArrayList<>(); + pageLink = new TextPageLink(4, title2); + do { + pageData = doGetTypedWithPageLink("/api/tenant/assets?", + new TypeReference>(){}, pageLink); + loadedAssetsTitle2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsTitle2, idComparator); + Collections.sort(loadedAssetsTitle2, idComparator); + + Assert.assertEquals(assetsTitle2, loadedAssetsTitle2); + + for (Asset asset : loadedAssetsTitle1) { + doDelete("/api/asset/"+asset.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4, title1); + pageData = doGetTypedWithPageLink("/api/tenant/assets?", + new TypeReference>(){}, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Asset asset : loadedAssetsTitle2) { + doDelete("/api/asset/"+asset.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4, title2); + pageData = doGetTypedWithPageLink("/api/tenant/assets?", + new TypeReference>(){}, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + } + + @Test + public void testFindTenantAssetsByType() throws Exception { + String title1 = "Asset title 1"; + String type1 = "typeA"; + List assetsType1 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Asset asset = new Asset(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType(type1); + assetsType1.add(doPost("/api/asset", asset, Asset.class)); + } + String title2 = "Asset title 2"; + String type2 = "typeB"; + List assetsType2 = new ArrayList<>(); + for (int i=0;i<75;i++) { + Asset asset = new Asset(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType(type2); + assetsType2.add(doPost("/api/asset", asset, Asset.class)); + } + + List loadedAssetsType1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15); + TextPageData pageData = null; + do { + pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", + new TypeReference>(){}, pageLink, type1); + loadedAssetsType1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsType1, idComparator); + Collections.sort(loadedAssetsType1, idComparator); + + Assert.assertEquals(assetsType1, loadedAssetsType1); + + List loadedAssetsType2 = new ArrayList<>(); + pageLink = new TextPageLink(4); + do { + pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", + new TypeReference>(){}, pageLink, type2); + loadedAssetsType2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsType2, idComparator); + Collections.sort(loadedAssetsType2, idComparator); + + Assert.assertEquals(assetsType2, loadedAssetsType2); + + for (Asset asset : loadedAssetsType1) { + doDelete("/api/asset/"+asset.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4); + pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", + new TypeReference>(){}, pageLink, type1); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Asset asset : loadedAssetsType2) { + doDelete("/api/asset/"+asset.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4); + pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&", + new TypeReference>(){}, pageLink, type2); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + } + + @Test + public void testFindCustomerAssets() throws Exception { + Customer customer = new Customer(); + customer.setTitle("Test customer"); + customer = doPost("/api/customer", customer, Customer.class); + CustomerId customerId = customer.getId(); + + List assets = new ArrayList<>(); + for (int i=0;i<128;i++) { + Asset asset = new Asset(); + asset.setName("Asset"+i); + asset.setType("default"); + asset = doPost("/api/asset", asset, Asset.class); + assets.add(doPost("/api/customer/" + customerId.getId().toString() + + "/asset/" + asset.getId().getId().toString(), Asset.class)); + } + + List loadedAssets = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(23); + TextPageData pageData = null; + do { + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", + new TypeReference>(){}, pageLink); + loadedAssets.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assets, idComparator); + Collections.sort(loadedAssets, idComparator); + + Assert.assertEquals(assets, loadedAssets); + } + + @Test + public void testFindCustomerAssetsByName() throws Exception { + Customer customer = new Customer(); + customer.setTitle("Test customer"); + customer = doPost("/api/customer", customer, Customer.class); + CustomerId customerId = customer.getId(); + + String title1 = "Asset title 1"; + List assetsTitle1 = new ArrayList<>(); + for (int i=0;i<125;i++) { + Asset asset = new Asset(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType("default"); + asset = doPost("/api/asset", asset, Asset.class); + assetsTitle1.add(doPost("/api/customer/" + customerId.getId().toString() + + "/asset/" + asset.getId().getId().toString(), Asset.class)); + } + String title2 = "Asset title 2"; + List assetsTitle2 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Asset asset = new Asset(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType("default"); + asset = doPost("/api/asset", asset, Asset.class); + assetsTitle2.add(doPost("/api/customer/" + customerId.getId().toString() + + "/asset/" + asset.getId().getId().toString(), Asset.class)); + } + + List loadedAssetsTitle1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15, title1); + TextPageData pageData = null; + do { + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", + new TypeReference>(){}, pageLink); + loadedAssetsTitle1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsTitle1, idComparator); + Collections.sort(loadedAssetsTitle1, idComparator); + + Assert.assertEquals(assetsTitle1, loadedAssetsTitle1); + + List loadedAssetsTitle2 = new ArrayList<>(); + pageLink = new TextPageLink(4, title2); + do { + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", + new TypeReference>(){}, pageLink); + loadedAssetsTitle2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsTitle2, idComparator); + Collections.sort(loadedAssetsTitle2, idComparator); + + Assert.assertEquals(assetsTitle2, loadedAssetsTitle2); + + for (Asset asset : loadedAssetsTitle1) { + doDelete("/api/customer/asset/" + asset.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4, title1); + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", + new TypeReference>(){}, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Asset asset : loadedAssetsTitle2) { + doDelete("/api/customer/asset/" + asset.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4, title2); + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?", + new TypeReference>(){}, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + } + + @Test + public void testFindCustomerAssetsByType() throws Exception { + Customer customer = new Customer(); + customer.setTitle("Test customer"); + customer = doPost("/api/customer", customer, Customer.class); + CustomerId customerId = customer.getId(); + + String title1 = "Asset title 1"; + String type1 = "typeC"; + List assetsType1 = new ArrayList<>(); + for (int i=0;i<125;i++) { + Asset asset = new Asset(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType(type1); + asset = doPost("/api/asset", asset, Asset.class); + assetsType1.add(doPost("/api/customer/" + customerId.getId().toString() + + "/asset/" + asset.getId().getId().toString(), Asset.class)); + } + String title2 = "Asset title 2"; + String type2 = "typeD"; + List assetsType2 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Asset asset = new Asset(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType(type2); + asset = doPost("/api/asset", asset, Asset.class); + assetsType2.add(doPost("/api/customer/" + customerId.getId().toString() + + "/asset/" + asset.getId().getId().toString(), Asset.class)); + } + + List loadedAssetsType1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15); + TextPageData pageData = null; + do { + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", + new TypeReference>(){}, pageLink, type1); + loadedAssetsType1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsType1, idComparator); + Collections.sort(loadedAssetsType1, idComparator); + + Assert.assertEquals(assetsType1, loadedAssetsType1); + + List loadedAssetsType2 = new ArrayList<>(); + pageLink = new TextPageLink(4); + do { + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", + new TypeReference>(){}, pageLink, type2); + loadedAssetsType2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsType2, idComparator); + Collections.sort(loadedAssetsType2, idComparator); + + Assert.assertEquals(assetsType2, loadedAssetsType2); + + for (Asset asset : loadedAssetsType1) { + doDelete("/api/customer/asset/" + asset.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4); + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", + new TypeReference>(){}, pageLink, type1); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Asset asset : loadedAssetsType2) { + doDelete("/api/customer/asset/" + asset.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4); + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&", + new TypeReference>(){}, pageLink, type2); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java index 69bb761c97..5d40a79680 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java @@ -24,10 +24,7 @@ import java.util.Collections; import java.util.List; import org.apache.commons.lang3.RandomStringUtils; -import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.*; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceCredentialsId; import org.thingsboard.server.common.data.id.DeviceId; @@ -83,6 +80,7 @@ public class DeviceControllerTest extends AbstractControllerTest { public void testSaveDevice() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); Assert.assertNotNull(savedDevice); @@ -114,16 +112,49 @@ public class DeviceControllerTest extends AbstractControllerTest { public void testFindDeviceById() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class); Assert.assertNotNull(foundDevice); Assert.assertEquals(savedDevice, foundDevice); } + + @Test + public void testFindDeviceTypesByTenantId() throws Exception { + List devices = new ArrayList<>(); + for (int i=0;i<3;i++) { + Device device = new Device(); + device.setName("My device B"+i); + device.setType("typeB"); + devices.add(doPost("/api/device", device, Device.class)); + } + for (int i=0;i<7;i++) { + Device device = new Device(); + device.setName("My device C"+i); + device.setType("typeC"); + devices.add(doPost("/api/device", device, Device.class)); + } + for (int i=0;i<9;i++) { + Device device = new Device(); + device.setName("My device A"+i); + device.setType("typeA"); + devices.add(doPost("/api/device", device, Device.class)); + } + List deviceTypes = doGetTyped("/api/device/types", + new TypeReference>(){}); + + Assert.assertNotNull(deviceTypes); + Assert.assertEquals(3, deviceTypes.size()); + Assert.assertEquals("typeA", deviceTypes.get(0).getType()); + Assert.assertEquals("typeB", deviceTypes.get(1).getType()); + Assert.assertEquals("typeC", deviceTypes.get(2).getType()); + } @Test public void testDeleteDevice() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); doDelete("/api/device/"+savedDevice.getId().getId().toString()) @@ -132,10 +163,20 @@ public class DeviceControllerTest extends AbstractControllerTest { doGet("/api/device/"+savedDevice.getId().getId().toString()) .andExpect(status().isNotFound()); } - + + @Test + public void testSaveDeviceWithEmptyType() throws Exception { + Device device = new Device(); + device.setName("My device"); + doPost("/api/device", device) + .andExpect(status().isBadRequest()) + .andExpect(statusReason(containsString("Device type should be specified"))); + } + @Test public void testSaveDeviceWithEmptyName() throws Exception { Device device = new Device(); + device.setType("default"); doPost("/api/device", device) .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("Device name should be specified"))); @@ -145,6 +186,7 @@ public class DeviceControllerTest extends AbstractControllerTest { public void testAssignUnassignDeviceToCustomer() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); Customer customer = new Customer(); @@ -170,6 +212,7 @@ public class DeviceControllerTest extends AbstractControllerTest { public void testAssignDeviceToNonExistentCustomer() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); doPost("/api/customer/" + UUIDs.timeBased().toString() @@ -203,6 +246,7 @@ public class DeviceControllerTest extends AbstractControllerTest { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); doPost("/api/customer/" + savedCustomer.getId().getId().toString() @@ -219,6 +263,7 @@ public class DeviceControllerTest extends AbstractControllerTest { public void testFindDeviceCredentialsByDeviceId() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); @@ -229,6 +274,7 @@ public class DeviceControllerTest extends AbstractControllerTest { public void testSaveDeviceCredentials() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); @@ -255,6 +301,7 @@ public class DeviceControllerTest extends AbstractControllerTest { public void testSaveDeviceCredentialsWithEmptyCredentialsType() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); @@ -268,6 +315,7 @@ public class DeviceControllerTest extends AbstractControllerTest { public void testSaveDeviceCredentialsWithEmptyCredentialsId() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); @@ -281,6 +329,7 @@ public class DeviceControllerTest extends AbstractControllerTest { public void testSaveNonExistentDeviceCredentials() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); @@ -298,6 +347,7 @@ public class DeviceControllerTest extends AbstractControllerTest { public void testSaveDeviceCredentialsWithNonExistentDevice() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); @@ -307,9 +357,10 @@ public class DeviceControllerTest extends AbstractControllerTest { } @Test - public void testSaveDeviceCredentialsWithInvalidCredemtialsIdLength() throws Exception { + public void testSaveDeviceCredentialsWithInvalidCredentialsIdLength() throws Exception { Device device = new Device(); device.setName("My device"); + device.setType("default"); Device savedDevice = doPost("/api/device", device, Device.class); DeviceCredentials deviceCredentials = doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); @@ -325,6 +376,7 @@ public class DeviceControllerTest extends AbstractControllerTest { for (int i=0;i<178;i++) { Device device = new Device(); device.setName("Device"+i); + device.setType("default"); devices.add(doPost("/api/device", device, Device.class)); } List loadedDevices = new ArrayList<>(); @@ -355,6 +407,7 @@ public class DeviceControllerTest extends AbstractControllerTest { String name = title1+suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); + device.setType("default"); devicesTitle1.add(doPost("/api/device", device, Device.class)); } String title2 = "Device title 2"; @@ -365,6 +418,7 @@ public class DeviceControllerTest extends AbstractControllerTest { String name = title2+suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); + device.setType("default"); devicesTitle2.add(doPost("/api/device", device, Device.class)); } @@ -423,6 +477,89 @@ public class DeviceControllerTest extends AbstractControllerTest { Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } + + @Test + public void testFindTenantDevicesByType() throws Exception { + String title1 = "Device title 1"; + String type1 = "typeA"; + List devicesType1 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Device device = new Device(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + device.setName(name); + device.setType(type1); + devicesType1.add(doPost("/api/device", device, Device.class)); + } + String title2 = "Device title 2"; + String type2 = "typeB"; + List devicesType2 = new ArrayList<>(); + for (int i=0;i<75;i++) { + Device device = new Device(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + device.setName(name); + device.setType(type2); + devicesType2.add(doPost("/api/device", device, Device.class)); + } + + List loadedDevicesType1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15); + TextPageData pageData = null; + do { + pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", + new TypeReference>(){}, pageLink, type1); + loadedDevicesType1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(devicesType1, idComparator); + Collections.sort(loadedDevicesType1, idComparator); + + Assert.assertEquals(devicesType1, loadedDevicesType1); + + List loadedDevicesType2 = new ArrayList<>(); + pageLink = new TextPageLink(4); + do { + pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", + new TypeReference>(){}, pageLink, type2); + loadedDevicesType2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(devicesType2, idComparator); + Collections.sort(loadedDevicesType2, idComparator); + + Assert.assertEquals(devicesType2, loadedDevicesType2); + + for (Device device : loadedDevicesType1) { + doDelete("/api/device/"+device.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4); + pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", + new TypeReference>(){}, pageLink, type1); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Device device : loadedDevicesType2) { + doDelete("/api/device/"+device.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4); + pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", + new TypeReference>(){}, pageLink, type2); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + } @Test public void testFindCustomerDevices() throws Exception { @@ -435,6 +572,7 @@ public class DeviceControllerTest extends AbstractControllerTest { for (int i=0;i<128;i++) { Device device = new Device(); device.setName("Device"+i); + device.setType("default"); device = doPost("/api/device", device, Device.class); devices.add(doPost("/api/customer/" + customerId.getId().toString() + "/device/" + device.getId().getId().toString(), Device.class)); @@ -473,6 +611,7 @@ public class DeviceControllerTest extends AbstractControllerTest { String name = title1+suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); + device.setType("default"); device = doPost("/api/device", device, Device.class); devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString() + "/device/" + device.getId().getId().toString(), Device.class)); @@ -485,6 +624,7 @@ public class DeviceControllerTest extends AbstractControllerTest { String name = title2+suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); + device.setType("default"); device = doPost("/api/device", device, Device.class); devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString() + "/device/" + device.getId().getId().toString(), Device.class)); @@ -546,4 +686,96 @@ public class DeviceControllerTest extends AbstractControllerTest { Assert.assertEquals(0, pageData.getData().size()); } + @Test + public void testFindCustomerDevicesByType() throws Exception { + Customer customer = new Customer(); + customer.setTitle("Test customer"); + customer = doPost("/api/customer", customer, Customer.class); + CustomerId customerId = customer.getId(); + + String title1 = "Device title 1"; + String type1 = "typeC"; + List devicesType1 = new ArrayList<>(); + for (int i=0;i<125;i++) { + Device device = new Device(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + device.setName(name); + device.setType(type1); + device = doPost("/api/device", device, Device.class); + devicesType1.add(doPost("/api/customer/" + customerId.getId().toString() + + "/device/" + device.getId().getId().toString(), Device.class)); + } + String title2 = "Device title 2"; + String type2 = "typeD"; + List devicesType2 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Device device = new Device(); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + device.setName(name); + device.setType(type2); + device = doPost("/api/device", device, Device.class); + devicesType2.add(doPost("/api/customer/" + customerId.getId().toString() + + "/device/" + device.getId().getId().toString(), Device.class)); + } + + List loadedDevicesType1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15); + TextPageData pageData = null; + do { + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", + new TypeReference>(){}, pageLink, type1); + loadedDevicesType1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(devicesType1, idComparator); + Collections.sort(loadedDevicesType1, idComparator); + + Assert.assertEquals(devicesType1, loadedDevicesType1); + + List loadedDevicesType2 = new ArrayList<>(); + pageLink = new TextPageLink(4); + do { + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", + new TypeReference>(){}, pageLink, type2); + loadedDevicesType2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(devicesType2, idComparator); + Collections.sort(loadedDevicesType2, idComparator); + + Assert.assertEquals(devicesType2, loadedDevicesType2); + + for (Device device : loadedDevicesType1) { + doDelete("/api/customer/device/" + device.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4); + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", + new TypeReference>(){}, pageLink, type1); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Device device : loadedDevicesType2) { + doDelete("/api/customer/device/" + device.getId().getId().toString()) + .andExpect(status().isOk()); + } + + pageLink = new TextPageLink(4); + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", + new TypeReference>(){}, pageLink, type2); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java index 5d72076ee6..dc4a4228f3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java @@ -130,7 +130,7 @@ public class TenantControllerTest extends AbstractControllerTest { Assert.assertEquals(tenants, loadedTenants); for (Tenant tenant : loadedTenants) { - if (!tenant.getTitle().equals("Tenant")) { + if (!tenant.getTitle().equals(TEST_TENANT_NAME)) { doDelete("/api/tenant/"+tenant.getId().getId().toString()) .andExpect(status().isOk()); } diff --git a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java index 7c0004956b..4fafd617d4 100644 --- a/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java @@ -182,7 +182,7 @@ public class UserControllerTest extends AbstractControllerTest { Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); Assert.assertNotNull(savedTenant); - String email = "tenant@thingsboard.org"; + String email = TENANT_ADMIN_EMAIL; User user = new User(); user.setAuthority(Authority.TENANT_ADMIN); user.setTenantId(savedTenant.getId()); diff --git a/application/src/test/java/org/thingsboard/server/system/HttpDeviceApiTest.java b/application/src/test/java/org/thingsboard/server/system/HttpDeviceApiTest.java index b65a5a60ea..af8b50fc48 100644 --- a/application/src/test/java/org/thingsboard/server/system/HttpDeviceApiTest.java +++ b/application/src/test/java/org/thingsboard/server/system/HttpDeviceApiTest.java @@ -47,6 +47,7 @@ public class HttpDeviceApiTest extends AbstractControllerTest { loginTenantAdmin(); device = new Device(); device.setName("My device"); + device.setType("default"); device = doPost("/api/device", device, Device.class); deviceCredentials = diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantDeviceType.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantDeviceType.java new file mode 100644 index 0000000000..d611a2507b --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantDeviceType.java @@ -0,0 +1,79 @@ +/** + * Copyright © 2016-2017 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; + +import org.thingsboard.server.common.data.id.TenantId; + +public class TenantDeviceType { + + private static final long serialVersionUID = 8057240243859922101L; + + private String type; + private TenantId tenantId; + + public TenantDeviceType() { + super(); + } + + public TenantDeviceType(String type, TenantId tenantId) { + this.type = type; + this.tenantId = tenantId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public TenantId getTenantId() { + return tenantId; + } + + public void setTenantId(TenantId tenantId) { + this.tenantId = tenantId; + } + + @Override + public int hashCode() { + int result = type != null ? type.hashCode() : 0; + result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TenantDeviceType that = (TenantDeviceType) o; + + if (type != null ? !type.equals(that.type) : that.type != null) return false; + return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null; + + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TenantDeviceType{"); + sb.append("type='").append(type).append('\''); + sb.append(", tenantId=").append(tenantId); + sb.append('}'); + return sb.toString(); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java new file mode 100644 index 0000000000..bd23f2aa5a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java @@ -0,0 +1,79 @@ +/** + * Copyright © 2016-2017 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.asset; + +import org.thingsboard.server.common.data.id.TenantId; + +public class TenantAssetType { + + private static final long serialVersionUID = 8057290243855622101L; + + private String type; + private TenantId tenantId; + + public TenantAssetType() { + super(); + } + + public TenantAssetType(String type, TenantId tenantId) { + this.type = type; + this.tenantId = tenantId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public TenantId getTenantId() { + return tenantId; + } + + public void setTenantId(TenantId tenantId) { + this.tenantId = tenantId; + } + + @Override + public int hashCode() { + int result = type != null ? type.hashCode() : 0; + result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TenantAssetType that = (TenantAssetType) o; + + if (type != null ? !type.equals(that.type) : that.type != null) return false; + return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null; + + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TenantAssetType{"); + sb.append("type='").append(type).append('\''); + sb.append(", tenantId=").append(tenantId); + sb.append('}'); + return sb.toString(); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java index 81f95b4821..c2ffd1fcc7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java @@ -16,12 +16,11 @@ package org.thingsboard.server.dao.asset; import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.model.AssetEntity; -import org.thingsboard.server.dao.model.DeviceEntity; +import org.thingsboard.server.dao.model.TenantAssetTypeEntity; import java.util.List; import java.util.Optional; @@ -50,6 +49,16 @@ public interface AssetDao extends Dao { */ List findAssetsByTenantId(UUID tenantId, TextPageLink pageLink); + /** + * Find assets by tenantId, type and page link. + * + * @param tenantId the tenantId + * @param type the type + * @param pageLink the page link + * @return the list of asset objects + */ + List findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink); + /** * Find assets by tenantId and assets Ids. * @@ -69,6 +78,17 @@ public interface AssetDao extends Dao { */ List findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink); + /** + * Find assets by tenantId, customerId, type and page link. + * + * @param tenantId the tenantId + * @param customerId the customerId + * @param type the type + * @param pageLink the page link + * @return the list of asset objects + */ + List findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink); + /** * Find assets by tenantId, customerId and assets Ids. * @@ -87,4 +107,12 @@ public interface AssetDao extends Dao { * @return the optional asset object */ Optional findAssetsByTenantIdAndName(UUID tenantId, String name); + + /** + * Find tenants asset types. + * + * @return the list of tenant asset type objects + */ + ListenableFuture> findTenantAssetTypesAsync(); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java index ed08c6ddf3..5dba18b07e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java @@ -15,7 +15,12 @@ */ package org.thingsboard.server.dao.asset; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.mapping.Result; +import com.google.common.base.Function; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -23,7 +28,9 @@ import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.AbstractSearchTextDao; import org.thingsboard.server.dao.model.AssetEntity; +import org.thingsboard.server.dao.model.TenantAssetTypeEntity; +import javax.annotation.Nullable; import java.util.*; import static com.datastax.driver.core.querybuilder.QueryBuilder.*; @@ -59,6 +66,16 @@ public class AssetDaoImpl extends AbstractSearchTextDao implements return assetEntities; } + @Override + public List findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) { + log.debug("Try to find assets by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink); + List assetEntities = findPageWithTextSearch(ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, + Arrays.asList(eq(ASSET_TYPE_PROPERTY, type), + eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink); + log.trace("Found assets [{}] by tenantId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, type, pageLink); + return assetEntities; + } + @Override public ListenableFuture> findAssetsByTenantIdAndIdsAsync(UUID tenantId, List assetIds) { log.debug("Try to find assets by tenantId [{}] and asset Ids [{}]", tenantId, assetIds); @@ -81,6 +98,19 @@ public class AssetDaoImpl extends AbstractSearchTextDao implements return assetEntities; } + @Override + public List findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) { + log.debug("Try to find assets by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink); + List assetEntities = findPageWithTextSearch(ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, + Arrays.asList(eq(ASSET_TYPE_PROPERTY, type), + eq(ASSET_CUSTOMER_ID_PROPERTY, customerId), + eq(ASSET_TENANT_ID_PROPERTY, tenantId)), + pageLink); + + log.trace("Found assets [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, customerId, type, pageLink); + return assetEntities; + } + @Override public ListenableFuture> findAssetsByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List assetIds) { log.debug("Try to find assets by tenantId [{}], customerId [{}] and asset Ids [{}]", tenantId, customerId, assetIds); @@ -101,4 +131,24 @@ public class AssetDaoImpl extends AbstractSearchTextDao implements return Optional.ofNullable(findOneByStatement(query)); } + @Override + public ListenableFuture> findTenantAssetTypesAsync() { + Select statement = select().distinct().column(ASSET_TYPE_PROPERTY).column(ASSET_TENANT_ID_PROPERTY).from(ASSET_TYPES_BY_TENANT_VIEW_NAME); + statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); + ResultSetFuture resultSetFuture = getSession().executeAsync(statement); + ListenableFuture> result = Futures.transform(resultSetFuture, new Function>() { + @Nullable + @Override + public List apply(@Nullable ResultSet resultSet) { + Result result = cluster.getMapper(TenantAssetTypeEntity.class).map(resultSet); + if (result != null) { + return result.all(); + } else { + return Collections.emptyList(); + } + } + }); + return result; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java index 7a61bd8019..25baeda0d5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.asset; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.TenantAssetType; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -34,7 +35,7 @@ public interface AssetService { Optional findAssetByTenantIdAndName(TenantId tenantId, String name); - Asset saveAsset(Asset device); + Asset saveAsset(Asset asset); Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId); @@ -44,16 +45,21 @@ public interface AssetService { TextPageData findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink); + TextPageData findAssetsByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink); + ListenableFuture> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List assetIds); void deleteAssetsByTenantId(TenantId tenantId); TextPageData findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink); + TextPageData findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink); + ListenableFuture> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List assetIds); void unassignCustomerAssets(TenantId tenantId, CustomerId customerId); ListenableFuture> findAssetsByQuery(AssetSearchQuery query); + ListenableFuture> findAssetTypesByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index c63e5d7aa0..fce0c5eefd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -26,6 +26,7 @@ import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.TenantAssetType; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -36,10 +37,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.dao.customer.CustomerDao; import org.thingsboard.server.dao.entity.BaseEntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.model.AssetEntity; -import org.thingsboard.server.dao.model.CustomerEntity; -import org.thingsboard.server.dao.model.TenantEntity; -import org.thingsboard.server.dao.relation.EntityRelationsQuery; +import org.thingsboard.server.dao.model.*; import org.thingsboard.server.dao.relation.EntitySearchDirection; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -132,7 +130,18 @@ public class BaseAssetService extends BaseEntityService implements AssetService validatePageLink(pageLink, "Incorrect page link " + pageLink); List assetEntities = assetDao.findAssetsByTenantId(tenantId.getId(), pageLink); List assets = convertDataList(assetEntities); - return new TextPageData(assets, pageLink); + return new TextPageData<>(assets, pageLink); + } + + @Override + public TextPageData findAssetsByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink) { + log.trace("Executing findAssetsByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); + validateId(tenantId, "Incorrect tenantId " + tenantId); + validateString(type, "Incorrect type " + type); + validatePageLink(pageLink, "Incorrect page link " + pageLink); + List assetEntities = assetDao.findAssetsByTenantIdAndType(tenantId.getId(), type, pageLink); + List assets = convertDataList(assetEntities); + return new TextPageData<>(assets, pageLink); } @Override @@ -159,7 +168,19 @@ public class BaseAssetService extends BaseEntityService implements AssetService validatePageLink(pageLink, "Incorrect page link " + pageLink); List assetEntities = assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); List assets = convertDataList(assetEntities); - return new TextPageData(assets, pageLink); + return new TextPageData<>(assets, pageLink); + } + + @Override + public TextPageData findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink) { + log.trace("Executing findAssetsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); + validateId(tenantId, "Incorrect tenantId " + tenantId); + validateId(customerId, "Incorrect customerId " + customerId); + validateString(type, "Incorrect type " + type); + validatePageLink(pageLink, "Incorrect page link " + pageLink); + List assetEntities = assetDao.findAssetsByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); + List assets = convertDataList(assetEntities); + return new TextPageData<>(assets, pageLink); } @Override @@ -207,6 +228,25 @@ public class BaseAssetService extends BaseEntityService implements AssetService return assets; } + @Override + public ListenableFuture> findAssetTypesByTenantId(TenantId tenantId) { + log.trace("Executing findAssetTypesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, "Incorrect tenantId " + tenantId); + ListenableFuture> tenantAssetTypeEntities = assetDao.findTenantAssetTypesAsync(); + ListenableFuture> tenantAssetTypes = Futures.transform(tenantAssetTypeEntities, + (Function, List>) assetTypeEntities -> { + List assetTypes = new ArrayList<>(); + for (TenantAssetTypeEntity assetTypeEntity : assetTypeEntities) { + if (assetTypeEntity.getTenantId().equals(tenantId.getId())) { + assetTypes.add(assetTypeEntity.toTenantAssetType()); + } + } + assetTypes.sort((TenantAssetType o1, TenantAssetType o2) -> o1.getType().compareTo(o2.getType())); + return assetTypes; + }); + return tenantAssetTypes; + } + private DataValidator assetValidator = new DataValidator() { @@ -232,6 +272,9 @@ public class BaseAssetService extends BaseEntityService implements AssetService @Override protected void validateDataImpl(Asset asset) { + if (StringUtils.isEmpty(asset.getType())) { + throw new DataValidationException("Asset type should be specified!"); + } if (StringUtils.isEmpty(asset.getName())) { throw new DataValidationException("Asset name should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index b8d395c8e2..241759c6dd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.page.TextPageLink; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.model.DeviceEntity; +import org.thingsboard.server.dao.model.TenantDeviceTypeEntity; /** * The Interface DeviceDao. @@ -48,6 +49,16 @@ public interface DeviceDao extends Dao { */ List findDevicesByTenantId(UUID tenantId, TextPageLink pageLink); + /** + * Find devices by tenantId, type and page link. + * + * @param tenantId the tenantId + * @param type the type + * @param pageLink the page link + * @return the list of device objects + */ + List findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink); + /** * Find devices by tenantId and devices Ids. * @@ -67,6 +78,18 @@ public interface DeviceDao extends Dao { */ List findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink); + /** + * Find devices by tenantId, customerId, type and page link. + * + * @param tenantId the tenantId + * @param customerId the customerId + * @param type the type + * @param pageLink the page link + * @return the list of device objects + */ + List findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink); + + /** * Find devices by tenantId, customerId and devices Ids. * @@ -85,4 +108,11 @@ public interface DeviceDao extends Dao { * @return the optional device object */ Optional findDevicesByTenantIdAndName(UUID tenantId, String name); + + /** + * Find tenants device types. + * + * @return the list of tenant device type objects + */ + ListenableFuture> findTenantDeviceTypesAsync(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java index 81fc0bcde1..590f7b7edf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java @@ -22,7 +22,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.*; import java.util.*; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.querybuilder.Select; +import com.datastax.driver.mapping.Result; +import com.google.common.base.Function; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -32,6 +37,9 @@ import org.thingsboard.server.dao.AbstractSearchTextDao; import org.thingsboard.server.dao.model.DeviceEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.thingsboard.server.dao.model.TenantDeviceTypeEntity; + +import javax.annotation.Nullable; @Component @Slf4j @@ -63,6 +71,16 @@ public class DeviceDaoImpl extends AbstractSearchTextDao implement return deviceEntities; } + @Override + public List findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) { + log.debug("Try to find devices by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink); + List deviceEntities = findPageWithTextSearch(DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, + Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), + eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), pageLink); + log.trace("Found devices [{}] by tenantId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, type, pageLink); + return deviceEntities; + } + @Override public ListenableFuture> findDevicesByTenantIdAndIdsAsync(UUID tenantId, List deviceIds) { log.debug("Try to find devices by tenantId [{}] and device Ids [{}]", tenantId, deviceIds); @@ -75,7 +93,7 @@ public class DeviceDaoImpl extends AbstractSearchTextDao implement @Override public List findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) { - log.debug("Try to find devices by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink); + log.debug("Try to find devices by tenantId [{}], customerId [{}] and pageLink [{}]", tenantId, customerId, pageLink); List deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId), eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), @@ -85,6 +103,19 @@ public class DeviceDaoImpl extends AbstractSearchTextDao implement return deviceEntities; } + @Override + public List findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) { + log.debug("Try to find devices by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink); + List deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME, + Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type), + eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId), + eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), + pageLink); + + log.trace("Found devices [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, type, pageLink); + return deviceEntities; + } + @Override public ListenableFuture> findDevicesByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List deviceIds) { log.debug("Try to find devices by tenantId [{}], customerId [{}] and device Ids [{}]", tenantId, customerId, deviceIds); @@ -105,4 +136,24 @@ public class DeviceDaoImpl extends AbstractSearchTextDao implement return Optional.ofNullable(findOneByStatement(query)); } + @Override + public ListenableFuture> findTenantDeviceTypesAsync() { + Select statement = select().distinct().column(DEVICE_TYPE_PROPERTY).column(DEVICE_TENANT_ID_PROPERTY).from(DEVICE_TYPES_BY_TENANT_VIEW_NAME); + statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel()); + ResultSetFuture resultSetFuture = getSession().executeAsync(statement); + ListenableFuture> result = Futures.transform(resultSetFuture, new Function>() { + @Nullable + @Override + public List apply(@Nullable ResultSet resultSet) { + Result result = cluster.getMapper(TenantDeviceTypeEntity.class).map(resultSet); + if (result != null) { + return result.all(); + } else { + return Collections.emptyList(); + } + } + }); + return result; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index 4715435dec..3e7b6af7ad 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.device; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.TenantDeviceType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; @@ -44,16 +45,22 @@ public interface DeviceService { TextPageData findDevicesByTenantId(TenantId tenantId, TextPageLink pageLink); + TextPageData findDevicesByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink); + ListenableFuture> findDevicesByTenantIdAndIdsAsync(TenantId tenantId, List deviceIds); void deleteDevicesByTenantId(TenantId tenantId); TextPageData findDevicesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink); + TextPageData findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink); + ListenableFuture> findDevicesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List deviceIds); void unassignCustomerDevices(TenantId tenantId, CustomerId customerId); ListenableFuture> findDevicesByQuery(DeviceSearchQuery query); + ListenableFuture> findDeviceTypesByTenantId(TenantId tenantId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index ab6fa4e7fa..d01f154197 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -26,6 +26,7 @@ import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.TenantDeviceType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EntityId; @@ -40,6 +41,7 @@ import org.thingsboard.server.dao.entity.BaseEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.CustomerEntity; import org.thingsboard.server.dao.model.DeviceEntity; +import org.thingsboard.server.dao.model.TenantDeviceTypeEntity; import org.thingsboard.server.dao.model.TenantEntity; import org.thingsboard.server.dao.relation.EntitySearchDirection; import org.thingsboard.server.dao.service.DataValidator; @@ -47,9 +49,7 @@ import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.tenant.TenantDao; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import static org.thingsboard.server.dao.DaoUtil.*; @@ -148,7 +148,18 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic validatePageLink(pageLink, "Incorrect page link " + pageLink); List deviceEntities = deviceDao.findDevicesByTenantId(tenantId.getId(), pageLink); List devices = convertDataList(deviceEntities); - return new TextPageData(devices, pageLink); + return new TextPageData<>(devices, pageLink); + } + + @Override + public TextPageData findDevicesByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink) { + log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); + validateId(tenantId, "Incorrect tenantId " + tenantId); + validateString(type, "Incorrect type " + type); + validatePageLink(pageLink, "Incorrect page link " + pageLink); + List deviceEntities = deviceDao.findDevicesByTenantIdAndType(tenantId.getId(), type, pageLink); + List devices = convertDataList(deviceEntities); + return new TextPageData<>(devices, pageLink); } @Override @@ -176,7 +187,19 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic validatePageLink(pageLink, "Incorrect page link " + pageLink); List deviceEntities = deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink); List devices = convertDataList(deviceEntities); - return new TextPageData(devices, pageLink); + return new TextPageData<>(devices, pageLink); + } + + @Override + public TextPageData findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink) { + log.trace("Executing findDevicesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink); + validateId(tenantId, "Incorrect tenantId " + tenantId); + validateId(customerId, "Incorrect customerId " + customerId); + validateString(type, "Incorrect type " + type); + validatePageLink(pageLink, "Incorrect page link " + pageLink); + List deviceEntities = deviceDao.findDevicesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink); + List devices = convertDataList(deviceEntities); + return new TextPageData<>(devices, pageLink); } @Override @@ -224,6 +247,25 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic return devices; } + @Override + public ListenableFuture> findDeviceTypesByTenantId(TenantId tenantId) { + log.trace("Executing findDeviceTypesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, "Incorrect tenantId " + tenantId); + ListenableFuture> tenantDeviceTypeEntities = deviceDao.findTenantDeviceTypesAsync(); + ListenableFuture> tenantDeviceTypes = Futures.transform(tenantDeviceTypeEntities, + (Function, List>) deviceTypeEntities -> { + List deviceTypes = new ArrayList<>(); + for (TenantDeviceTypeEntity deviceTypeEntity : deviceTypeEntities) { + if (deviceTypeEntity.getTenantId().equals(tenantId.getId())) { + deviceTypes.add(deviceTypeEntity.toTenantDeviceType()); + } + } + deviceTypes.sort((TenantDeviceType o1, TenantDeviceType o2) -> o1.getType().compareTo(o2.getType())); + return deviceTypes; + }); + return tenantDeviceTypes; + } + private DataValidator deviceValidator = new DataValidator() { @@ -249,6 +291,9 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic @Override protected void validateDataImpl(Device device) { + if (StringUtils.isEmpty(device.getType())) { + throw new DataValidationException("Device type should be specified!"); + } if (StringUtils.isEmpty(device.getName())) { throw new DataValidationException("Device name should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java index 0444d11af9..0d477c09ed 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java @@ -49,12 +49,13 @@ public final class AssetEntity implements SearchTextEntity { @Column(name = ASSET_CUSTOMER_ID_PROPERTY) private UUID customerId; - @Column(name = ASSET_NAME_PROPERTY) - private String name; - + @PartitionKey(value = 3) @Column(name = ASSET_TYPE_PROPERTY) private String type; + @Column(name = ASSET_NAME_PROPERTY) + private String name; + @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java index 69ed92cc1c..a7fe243f07 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java @@ -49,12 +49,13 @@ public final class DeviceEntity implements SearchTextEntity { @Column(name = DEVICE_CUSTOMER_ID_PROPERTY) private UUID customerId; - @Column(name = DEVICE_NAME_PROPERTY) - private String name; - + @PartitionKey(value = 3) @Column(name = DEVICE_TYPE_PROPERTY) private String type; + @Column(name = DEVICE_NAME_PROPERTY) + private String name; + @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; 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 9f78e27557..108131153a 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 @@ -124,8 +124,11 @@ public class ModelConstants { public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text"; + public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text"; public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text"; + public static final String DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_by_type_and_search_text"; public static final String DEVICE_BY_TENANT_AND_NAME_VIEW_NAME = "device_by_tenant_and_name"; + public static final String DEVICE_TYPES_BY_TENANT_VIEW_NAME = "device_types_by_tenant"; /** * Cassandra asset constants. @@ -138,8 +141,11 @@ public class ModelConstants { public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text"; + public static final String ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_by_type_and_search_text"; public static final String ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_and_search_text"; + public static final String ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_by_type_and_search_text"; public static final String ASSET_BY_TENANT_AND_NAME_VIEW_NAME = "asset_by_tenant_and_name"; + public static final String ASSET_TYPES_BY_TENANT_VIEW_NAME = "asset_types_by_tenant"; /** * Cassandra entity relation constants. diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/TenantAssetTypeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/TenantAssetTypeEntity.java new file mode 100644 index 0000000000..36361ef2b4 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/TenantAssetTypeEntity.java @@ -0,0 +1,107 @@ +/** + * Copyright © 2016-2017 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.model; + +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; +import com.datastax.driver.mapping.annotations.Transient; +import org.thingsboard.server.common.data.asset.TenantAssetType; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.*; + +@Table(name = ASSET_TYPES_BY_TENANT_VIEW_NAME) +public class TenantAssetTypeEntity { + + @Transient + private static final long serialVersionUID = -1268181161886910152L; + + @PartitionKey(value = 0) + @Column(name = ASSET_TYPE_PROPERTY) + private String type; + + @PartitionKey(value = 1) + @Column(name = ASSET_TENANT_ID_PROPERTY) + private UUID tenantId; + + public TenantAssetTypeEntity() { + super(); + } + + public TenantAssetTypeEntity(TenantAssetType tenantAssetType) { + this.type = tenantAssetType.getType(); + if (tenantAssetType.getTenantId() != null) { + this.tenantId = tenantAssetType.getTenantId().getId(); + } + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public UUID getTenantId() { + return tenantId; + } + + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + + @Override + public int hashCode() { + int result = type != null ? type.hashCode() : 0; + result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TenantAssetTypeEntity that = (TenantAssetTypeEntity) o; + + if (type != null ? !type.equals(that.type) : that.type != null) return false; + return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null; + + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TenantAssetTypeEntity{"); + sb.append("type='").append(type).append('\''); + sb.append(", tenantId=").append(tenantId); + sb.append('}'); + return sb.toString(); + } + + public TenantAssetType toTenantAssetType() { + TenantAssetType tenantAssetType = new TenantAssetType(); + tenantAssetType.setType(type); + if (tenantId != null) { + tenantAssetType.setTenantId(new TenantId(tenantId)); + } + return tenantAssetType; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/TenantDeviceTypeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/TenantDeviceTypeEntity.java new file mode 100644 index 0000000000..dad954c63f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/TenantDeviceTypeEntity.java @@ -0,0 +1,107 @@ +/** + * Copyright © 2016-2017 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.model; + +import com.datastax.driver.mapping.annotations.Column; +import com.datastax.driver.mapping.annotations.PartitionKey; +import com.datastax.driver.mapping.annotations.Table; +import com.datastax.driver.mapping.annotations.Transient; +import org.thingsboard.server.common.data.TenantDeviceType; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.*; + +@Table(name = DEVICE_TYPES_BY_TENANT_VIEW_NAME) +public class TenantDeviceTypeEntity { + + @Transient + private static final long serialVersionUID = -1268181166886910152L; + + @PartitionKey(value = 0) + @Column(name = DEVICE_TYPE_PROPERTY) + private String type; + + @PartitionKey(value = 1) + @Column(name = DEVICE_TENANT_ID_PROPERTY) + private UUID tenantId; + + public TenantDeviceTypeEntity() { + super(); + } + + public TenantDeviceTypeEntity(TenantDeviceType tenantDeviceType) { + this.type = tenantDeviceType.getType(); + if (tenantDeviceType.getTenantId() != null) { + this.tenantId = tenantDeviceType.getTenantId().getId(); + } + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public UUID getTenantId() { + return tenantId; + } + + public void setTenantId(UUID tenantId) { + this.tenantId = tenantId; + } + + @Override + public int hashCode() { + int result = type != null ? type.hashCode() : 0; + result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TenantDeviceTypeEntity that = (TenantDeviceTypeEntity) o; + + if (type != null ? !type.equals(that.type) : that.type != null) return false; + return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null; + + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TenantDeviceTypeEntity{"); + sb.append("type='").append(type).append('\''); + sb.append(", tenantId=").append(tenantId); + sb.append('}'); + return sb.toString(); + } + + public TenantDeviceType toTenantDeviceType() { + TenantDeviceType tenantDeviceType = new TenantDeviceType(); + tenantDeviceType.setType(type); + if (tenantId != null) { + tenantDeviceType.setTenantId(new TenantId(tenantId)); + } + return tenantDeviceType; + } +} diff --git a/dao/src/main/resources/demo-data.cql b/dao/src/main/resources/demo-data.cql index 023cf1b977..47d8de481c 100644 --- a/dao/src/main/resources/demo-data.cql +++ b/dao/src/main/resources/demo-data.cql @@ -149,66 +149,73 @@ VALUES ( /** Demo device **/ -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text) +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text) VALUES ( minTimeuuid ( '2016-11-01 01:02:05+0000' ), minTimeuuid ( '2016-11-01 01:02:01+0000' ), minTimeuuid ( '2016-11-01 01:02:03+0000' ), + 'default', 'Test Device A1', 'test device a1' ); -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text) +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text) VALUES ( minTimeuuid ( '2016-11-01 01:02:05+0001' ), minTimeuuid ( '2016-11-01 01:02:01+0000' ), minTimeuuid ( '2016-11-01 01:02:03+0000' ), + 'default', 'Test Device A2', 'test device a2' ); -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text) +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text) VALUES ( minTimeuuid ( '2016-11-01 01:02:05+0002' ), minTimeuuid ( '2016-11-01 01:02:01+0000' ), minTimeuuid ( '2016-11-01 01:02:03+0000' ), + 'default', 'Test Device A3', 'test device a3' ); -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text) +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text) VALUES ( minTimeuuid ( '2016-11-01 01:02:05+0003' ), minTimeuuid ( '2016-11-01 01:02:01+0000' ), minTimeuuid ( '2016-11-01 01:02:03+0001' ), + 'default', 'Test Device B1', 'test device b1' ); -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text) +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text) VALUES ( minTimeuuid ( '2016-11-01 01:02:05+0004' ), minTimeuuid ( '2016-11-01 01:02:01+0000' ), minTimeuuid ( '2016-11-01 01:02:03+0002' ), + 'default', 'Test Device C1', 'test device c1' ); -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text, additional_info) +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text, additional_info) VALUES ( c8f1a6f0-b993-11e6-8a04-9ff4e1b7933c, minTimeuuid ( '2016-11-01 01:02:01+0000' ), minTimeuuid ( 0 ), + 'default', 'DHT11 Demo Device', 'dht11 demo device', '{"description":"Demo device that is used in sample applications that upload data from DHT11 temperature and humidity sensor"}' ); -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text, additional_info) +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text, additional_info) VALUES ( c8f1a6f0-b993-11e6-8a04-9ff4e1b7933d, minTimeuuid ( '2016-11-01 01:02:01+0000' ), minTimeuuid ( 0 ), + 'default', 'Raspberry Pi Demo Device', 'raspberry pi demo device', '{"description":"Demo device that is used in Raspberry Pi GPIO control sample application"}' diff --git a/dao/src/main/resources/schema.cql b/dao/src/main/resources/schema.cql index c3e7cdae64..5ce3dc9599 100644 --- a/dao/src/main/resources/schema.cql +++ b/dao/src/main/resources/schema.cql @@ -152,36 +152,57 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.customer_by_tenant_and_search WITH CLUSTERING ORDER BY ( search_text ASC, id DESC ); CREATE TABLE IF NOT EXISTS thingsboard.device ( - id timeuuid, - tenant_id timeuuid, - customer_id timeuuid, - name text, - type text, - search_text text, - additional_info text, - PRIMARY KEY (id, tenant_id, customer_id) + id timeuuid, + tenant_id timeuuid, + customer_id timeuuid, + name text, + type text, + search_text text, + additional_info text, + PRIMARY KEY (id, tenant_id, customer_id, type) ); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_name AS - SELECT * - from thingsboard.device - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, name, id, customer_id) - WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC); + SELECT * + from thingsboard.device + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, name, id, customer_id, type) + WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_search_text AS - SELECT * - from thingsboard.device - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, search_text, id, customer_id) - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC); + SELECT * + from thingsboard.device + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, search_text, id, customer_id, type) + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_by_type_and_search_text AS + SELECT * + from thingsboard.device + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, type, search_text, id, customer_id) + WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_and_search_text AS - SELECT * - from thingsboard.device - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, search_text, id ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); + SELECT * + from thingsboard.device + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( customer_id, tenant_id, search_text, id, type ) + WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_by_type_and_search_text AS + SELECT * + from thingsboard.device + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( customer_id, tenant_id, type, search_text, id ) + WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC ); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_types_by_tenant AS + SELECT * + from thingsboard.device + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( (type, tenant_id), id, customer_id) + WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC); CREATE TABLE IF NOT EXISTS thingsboard.device_credentials ( id timeuuid PRIMARY KEY, @@ -203,38 +224,58 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_credentials_by_credent WHERE credentials_id IS NOT NULL AND id IS NOT NULL PRIMARY KEY ( credentials_id, id ); - CREATE TABLE IF NOT EXISTS thingsboard.asset ( - id timeuuid, - tenant_id timeuuid, - customer_id timeuuid, - name text, - type text, - search_text text, - additional_info text, - PRIMARY KEY (id, tenant_id, customer_id) + id timeuuid, + tenant_id timeuuid, + customer_id timeuuid, + name text, + type text, + search_text text, + additional_info text, + PRIMARY KEY (id, tenant_id, customer_id, type) ); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_name AS - SELECT * - from thingsboard.asset - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, name, id, customer_id) - WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC); + SELECT * + from thingsboard.asset + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, name, id, customer_id, type) + WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_search_text AS - SELECT * - from thingsboard.asset - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( tenant_id, search_text, id, customer_id) - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC); + SELECT * + from thingsboard.asset + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, search_text, id, customer_id, type) + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_by_type_and_search_text AS + SELECT * + from thingsboard.asset + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( tenant_id, type, search_text, id, customer_id) + WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_and_search_text AS - SELECT * - from thingsboard.asset - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL - PRIMARY KEY ( customer_id, tenant_id, search_text, id ) - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); + SELECT * + from thingsboard.asset + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( customer_id, tenant_id, search_text, id, type ) + WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC ); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_by_type_and_search_text AS + SELECT * + from thingsboard.asset + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( customer_id, tenant_id, type, search_text, id ) + WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC ); + +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_types_by_tenant AS + SELECT * + from thingsboard.asset + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL + PRIMARY KEY ( (type, tenant_id), id, customer_id) + WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC); CREATE TABLE IF NOT EXISTS thingsboard.relation ( from_id timeuuid, @@ -247,11 +288,11 @@ CREATE TABLE IF NOT EXISTS thingsboard.relation ( ); CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.reverse_relation AS -SELECT * -from thingsboard.relation -WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL -PRIMARY KEY ((to_id, to_type), relation_type, from_id, from_type) -WITH CLUSTERING ORDER BY ( relation_type ASC, from_id ASC, from_type ASC); + SELECT * + from thingsboard.relation + WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL + PRIMARY KEY ((to_id, to_type), relation_type, from_id, from_type) + WITH CLUSTERING ORDER BY ( relation_type ASC, from_id ASC, from_type ASC); CREATE TABLE IF NOT EXISTS thingsboard.widgets_bundle ( id timeuuid, 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 903207c1d5..1e03a97642 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 @@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.plugin.ComponentScope; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.PluginMetaData; import org.thingsboard.server.common.data.rule.RuleMetaData; +import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.component.ComponentDescriptorService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; @@ -87,6 +88,9 @@ public abstract class AbstractServiceTest { @Autowired protected DeviceService deviceService; + @Autowired + protected AssetService assetService; + @Autowired protected DeviceCredentialsService deviceCredentialsService; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java new file mode 100644 index 0000000000..958770397c --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java @@ -0,0 +1,634 @@ +/** + * Copyright © 2016-2017 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.driver.core.utils.UUIDs; +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.asset.TenantAssetType; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.TextPageData; +import org.thingsboard.server.common.data.page.TextPageLink; +import org.thingsboard.server.dao.exception.DataValidationException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; + +public class BaseAssetServiceTest extends AbstractServiceTest { + + 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 testSaveAsset() { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = assetService.saveAsset(asset); + + Assert.assertNotNull(savedAsset); + Assert.assertNotNull(savedAsset.getId()); + Assert.assertTrue(savedAsset.getCreatedTime() > 0); + Assert.assertEquals(asset.getTenantId(), savedAsset.getTenantId()); + Assert.assertNotNull(savedAsset.getCustomerId()); + Assert.assertEquals(NULL_UUID, savedAsset.getCustomerId().getId()); + Assert.assertEquals(asset.getName(), savedAsset.getName()); + + savedAsset.setName("My new asset"); + + assetService.saveAsset(savedAsset); + Asset foundAsset = assetService.findAssetById(savedAsset.getId()); + Assert.assertEquals(foundAsset.getName(), savedAsset.getName()); + + assetService.deleteAsset(savedAsset.getId()); + } + + @Test(expected = DataValidationException.class) + public void testSaveAssetWithEmptyName() { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setType("default"); + assetService.saveAsset(asset); + } + + @Test(expected = DataValidationException.class) + public void testSaveAssetWithEmptyTenant() { + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + assetService.saveAsset(asset); + } + + @Test(expected = DataValidationException.class) + public void testSaveAssetWithInvalidTenant() { + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + asset.setTenantId(new TenantId(UUIDs.timeBased())); + assetService.saveAsset(asset); + } + + @Test(expected = DataValidationException.class) + public void testAssignAssetToNonExistentCustomer() { + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + asset.setTenantId(tenantId); + asset = assetService.saveAsset(asset); + try { + assetService.assignAssetToCustomer(asset.getId(), new CustomerId(UUIDs.timeBased())); + } finally { + assetService.deleteAsset(asset.getId()); + } + } + + @Test(expected = DataValidationException.class) + public void testAssignAssetToCustomerFromDifferentTenant() { + Asset asset = new Asset(); + asset.setName("My asset"); + asset.setType("default"); + asset.setTenantId(tenantId); + asset = assetService.saveAsset(asset); + Tenant tenant = new Tenant(); + tenant.setTitle("Test different tenant"); + tenant = tenantService.saveTenant(tenant); + Customer customer = new Customer(); + customer.setTenantId(tenant.getId()); + customer.setTitle("Test different customer"); + customer = customerService.saveCustomer(customer); + try { + assetService.assignAssetToCustomer(asset.getId(), customer.getId()); + } finally { + assetService.deleteAsset(asset.getId()); + tenantService.deleteTenant(tenant.getId()); + } + } + + @Test + public void testFindAssetById() { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = assetService.saveAsset(asset); + Asset foundAsset = assetService.findAssetById(savedAsset.getId()); + Assert.assertNotNull(foundAsset); + Assert.assertEquals(savedAsset, foundAsset); + assetService.deleteAsset(savedAsset.getId()); + } + + @Test + public void testFindAssetTypesByTenantId() throws Exception { + List assets = new ArrayList<>(); + try { + for (int i=0;i<3;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName("My asset B"+i); + asset.setType("typeB"); + assets.add(assetService.saveAsset(asset)); + } + for (int i=0;i<7;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName("My asset C"+i); + asset.setType("typeC"); + assets.add(assetService.saveAsset(asset)); + } + for (int i=0;i<9;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName("My asset A"+i); + asset.setType("typeA"); + assets.add(assetService.saveAsset(asset)); + } + List assetTypes = assetService.findAssetTypesByTenantId(tenantId).get(); + Assert.assertNotNull(assetTypes); + Assert.assertEquals(3, assetTypes.size()); + Assert.assertEquals("typeA", assetTypes.get(0).getType()); + Assert.assertEquals("typeB", assetTypes.get(1).getType()); + Assert.assertEquals("typeC", assetTypes.get(2).getType()); + } finally { + assets.forEach((asset) -> { assetService.deleteAsset(asset.getId()); }); + } + } + + @Test + public void testDeleteAsset() { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName("My asset"); + asset.setType("default"); + Asset savedAsset = assetService.saveAsset(asset); + Asset foundAsset = assetService.findAssetById(savedAsset.getId()); + Assert.assertNotNull(foundAsset); + assetService.deleteAsset(savedAsset.getId()); + foundAsset = assetService.findAssetById(savedAsset.getId()); + Assert.assertNull(foundAsset); + } + + @Test + public void testFindAssetsByTenantId() { + Tenant tenant = new Tenant(); + tenant.setTitle("Test tenant"); + tenant = tenantService.saveTenant(tenant); + + TenantId tenantId = tenant.getId(); + + List assets = new ArrayList<>(); + for (int i=0;i<178;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName("Asset"+i); + asset.setType("default"); + assets.add(assetService.saveAsset(asset)); + } + + List loadedAssets = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(23); + TextPageData pageData = null; + do { + pageData = assetService.findAssetsByTenantId(tenantId, pageLink); + loadedAssets.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assets, idComparator); + Collections.sort(loadedAssets, idComparator); + + Assert.assertEquals(assets, loadedAssets); + + assetService.deleteAssetsByTenantId(tenantId); + + pageLink = new TextPageLink(33); + pageData = assetService.findAssetsByTenantId(tenantId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertTrue(pageData.getData().isEmpty()); + + tenantService.deleteTenant(tenantId); + } + + @Test + public void testFindAssetsByTenantIdAndName() { + String title1 = "Asset title 1"; + List assetsTitle1 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType("default"); + assetsTitle1.add(assetService.saveAsset(asset)); + } + String title2 = "Asset title 2"; + List assetsTitle2 = new ArrayList<>(); + for (int i=0;i<175;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType("default"); + assetsTitle2.add(assetService.saveAsset(asset)); + } + + List loadedAssetsTitle1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15, title1); + TextPageData pageData = null; + do { + pageData = assetService.findAssetsByTenantId(tenantId, pageLink); + loadedAssetsTitle1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsTitle1, idComparator); + Collections.sort(loadedAssetsTitle1, idComparator); + + Assert.assertEquals(assetsTitle1, loadedAssetsTitle1); + + List loadedAssetsTitle2 = new ArrayList<>(); + pageLink = new TextPageLink(4, title2); + do { + pageData = assetService.findAssetsByTenantId(tenantId, pageLink); + loadedAssetsTitle2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsTitle2, idComparator); + Collections.sort(loadedAssetsTitle2, idComparator); + + Assert.assertEquals(assetsTitle2, loadedAssetsTitle2); + + for (Asset asset : loadedAssetsTitle1) { + assetService.deleteAsset(asset.getId()); + } + + pageLink = new TextPageLink(4, title1); + pageData = assetService.findAssetsByTenantId(tenantId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Asset asset : loadedAssetsTitle2) { + assetService.deleteAsset(asset.getId()); + } + + pageLink = new TextPageLink(4, title2); + pageData = assetService.findAssetsByTenantId(tenantId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + } + + @Test + public void testFindAssetsByTenantIdAndType() { + String title1 = "Asset title 1"; + String type1 = "typeA"; + List assetsType1 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType(type1); + assetsType1.add(assetService.saveAsset(asset)); + } + String title2 = "Asset title 2"; + String type2 = "typeB"; + List assetsType2 = new ArrayList<>(); + for (int i=0;i<175;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType(type2); + assetsType2.add(assetService.saveAsset(asset)); + } + + List loadedAssetsType1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15); + TextPageData pageData = null; + do { + pageData = assetService.findAssetsByTenantIdAndType(tenantId, type1, pageLink); + loadedAssetsType1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsType1, idComparator); + Collections.sort(loadedAssetsType1, idComparator); + + Assert.assertEquals(assetsType1, loadedAssetsType1); + + List loadedAssetsType2 = new ArrayList<>(); + pageLink = new TextPageLink(4); + do { + pageData = assetService.findAssetsByTenantIdAndType(tenantId, type2, pageLink); + loadedAssetsType2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsType2, idComparator); + Collections.sort(loadedAssetsType2, idComparator); + + Assert.assertEquals(assetsType2, loadedAssetsType2); + + for (Asset asset : loadedAssetsType1) { + assetService.deleteAsset(asset.getId()); + } + + pageLink = new TextPageLink(4); + pageData = assetService.findAssetsByTenantIdAndType(tenantId, type1, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Asset asset : loadedAssetsType2) { + assetService.deleteAsset(asset.getId()); + } + + pageLink = new TextPageLink(4); + pageData = assetService.findAssetsByTenantIdAndType(tenantId, type2, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + } + + @Test + public void testFindAssetsByTenantIdAndCustomerId() { + Tenant tenant = new Tenant(); + tenant.setTitle("Test tenant"); + tenant = tenantService.saveTenant(tenant); + + TenantId tenantId = tenant.getId(); + + Customer customer = new Customer(); + customer.setTitle("Test customer"); + customer.setTenantId(tenantId); + customer = customerService.saveCustomer(customer); + CustomerId customerId = customer.getId(); + + List assets = new ArrayList<>(); + for (int i=0;i<278;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setName("Asset"+i); + asset.setType("default"); + asset = assetService.saveAsset(asset); + assets.add(assetService.assignAssetToCustomer(asset.getId(), customerId)); + } + + List loadedAssets = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(23); + TextPageData pageData = null; + do { + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink); + loadedAssets.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assets, idComparator); + Collections.sort(loadedAssets, idComparator); + + Assert.assertEquals(assets, loadedAssets); + + assetService.unassignCustomerAssets(tenantId, customerId); + + pageLink = new TextPageLink(33); + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertTrue(pageData.getData().isEmpty()); + + tenantService.deleteTenant(tenantId); + } + + @Test + public void testFindAssetsByTenantIdCustomerIdAndName() { + + Customer customer = new Customer(); + customer.setTitle("Test customer"); + customer.setTenantId(tenantId); + customer = customerService.saveCustomer(customer); + CustomerId customerId = customer.getId(); + + String title1 = "Asset title 1"; + List assetsTitle1 = new ArrayList<>(); + for (int i=0;i<175;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType("default"); + asset = assetService.saveAsset(asset); + assetsTitle1.add(assetService.assignAssetToCustomer(asset.getId(), customerId)); + } + String title2 = "Asset title 2"; + List assetsTitle2 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType("default"); + asset = assetService.saveAsset(asset); + assetsTitle2.add(assetService.assignAssetToCustomer(asset.getId(), customerId)); + } + + List loadedAssetsTitle1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15, title1); + TextPageData pageData = null; + do { + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink); + loadedAssetsTitle1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsTitle1, idComparator); + Collections.sort(loadedAssetsTitle1, idComparator); + + Assert.assertEquals(assetsTitle1, loadedAssetsTitle1); + + List loadedAssetsTitle2 = new ArrayList<>(); + pageLink = new TextPageLink(4, title2); + do { + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink); + loadedAssetsTitle2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsTitle2, idComparator); + Collections.sort(loadedAssetsTitle2, idComparator); + + Assert.assertEquals(assetsTitle2, loadedAssetsTitle2); + + for (Asset asset : loadedAssetsTitle1) { + assetService.deleteAsset(asset.getId()); + } + + pageLink = new TextPageLink(4, title1); + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Asset asset : loadedAssetsTitle2) { + assetService.deleteAsset(asset.getId()); + } + + pageLink = new TextPageLink(4, title2); + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + customerService.deleteCustomer(customerId); + } + + @Test + public void testFindAssetsByTenantIdCustomerIdAndType() { + + Customer customer = new Customer(); + customer.setTitle("Test customer"); + customer.setTenantId(tenantId); + customer = customerService.saveCustomer(customer); + CustomerId customerId = customer.getId(); + + String title1 = "Asset title 1"; + String type1 = "typeC"; + List assetsType1 = new ArrayList<>(); + for (int i=0;i<175;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType(type1); + asset = assetService.saveAsset(asset); + assetsType1.add(assetService.assignAssetToCustomer(asset.getId(), customerId)); + } + String title2 = "Asset title 2"; + String type2 = "typeD"; + List assetsType2 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + asset.setName(name); + asset.setType(type2); + asset = assetService.saveAsset(asset); + assetsType2.add(assetService.assignAssetToCustomer(asset.getId(), customerId)); + } + + List loadedAssetsType1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15); + TextPageData pageData = null; + do { + pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink); + loadedAssetsType1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsType1, idComparator); + Collections.sort(loadedAssetsType1, idComparator); + + Assert.assertEquals(assetsType1, loadedAssetsType1); + + List loadedAssetsType2 = new ArrayList<>(); + pageLink = new TextPageLink(4); + do { + pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink); + loadedAssetsType2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(assetsType2, idComparator); + Collections.sort(loadedAssetsType2, idComparator); + + Assert.assertEquals(assetsType2, loadedAssetsType2); + + for (Asset asset : loadedAssetsType1) { + assetService.deleteAsset(asset.getId()); + } + + pageLink = new TextPageLink(4); + pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Asset asset : loadedAssetsType2) { + assetService.deleteAsset(asset.getId()); + } + + pageLink = new TextPageLink(4); + pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + customerService.deleteCustomer(customerId); + } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceImplTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceImplTest.java index efdab8a08a..4d9ef9f43d 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceImplTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceImplTest.java @@ -58,6 +58,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest { public void testSaveDeviceCredentialsWithEmptyDevice() { Device device = new Device(); device.setName("My device"); + device.setType("default"); device.setTenantId(tenantId); device = deviceService.saveDevice(device); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId()); @@ -73,6 +74,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest { public void testSaveDeviceCredentialsWithEmptyCredentialsType() { Device device = new Device(); device.setName("My device"); + device.setType("default"); device.setTenantId(tenantId); device = deviceService.saveDevice(device); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId()); @@ -88,6 +90,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest { public void testSaveDeviceCredentialsWithEmptyCredentialsId() { Device device = new Device(); device.setName("My device"); + device.setType("default"); device.setTenantId(tenantId); device = deviceService.saveDevice(device); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId()); @@ -103,6 +106,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest { public void testSaveNonExistentDeviceCredentials() { Device device = new Device(); device.setName("My device"); + device.setType("default"); device.setTenantId(tenantId); device = deviceService.saveDevice(device); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId()); @@ -122,6 +126,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest { public void testSaveDeviceCredentialsWithNonExistentDevice() { Device device = new Device(); device.setName("My device"); + device.setType("default"); device.setTenantId(tenantId); device = deviceService.saveDevice(device); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId()); @@ -137,6 +142,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest { public void testSaveDeviceCredentialsWithInvalidCredemtialsIdLength() { Device device = new Device(); device.setName("My device"); + device.setType("default"); device.setTenantId(tenantId); device = deviceService.saveDevice(device); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId()); @@ -153,6 +159,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest { Device device = new Device(); device.setTenantId(tenantId); device.setName("My device"); + device.setType("default"); Device savedDevice = deviceService.saveDevice(device); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId()); Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); @@ -166,6 +173,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest { Device device = new Device(); device.setTenantId(tenantId); device.setName("My device"); + device.setType("default"); Device savedDevice = deviceService.saveDevice(device); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId()); Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); @@ -181,6 +189,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest { Device device = new Device(); device.setTenantId(tenantId); device.setName("My device"); + device.setType("default"); Device savedDevice = deviceService.saveDevice(device); DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId()); Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceImplTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceImplTest.java index e9113d447b..4f0bc8e1fa 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceImplTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceImplTest.java @@ -24,6 +24,7 @@ import org.junit.Test; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantDeviceType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceCredentialsId; import org.thingsboard.server.common.data.id.DeviceId; @@ -37,6 +38,7 @@ import org.thingsboard.server.dao.exception.DataValidationException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.Executors; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -65,6 +67,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { Device device = new Device(); device.setTenantId(tenantId); device.setName("My device"); + device.setType("default"); Device savedDevice = deviceService.saveDevice(device); Assert.assertNotNull(savedDevice); @@ -95,6 +98,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { @Test(expected = DataValidationException.class) public void testSaveDeviceWithEmptyName() { Device device = new Device(); + device.setType("default"); device.setTenantId(tenantId); deviceService.saveDevice(device); } @@ -103,6 +107,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { public void testSaveDeviceWithEmptyTenant() { Device device = new Device(); device.setName("My device"); + device.setType("default"); deviceService.saveDevice(device); } @@ -110,6 +115,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { public void testSaveDeviceWithInvalidTenant() { Device device = new Device(); device.setName("My device"); + device.setType("default"); device.setTenantId(new TenantId(UUIDs.timeBased())); deviceService.saveDevice(device); } @@ -118,6 +124,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { public void testAssignDeviceToNonExistentCustomer() { Device device = new Device(); device.setName("My device"); + device.setType("default"); device.setTenantId(tenantId); device = deviceService.saveDevice(device); try { @@ -131,6 +138,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { public void testAssignDeviceToCustomerFromDifferentTenant() { Device device = new Device(); device.setName("My device"); + device.setType("default"); device.setTenantId(tenantId); device = deviceService.saveDevice(device); Tenant tenant = new Tenant(); @@ -153,18 +161,56 @@ public class DeviceServiceImplTest extends AbstractServiceTest { Device device = new Device(); device.setTenantId(tenantId); device.setName("My device"); + device.setType("default"); Device savedDevice = deviceService.saveDevice(device); Device foundDevice = deviceService.findDeviceById(savedDevice.getId()); Assert.assertNotNull(foundDevice); Assert.assertEquals(savedDevice, foundDevice); deviceService.deleteDevice(savedDevice.getId()); } + + @Test + public void testFindDeviceTypesByTenantId() throws Exception { + List devices = new ArrayList<>(); + try { + for (int i=0;i<3;i++) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setName("My device B"+i); + device.setType("typeB"); + devices.add(deviceService.saveDevice(device)); + } + for (int i=0;i<7;i++) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setName("My device C"+i); + device.setType("typeC"); + devices.add(deviceService.saveDevice(device)); + } + for (int i=0;i<9;i++) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setName("My device A"+i); + device.setType("typeA"); + devices.add(deviceService.saveDevice(device)); + } + List deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId).get(); + Assert.assertNotNull(deviceTypes); + Assert.assertEquals(3, deviceTypes.size()); + Assert.assertEquals("typeA", deviceTypes.get(0).getType()); + Assert.assertEquals("typeB", deviceTypes.get(1).getType()); + Assert.assertEquals("typeC", deviceTypes.get(2).getType()); + } finally { + devices.forEach((device) -> { deviceService.deleteDevice(device.getId()); }); + } + } @Test public void testDeleteDevice() { Device device = new Device(); device.setTenantId(tenantId); device.setName("My device"); + device.setType("default"); Device savedDevice = deviceService.saveDevice(device); Device foundDevice = deviceService.findDeviceById(savedDevice.getId()); Assert.assertNotNull(foundDevice); @@ -188,6 +234,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { Device device = new Device(); device.setTenantId(tenantId); device.setName("Device"+i); + device.setType("default"); devices.add(deviceService.saveDevice(device)); } @@ -216,7 +263,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { tenantService.deleteTenant(tenantId); } - + @Test public void testFindDevicesByTenantIdAndName() { String title1 = "Device title 1"; @@ -228,6 +275,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { String name = title1+suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); + device.setType("default"); devicesTitle1.add(deviceService.saveDevice(device)); } String title2 = "Device title 2"; @@ -239,6 +287,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { String name = title2+suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); + device.setType("default"); devicesTitle2.add(deviceService.saveDevice(device)); } @@ -291,6 +340,85 @@ public class DeviceServiceImplTest extends AbstractServiceTest { Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } + + @Test + public void testFindDevicesByTenantIdAndType() { + String title1 = "Device title 1"; + String type1 = "typeA"; + List devicesType1 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Device device = new Device(); + device.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + device.setName(name); + device.setType(type1); + devicesType1.add(deviceService.saveDevice(device)); + } + String title2 = "Device title 2"; + String type2 = "typeB"; + List devicesType2 = new ArrayList<>(); + for (int i=0;i<175;i++) { + Device device = new Device(); + device.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + device.setName(name); + device.setType(type2); + devicesType2.add(deviceService.saveDevice(device)); + } + + List loadedDevicesType1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15); + TextPageData pageData = null; + do { + pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type1, pageLink); + loadedDevicesType1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(devicesType1, idComparator); + Collections.sort(loadedDevicesType1, idComparator); + + Assert.assertEquals(devicesType1, loadedDevicesType1); + + List loadedDevicesType2 = new ArrayList<>(); + pageLink = new TextPageLink(4); + do { + pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type2, pageLink); + loadedDevicesType2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(devicesType2, idComparator); + Collections.sort(loadedDevicesType2, idComparator); + + Assert.assertEquals(devicesType2, loadedDevicesType2); + + for (Device device : loadedDevicesType1) { + deviceService.deleteDevice(device.getId()); + } + + pageLink = new TextPageLink(4); + pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type1, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Device device : loadedDevicesType2) { + deviceService.deleteDevice(device.getId()); + } + + pageLink = new TextPageLink(4); + pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type2, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + } @Test public void testFindDevicesByTenantIdAndCustomerId() { @@ -311,6 +439,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { Device device = new Device(); device.setTenantId(tenantId); device.setName("Device"+i); + device.setType("default"); device = deviceService.saveDevice(device); devices.add(deviceService.assignDeviceToCustomer(device.getId(), customerId)); } @@ -359,6 +488,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { String name = title1+suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); + device.setType("default"); device = deviceService.saveDevice(device); devicesTitle1.add(deviceService.assignDeviceToCustomer(device.getId(), customerId)); } @@ -371,6 +501,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest { String name = title2+suffix; name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); + device.setType("default"); device = deviceService.saveDevice(device); devicesTitle2.add(deviceService.assignDeviceToCustomer(device.getId(), customerId)); } @@ -425,4 +556,94 @@ public class DeviceServiceImplTest extends AbstractServiceTest { Assert.assertEquals(0, pageData.getData().size()); customerService.deleteCustomer(customerId); } + + @Test + public void testFindDevicesByTenantIdCustomerIdAndType() { + + Customer customer = new Customer(); + customer.setTitle("Test customer"); + customer.setTenantId(tenantId); + customer = customerService.saveCustomer(customer); + CustomerId customerId = customer.getId(); + + String title1 = "Device title 1"; + String type1 = "typeC"; + List devicesType1 = new ArrayList<>(); + for (int i=0;i<175;i++) { + Device device = new Device(); + device.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title1+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + device.setName(name); + device.setType(type1); + device = deviceService.saveDevice(device); + devicesType1.add(deviceService.assignDeviceToCustomer(device.getId(), customerId)); + } + String title2 = "Device title 2"; + String type2 = "typeD"; + List devicesType2 = new ArrayList<>(); + for (int i=0;i<143;i++) { + Device device = new Device(); + device.setTenantId(tenantId); + String suffix = RandomStringUtils.randomAlphanumeric(15); + String name = title2+suffix; + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); + device.setName(name); + device.setType(type2); + device = deviceService.saveDevice(device); + devicesType2.add(deviceService.assignDeviceToCustomer(device.getId(), customerId)); + } + + List loadedDevicesType1 = new ArrayList<>(); + TextPageLink pageLink = new TextPageLink(15); + TextPageData pageData = null; + do { + pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink); + loadedDevicesType1.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(devicesType1, idComparator); + Collections.sort(loadedDevicesType1, idComparator); + + Assert.assertEquals(devicesType1, loadedDevicesType1); + + List loadedDevicesType2 = new ArrayList<>(); + pageLink = new TextPageLink(4); + do { + pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink); + loadedDevicesType2.addAll(pageData.getData()); + if (pageData.hasNext()) { + pageLink = pageData.getNextPageLink(); + } + } while (pageData.hasNext()); + + Collections.sort(devicesType2, idComparator); + Collections.sort(loadedDevicesType2, idComparator); + + Assert.assertEquals(devicesType2, loadedDevicesType2); + + for (Device device : loadedDevicesType1) { + deviceService.deleteDevice(device.getId()); + } + + pageLink = new TextPageLink(4); + pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + + for (Device device : loadedDevicesType2) { + deviceService.deleteDevice(device.getId()); + } + + pageLink = new TextPageLink(4); + pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink); + Assert.assertFalse(pageData.hasNext()); + Assert.assertEquals(0, pageData.getData().size()); + customerService.deleteCustomer(customerId); + } + }