Commit d6ad8c9e55eb4b3343ad9640775878c5526487ad

Authored by Andrew Shvayka
2 parents 2435ca5d 90ef91e3

Merge with master

Showing 47 changed files with 2688 additions and 1072 deletions

Too many changes to show.

To preserve performance only 47 of 94 files are displayed.

... ... @@ -21,6 +21,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
21 21 import org.springframework.web.bind.annotation.*;
22 22 import org.thingsboard.server.common.data.Customer;
23 23 import org.thingsboard.server.common.data.asset.Asset;
  24 +import org.thingsboard.server.common.data.asset.TenantAssetType;
24 25 import org.thingsboard.server.common.data.id.AssetId;
25 26 import org.thingsboard.server.common.data.id.CustomerId;
26 27 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -136,13 +137,18 @@ public class AssetController extends BaseController {
136 137 @ResponseBody
137 138 public TextPageData<Asset> getTenantAssets(
138 139 @RequestParam int limit,
  140 + @RequestParam(required = false) String type,
139 141 @RequestParam(required = false) String textSearch,
140 142 @RequestParam(required = false) String idOffset,
141 143 @RequestParam(required = false) String textOffset) throws ThingsboardException {
142 144 try {
143 145 TenantId tenantId = getCurrentUser().getTenantId();
144 146 TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
145   - return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
  147 + if (type != null && type.trim().length()>0) {
  148 + return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink));
  149 + } else {
  150 + return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
  151 + }
146 152 } catch (Exception e) {
147 153 throw handleException(e);
148 154 }
... ... @@ -167,6 +173,7 @@ public class AssetController extends BaseController {
167 173 public TextPageData<Asset> getCustomerAssets(
168 174 @PathVariable("customerId") String strCustomerId,
169 175 @RequestParam int limit,
  176 + @RequestParam(required = false) String type,
170 177 @RequestParam(required = false) String textSearch,
171 178 @RequestParam(required = false) String idOffset,
172 179 @RequestParam(required = false) String textOffset) throws ThingsboardException {
... ... @@ -176,7 +183,11 @@ public class AssetController extends BaseController {
176 183 CustomerId customerId = new CustomerId(toUUID(strCustomerId));
177 184 checkCustomerId(customerId);
178 185 TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
179   - return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
  186 + if (type != null && type.trim().length()>0) {
  187 + return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
  188 + } else {
  189 + return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
  190 + }
180 191 } catch (Exception e) {
181 192 throw handleException(e);
182 193 }
... ... @@ -231,4 +242,18 @@ public class AssetController extends BaseController {
231 242 throw handleException(e);
232 243 }
233 244 }
  245 +
  246 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  247 + @RequestMapping(value = "/asset/types", method = RequestMethod.GET)
  248 + @ResponseBody
  249 + public List<TenantAssetType> getAssetTypes() throws ThingsboardException {
  250 + try {
  251 + SecurityUser user = getCurrentUser();
  252 + TenantId tenantId = user.getTenantId();
  253 + ListenableFuture<List<TenantAssetType>> assetTypes = assetService.findAssetTypesByTenantId(tenantId);
  254 + return checkNotNull(assetTypes.get());
  255 + } catch (Exception e) {
  256 + throw handleException(e);
  257 + }
  258 + }
234 259 }
... ...
... ... @@ -21,6 +21,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
21 21 import org.springframework.web.bind.annotation.*;
22 22 import org.thingsboard.server.common.data.Customer;
23 23 import org.thingsboard.server.common.data.Device;
  24 +import org.thingsboard.server.common.data.TenantDeviceType;
24 25 import org.thingsboard.server.common.data.id.CustomerId;
25 26 import org.thingsboard.server.common.data.id.DeviceId;
26 27 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -166,13 +167,18 @@ public class DeviceController extends BaseController {
166 167 @ResponseBody
167 168 public TextPageData<Device> getTenantDevices(
168 169 @RequestParam int limit,
  170 + @RequestParam(required = false) String type,
169 171 @RequestParam(required = false) String textSearch,
170 172 @RequestParam(required = false) String idOffset,
171 173 @RequestParam(required = false) String textOffset) throws ThingsboardException {
172 174 try {
173 175 TenantId tenantId = getCurrentUser().getTenantId();
174 176 TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
175   - return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink));
  177 + if (type != null && type.trim().length()>0) {
  178 + return checkNotNull(deviceService.findDevicesByTenantIdAndType(tenantId, type, pageLink));
  179 + } else {
  180 + return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink));
  181 + }
176 182 } catch (Exception e) {
177 183 throw handleException(e);
178 184 }
... ... @@ -197,6 +203,7 @@ public class DeviceController extends BaseController {
197 203 public TextPageData<Device> getCustomerDevices(
198 204 @PathVariable("customerId") String strCustomerId,
199 205 @RequestParam int limit,
  206 + @RequestParam(required = false) String type,
200 207 @RequestParam(required = false) String textSearch,
201 208 @RequestParam(required = false) String idOffset,
202 209 @RequestParam(required = false) String textOffset) throws ThingsboardException {
... ... @@ -206,7 +213,11 @@ public class DeviceController extends BaseController {
206 213 CustomerId customerId = new CustomerId(toUUID(strCustomerId));
207 214 checkCustomerId(customerId);
208 215 TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
209   - return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink));
  216 + if (type != null && type.trim().length()>0) {
  217 + return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
  218 + } else {
  219 + return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink));
  220 + }
210 221 } catch (Exception e) {
211 222 throw handleException(e);
212 223 }
... ... @@ -261,4 +272,19 @@ public class DeviceController extends BaseController {
261 272 throw handleException(e);
262 273 }
263 274 }
  275 +
  276 + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
  277 + @RequestMapping(value = "/device/types", method = RequestMethod.GET)
  278 + @ResponseBody
  279 + public List<TenantDeviceType> getDeviceTypes() throws ThingsboardException {
  280 + try {
  281 + SecurityUser user = getCurrentUser();
  282 + TenantId tenantId = user.getTenantId();
  283 + ListenableFuture<List<TenantDeviceType>> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId);
  284 + return checkNotNull(deviceTypes.get());
  285 + } catch (Exception e) {
  286 + throw handleException(e);
  287 + }
  288 + }
  289 +
264 290 }
... ...
... ... @@ -98,13 +98,15 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppC
98 98 @IntegrationTest("server.port:0")
99 99 public abstract class AbstractControllerTest {
100 100
  101 + protected static final String TEST_TENANT_NAME = "TEST TENANT";
  102 +
101 103 protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org";
102 104 private static final String SYS_ADMIN_PASSWORD = "sysadmin";
103 105
104   - protected static final String TENANT_ADMIN_EMAIL = "tenant@thingsboard.org";
  106 + protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org";
105 107 private static final String TENANT_ADMIN_PASSWORD = "tenant";
106 108
107   - protected static final String CUSTOMER_USER_EMAIL = "customer@thingsboard.org";
  109 + protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
108 110 private static final String CUSTOMER_USER_PASSWORD = "customer";
109 111
110 112 protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
... ... @@ -147,7 +149,7 @@ public abstract class AbstractControllerTest {
147 149 loginSysAdmin();
148 150
149 151 Tenant tenant = new Tenant();
150   - tenant.setTitle("Tenant");
  152 + tenant.setTitle(TEST_TENANT_NAME);
151 153 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
152 154 Assert.assertNotNull(savedTenant);
153 155 tenantId = savedTenant.getId();
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.controller;
  17 +
  18 +import static org.hamcrest.Matchers.containsString;
  19 +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
  20 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  21 +
  22 +import java.util.ArrayList;
  23 +import java.util.Collections;
  24 +import java.util.List;
  25 +
  26 +import org.apache.commons.lang3.RandomStringUtils;
  27 +import org.thingsboard.server.common.data.*;
  28 +import org.thingsboard.server.common.data.asset.Asset;
  29 +import org.thingsboard.server.common.data.asset.TenantAssetType;
  30 +import org.thingsboard.server.common.data.id.CustomerId;
  31 +import org.thingsboard.server.common.data.id.AssetId;
  32 +import org.thingsboard.server.common.data.page.TextPageData;
  33 +import org.thingsboard.server.common.data.page.TextPageLink;
  34 +import org.thingsboard.server.common.data.security.Authority;
  35 +import org.thingsboard.server.dao.model.ModelConstants;
  36 +import org.junit.After;
  37 +import org.junit.Assert;
  38 +import org.junit.Before;
  39 +import org.junit.Test;
  40 +
  41 +import com.datastax.driver.core.utils.UUIDs;
  42 +import com.fasterxml.jackson.core.type.TypeReference;
  43 +
  44 +public class AssetControllerTest extends AbstractControllerTest {
  45 +
  46 + private IdComparator<Asset> idComparator = new IdComparator<>();
  47 +
  48 + private Tenant savedTenant;
  49 + private User tenantAdmin;
  50 +
  51 + @Before
  52 + public void beforeTest() throws Exception {
  53 + loginSysAdmin();
  54 +
  55 + Tenant tenant = new Tenant();
  56 + tenant.setTitle("My tenant");
  57 + savedTenant = doPost("/api/tenant", tenant, Tenant.class);
  58 + Assert.assertNotNull(savedTenant);
  59 +
  60 + tenantAdmin = new User();
  61 + tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
  62 + tenantAdmin.setTenantId(savedTenant.getId());
  63 + tenantAdmin.setEmail("tenant2@thingsboard.org");
  64 + tenantAdmin.setFirstName("Joe");
  65 + tenantAdmin.setLastName("Downs");
  66 +
  67 + tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
  68 + }
  69 +
  70 + @After
  71 + public void afterTest() throws Exception {
  72 + loginSysAdmin();
  73 +
  74 + doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
  75 + .andExpect(status().isOk());
  76 + }
  77 +
  78 + @Test
  79 + public void testSaveAsset() throws Exception {
  80 + Asset asset = new Asset();
  81 + asset.setName("My asset");
  82 + asset.setType("default");
  83 + Asset savedAsset = doPost("/api/asset", asset, Asset.class);
  84 +
  85 + Assert.assertNotNull(savedAsset);
  86 + Assert.assertNotNull(savedAsset.getId());
  87 + Assert.assertTrue(savedAsset.getCreatedTime() > 0);
  88 + Assert.assertEquals(savedTenant.getId(), savedAsset.getTenantId());
  89 + Assert.assertNotNull(savedAsset.getCustomerId());
  90 + Assert.assertEquals(NULL_UUID, savedAsset.getCustomerId().getId());
  91 + Assert.assertEquals(asset.getName(), savedAsset.getName());
  92 +
  93 + savedAsset.setName("My new asset");
  94 + doPost("/api/asset", savedAsset, Asset.class);
  95 +
  96 + Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
  97 + Assert.assertEquals(foundAsset.getName(), savedAsset.getName());
  98 + }
  99 +
  100 + @Test
  101 + public void testFindAssetById() throws Exception {
  102 + Asset asset = new Asset();
  103 + asset.setName("My asset");
  104 + asset.setType("default");
  105 + Asset savedAsset = doPost("/api/asset", asset, Asset.class);
  106 + Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
  107 + Assert.assertNotNull(foundAsset);
  108 + Assert.assertEquals(savedAsset, foundAsset);
  109 + }
  110 +
  111 + @Test
  112 + public void testFindAssetTypesByTenantId() throws Exception {
  113 + List<Asset> assets = new ArrayList<>();
  114 + for (int i=0;i<3;i++) {
  115 + Asset asset = new Asset();
  116 + asset.setName("My asset B"+i);
  117 + asset.setType("typeB");
  118 + assets.add(doPost("/api/asset", asset, Asset.class));
  119 + }
  120 + for (int i=0;i<7;i++) {
  121 + Asset asset = new Asset();
  122 + asset.setName("My asset C"+i);
  123 + asset.setType("typeC");
  124 + assets.add(doPost("/api/asset", asset, Asset.class));
  125 + }
  126 + for (int i=0;i<9;i++) {
  127 + Asset asset = new Asset();
  128 + asset.setName("My asset A"+i);
  129 + asset.setType("typeA");
  130 + assets.add(doPost("/api/asset", asset, Asset.class));
  131 + }
  132 + List<TenantAssetType> assetTypes = doGetTyped("/api/asset/types",
  133 + new TypeReference<List<TenantAssetType>>(){});
  134 +
  135 + Assert.assertNotNull(assetTypes);
  136 + Assert.assertEquals(3, assetTypes.size());
  137 + Assert.assertEquals("typeA", assetTypes.get(0).getType());
  138 + Assert.assertEquals("typeB", assetTypes.get(1).getType());
  139 + Assert.assertEquals("typeC", assetTypes.get(2).getType());
  140 + }
  141 +
  142 + @Test
  143 + public void testDeleteAsset() throws Exception {
  144 + Asset asset = new Asset();
  145 + asset.setName("My asset");
  146 + asset.setType("default");
  147 + Asset savedAsset = doPost("/api/asset", asset, Asset.class);
  148 +
  149 + doDelete("/api/asset/"+savedAsset.getId().getId().toString())
  150 + .andExpect(status().isOk());
  151 +
  152 + doGet("/api/asset/"+savedAsset.getId().getId().toString())
  153 + .andExpect(status().isNotFound());
  154 + }
  155 +
  156 + @Test
  157 + public void testSaveAssetWithEmptyType() throws Exception {
  158 + Asset asset = new Asset();
  159 + asset.setName("My asset");
  160 + doPost("/api/asset", asset)
  161 + .andExpect(status().isBadRequest())
  162 + .andExpect(statusReason(containsString("Asset type should be specified")));
  163 + }
  164 +
  165 + @Test
  166 + public void testSaveAssetWithEmptyName() throws Exception {
  167 + Asset asset = new Asset();
  168 + asset.setType("default");
  169 + doPost("/api/asset", asset)
  170 + .andExpect(status().isBadRequest())
  171 + .andExpect(statusReason(containsString("Asset name should be specified")));
  172 + }
  173 +
  174 + @Test
  175 + public void testAssignUnassignAssetToCustomer() throws Exception {
  176 + Asset asset = new Asset();
  177 + asset.setName("My asset");
  178 + asset.setType("default");
  179 + Asset savedAsset = doPost("/api/asset", asset, Asset.class);
  180 +
  181 + Customer customer = new Customer();
  182 + customer.setTitle("My customer");
  183 + Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
  184 +
  185 + Asset assignedAsset = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
  186 + + "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
  187 + Assert.assertEquals(savedCustomer.getId(), assignedAsset.getCustomerId());
  188 +
  189 + Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
  190 + Assert.assertEquals(savedCustomer.getId(), foundAsset.getCustomerId());
  191 +
  192 + Asset unassignedAsset =
  193 + doDelete("/api/customer/asset/" + savedAsset.getId().getId().toString(), Asset.class);
  194 + Assert.assertEquals(ModelConstants.NULL_UUID, unassignedAsset.getCustomerId().getId());
  195 +
  196 + foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
  197 + Assert.assertEquals(ModelConstants.NULL_UUID, foundAsset.getCustomerId().getId());
  198 + }
  199 +
  200 + @Test
  201 + public void testAssignAssetToNonExistentCustomer() throws Exception {
  202 + Asset asset = new Asset();
  203 + asset.setName("My asset");
  204 + asset.setType("default");
  205 + Asset savedAsset = doPost("/api/asset", asset, Asset.class);
  206 +
  207 + doPost("/api/customer/" + UUIDs.timeBased().toString()
  208 + + "/asset/" + savedAsset.getId().getId().toString())
  209 + .andExpect(status().isNotFound());
  210 + }
  211 +
  212 + @Test
  213 + public void testAssignAssetToCustomerFromDifferentTenant() throws Exception {
  214 + loginSysAdmin();
  215 +
  216 + Tenant tenant2 = new Tenant();
  217 + tenant2.setTitle("Different tenant");
  218 + Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class);
  219 + Assert.assertNotNull(savedTenant2);
  220 +
  221 + User tenantAdmin2 = new User();
  222 + tenantAdmin2.setAuthority(Authority.TENANT_ADMIN);
  223 + tenantAdmin2.setTenantId(savedTenant2.getId());
  224 + tenantAdmin2.setEmail("tenant3@thingsboard.org");
  225 + tenantAdmin2.setFirstName("Joe");
  226 + tenantAdmin2.setLastName("Downs");
  227 +
  228 + tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1");
  229 +
  230 + Customer customer = new Customer();
  231 + customer.setTitle("Different customer");
  232 + Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
  233 +
  234 + login(tenantAdmin.getEmail(), "testPassword1");
  235 +
  236 + Asset asset = new Asset();
  237 + asset.setName("My asset");
  238 + asset.setType("default");
  239 + Asset savedAsset = doPost("/api/asset", asset, Asset.class);
  240 +
  241 + doPost("/api/customer/" + savedCustomer.getId().getId().toString()
  242 + + "/asset/" + savedAsset.getId().getId().toString())
  243 + .andExpect(status().isForbidden());
  244 +
  245 + loginSysAdmin();
  246 +
  247 + doDelete("/api/tenant/"+savedTenant2.getId().getId().toString())
  248 + .andExpect(status().isOk());
  249 + }
  250 +
  251 + @Test
  252 + public void testFindTenantAssets() throws Exception {
  253 + List<Asset> assets = new ArrayList<>();
  254 + for (int i=0;i<178;i++) {
  255 + Asset asset = new Asset();
  256 + asset.setName("Asset"+i);
  257 + asset.setType("default");
  258 + assets.add(doPost("/api/asset", asset, Asset.class));
  259 + }
  260 + List<Asset> loadedAssets = new ArrayList<>();
  261 + TextPageLink pageLink = new TextPageLink(23);
  262 + TextPageData<Asset> pageData = null;
  263 + do {
  264 + pageData = doGetTypedWithPageLink("/api/tenant/assets?",
  265 + new TypeReference<TextPageData<Asset>>(){}, pageLink);
  266 + loadedAssets.addAll(pageData.getData());
  267 + if (pageData.hasNext()) {
  268 + pageLink = pageData.getNextPageLink();
  269 + }
  270 + } while (pageData.hasNext());
  271 +
  272 + Collections.sort(assets, idComparator);
  273 + Collections.sort(loadedAssets, idComparator);
  274 +
  275 + Assert.assertEquals(assets, loadedAssets);
  276 + }
  277 +
  278 + @Test
  279 + public void testFindTenantAssetsByName() throws Exception {
  280 + String title1 = "Asset title 1";
  281 + List<Asset> assetsTitle1 = new ArrayList<>();
  282 + for (int i=0;i<143;i++) {
  283 + Asset asset = new Asset();
  284 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  285 + String name = title1+suffix;
  286 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  287 + asset.setName(name);
  288 + asset.setType("default");
  289 + assetsTitle1.add(doPost("/api/asset", asset, Asset.class));
  290 + }
  291 + String title2 = "Asset title 2";
  292 + List<Asset> assetsTitle2 = new ArrayList<>();
  293 + for (int i=0;i<75;i++) {
  294 + Asset asset = new Asset();
  295 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  296 + String name = title2+suffix;
  297 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  298 + asset.setName(name);
  299 + asset.setType("default");
  300 + assetsTitle2.add(doPost("/api/asset", asset, Asset.class));
  301 + }
  302 +
  303 + List<Asset> loadedAssetsTitle1 = new ArrayList<>();
  304 + TextPageLink pageLink = new TextPageLink(15, title1);
  305 + TextPageData<Asset> pageData = null;
  306 + do {
  307 + pageData = doGetTypedWithPageLink("/api/tenant/assets?",
  308 + new TypeReference<TextPageData<Asset>>(){}, pageLink);
  309 + loadedAssetsTitle1.addAll(pageData.getData());
  310 + if (pageData.hasNext()) {
  311 + pageLink = pageData.getNextPageLink();
  312 + }
  313 + } while (pageData.hasNext());
  314 +
  315 + Collections.sort(assetsTitle1, idComparator);
  316 + Collections.sort(loadedAssetsTitle1, idComparator);
  317 +
  318 + Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
  319 +
  320 + List<Asset> loadedAssetsTitle2 = new ArrayList<>();
  321 + pageLink = new TextPageLink(4, title2);
  322 + do {
  323 + pageData = doGetTypedWithPageLink("/api/tenant/assets?",
  324 + new TypeReference<TextPageData<Asset>>(){}, pageLink);
  325 + loadedAssetsTitle2.addAll(pageData.getData());
  326 + if (pageData.hasNext()) {
  327 + pageLink = pageData.getNextPageLink();
  328 + }
  329 + } while (pageData.hasNext());
  330 +
  331 + Collections.sort(assetsTitle2, idComparator);
  332 + Collections.sort(loadedAssetsTitle2, idComparator);
  333 +
  334 + Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
  335 +
  336 + for (Asset asset : loadedAssetsTitle1) {
  337 + doDelete("/api/asset/"+asset.getId().getId().toString())
  338 + .andExpect(status().isOk());
  339 + }
  340 +
  341 + pageLink = new TextPageLink(4, title1);
  342 + pageData = doGetTypedWithPageLink("/api/tenant/assets?",
  343 + new TypeReference<TextPageData<Asset>>(){}, pageLink);
  344 + Assert.assertFalse(pageData.hasNext());
  345 + Assert.assertEquals(0, pageData.getData().size());
  346 +
  347 + for (Asset asset : loadedAssetsTitle2) {
  348 + doDelete("/api/asset/"+asset.getId().getId().toString())
  349 + .andExpect(status().isOk());
  350 + }
  351 +
  352 + pageLink = new TextPageLink(4, title2);
  353 + pageData = doGetTypedWithPageLink("/api/tenant/assets?",
  354 + new TypeReference<TextPageData<Asset>>(){}, pageLink);
  355 + Assert.assertFalse(pageData.hasNext());
  356 + Assert.assertEquals(0, pageData.getData().size());
  357 + }
  358 +
  359 + @Test
  360 + public void testFindTenantAssetsByType() throws Exception {
  361 + String title1 = "Asset title 1";
  362 + String type1 = "typeA";
  363 + List<Asset> assetsType1 = new ArrayList<>();
  364 + for (int i=0;i<143;i++) {
  365 + Asset asset = new Asset();
  366 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  367 + String name = title1+suffix;
  368 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  369 + asset.setName(name);
  370 + asset.setType(type1);
  371 + assetsType1.add(doPost("/api/asset", asset, Asset.class));
  372 + }
  373 + String title2 = "Asset title 2";
  374 + String type2 = "typeB";
  375 + List<Asset> assetsType2 = new ArrayList<>();
  376 + for (int i=0;i<75;i++) {
  377 + Asset asset = new Asset();
  378 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  379 + String name = title2+suffix;
  380 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  381 + asset.setName(name);
  382 + asset.setType(type2);
  383 + assetsType2.add(doPost("/api/asset", asset, Asset.class));
  384 + }
  385 +
  386 + List<Asset> loadedAssetsType1 = new ArrayList<>();
  387 + TextPageLink pageLink = new TextPageLink(15);
  388 + TextPageData<Asset> pageData = null;
  389 + do {
  390 + pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
  391 + new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
  392 + loadedAssetsType1.addAll(pageData.getData());
  393 + if (pageData.hasNext()) {
  394 + pageLink = pageData.getNextPageLink();
  395 + }
  396 + } while (pageData.hasNext());
  397 +
  398 + Collections.sort(assetsType1, idComparator);
  399 + Collections.sort(loadedAssetsType1, idComparator);
  400 +
  401 + Assert.assertEquals(assetsType1, loadedAssetsType1);
  402 +
  403 + List<Asset> loadedAssetsType2 = new ArrayList<>();
  404 + pageLink = new TextPageLink(4);
  405 + do {
  406 + pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
  407 + new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
  408 + loadedAssetsType2.addAll(pageData.getData());
  409 + if (pageData.hasNext()) {
  410 + pageLink = pageData.getNextPageLink();
  411 + }
  412 + } while (pageData.hasNext());
  413 +
  414 + Collections.sort(assetsType2, idComparator);
  415 + Collections.sort(loadedAssetsType2, idComparator);
  416 +
  417 + Assert.assertEquals(assetsType2, loadedAssetsType2);
  418 +
  419 + for (Asset asset : loadedAssetsType1) {
  420 + doDelete("/api/asset/"+asset.getId().getId().toString())
  421 + .andExpect(status().isOk());
  422 + }
  423 +
  424 + pageLink = new TextPageLink(4);
  425 + pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
  426 + new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
  427 + Assert.assertFalse(pageData.hasNext());
  428 + Assert.assertEquals(0, pageData.getData().size());
  429 +
  430 + for (Asset asset : loadedAssetsType2) {
  431 + doDelete("/api/asset/"+asset.getId().getId().toString())
  432 + .andExpect(status().isOk());
  433 + }
  434 +
  435 + pageLink = new TextPageLink(4);
  436 + pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
  437 + new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
  438 + Assert.assertFalse(pageData.hasNext());
  439 + Assert.assertEquals(0, pageData.getData().size());
  440 + }
  441 +
  442 + @Test
  443 + public void testFindCustomerAssets() throws Exception {
  444 + Customer customer = new Customer();
  445 + customer.setTitle("Test customer");
  446 + customer = doPost("/api/customer", customer, Customer.class);
  447 + CustomerId customerId = customer.getId();
  448 +
  449 + List<Asset> assets = new ArrayList<>();
  450 + for (int i=0;i<128;i++) {
  451 + Asset asset = new Asset();
  452 + asset.setName("Asset"+i);
  453 + asset.setType("default");
  454 + asset = doPost("/api/asset", asset, Asset.class);
  455 + assets.add(doPost("/api/customer/" + customerId.getId().toString()
  456 + + "/asset/" + asset.getId().getId().toString(), Asset.class));
  457 + }
  458 +
  459 + List<Asset> loadedAssets = new ArrayList<>();
  460 + TextPageLink pageLink = new TextPageLink(23);
  461 + TextPageData<Asset> pageData = null;
  462 + do {
  463 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
  464 + new TypeReference<TextPageData<Asset>>(){}, pageLink);
  465 + loadedAssets.addAll(pageData.getData());
  466 + if (pageData.hasNext()) {
  467 + pageLink = pageData.getNextPageLink();
  468 + }
  469 + } while (pageData.hasNext());
  470 +
  471 + Collections.sort(assets, idComparator);
  472 + Collections.sort(loadedAssets, idComparator);
  473 +
  474 + Assert.assertEquals(assets, loadedAssets);
  475 + }
  476 +
  477 + @Test
  478 + public void testFindCustomerAssetsByName() throws Exception {
  479 + Customer customer = new Customer();
  480 + customer.setTitle("Test customer");
  481 + customer = doPost("/api/customer", customer, Customer.class);
  482 + CustomerId customerId = customer.getId();
  483 +
  484 + String title1 = "Asset title 1";
  485 + List<Asset> assetsTitle1 = new ArrayList<>();
  486 + for (int i=0;i<125;i++) {
  487 + Asset asset = new Asset();
  488 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  489 + String name = title1+suffix;
  490 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  491 + asset.setName(name);
  492 + asset.setType("default");
  493 + asset = doPost("/api/asset", asset, Asset.class);
  494 + assetsTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
  495 + + "/asset/" + asset.getId().getId().toString(), Asset.class));
  496 + }
  497 + String title2 = "Asset title 2";
  498 + List<Asset> assetsTitle2 = new ArrayList<>();
  499 + for (int i=0;i<143;i++) {
  500 + Asset asset = new Asset();
  501 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  502 + String name = title2+suffix;
  503 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  504 + asset.setName(name);
  505 + asset.setType("default");
  506 + asset = doPost("/api/asset", asset, Asset.class);
  507 + assetsTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
  508 + + "/asset/" + asset.getId().getId().toString(), Asset.class));
  509 + }
  510 +
  511 + List<Asset> loadedAssetsTitle1 = new ArrayList<>();
  512 + TextPageLink pageLink = new TextPageLink(15, title1);
  513 + TextPageData<Asset> pageData = null;
  514 + do {
  515 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
  516 + new TypeReference<TextPageData<Asset>>(){}, pageLink);
  517 + loadedAssetsTitle1.addAll(pageData.getData());
  518 + if (pageData.hasNext()) {
  519 + pageLink = pageData.getNextPageLink();
  520 + }
  521 + } while (pageData.hasNext());
  522 +
  523 + Collections.sort(assetsTitle1, idComparator);
  524 + Collections.sort(loadedAssetsTitle1, idComparator);
  525 +
  526 + Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
  527 +
  528 + List<Asset> loadedAssetsTitle2 = new ArrayList<>();
  529 + pageLink = new TextPageLink(4, title2);
  530 + do {
  531 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
  532 + new TypeReference<TextPageData<Asset>>(){}, pageLink);
  533 + loadedAssetsTitle2.addAll(pageData.getData());
  534 + if (pageData.hasNext()) {
  535 + pageLink = pageData.getNextPageLink();
  536 + }
  537 + } while (pageData.hasNext());
  538 +
  539 + Collections.sort(assetsTitle2, idComparator);
  540 + Collections.sort(loadedAssetsTitle2, idComparator);
  541 +
  542 + Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
  543 +
  544 + for (Asset asset : loadedAssetsTitle1) {
  545 + doDelete("/api/customer/asset/" + asset.getId().getId().toString())
  546 + .andExpect(status().isOk());
  547 + }
  548 +
  549 + pageLink = new TextPageLink(4, title1);
  550 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
  551 + new TypeReference<TextPageData<Asset>>(){}, pageLink);
  552 + Assert.assertFalse(pageData.hasNext());
  553 + Assert.assertEquals(0, pageData.getData().size());
  554 +
  555 + for (Asset asset : loadedAssetsTitle2) {
  556 + doDelete("/api/customer/asset/" + asset.getId().getId().toString())
  557 + .andExpect(status().isOk());
  558 + }
  559 +
  560 + pageLink = new TextPageLink(4, title2);
  561 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
  562 + new TypeReference<TextPageData<Asset>>(){}, pageLink);
  563 + Assert.assertFalse(pageData.hasNext());
  564 + Assert.assertEquals(0, pageData.getData().size());
  565 + }
  566 +
  567 + @Test
  568 + public void testFindCustomerAssetsByType() throws Exception {
  569 + Customer customer = new Customer();
  570 + customer.setTitle("Test customer");
  571 + customer = doPost("/api/customer", customer, Customer.class);
  572 + CustomerId customerId = customer.getId();
  573 +
  574 + String title1 = "Asset title 1";
  575 + String type1 = "typeC";
  576 + List<Asset> assetsType1 = new ArrayList<>();
  577 + for (int i=0;i<125;i++) {
  578 + Asset asset = new Asset();
  579 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  580 + String name = title1+suffix;
  581 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  582 + asset.setName(name);
  583 + asset.setType(type1);
  584 + asset = doPost("/api/asset", asset, Asset.class);
  585 + assetsType1.add(doPost("/api/customer/" + customerId.getId().toString()
  586 + + "/asset/" + asset.getId().getId().toString(), Asset.class));
  587 + }
  588 + String title2 = "Asset title 2";
  589 + String type2 = "typeD";
  590 + List<Asset> assetsType2 = new ArrayList<>();
  591 + for (int i=0;i<143;i++) {
  592 + Asset asset = new Asset();
  593 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  594 + String name = title2+suffix;
  595 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  596 + asset.setName(name);
  597 + asset.setType(type2);
  598 + asset = doPost("/api/asset", asset, Asset.class);
  599 + assetsType2.add(doPost("/api/customer/" + customerId.getId().toString()
  600 + + "/asset/" + asset.getId().getId().toString(), Asset.class));
  601 + }
  602 +
  603 + List<Asset> loadedAssetsType1 = new ArrayList<>();
  604 + TextPageLink pageLink = new TextPageLink(15);
  605 + TextPageData<Asset> pageData = null;
  606 + do {
  607 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
  608 + new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
  609 + loadedAssetsType1.addAll(pageData.getData());
  610 + if (pageData.hasNext()) {
  611 + pageLink = pageData.getNextPageLink();
  612 + }
  613 + } while (pageData.hasNext());
  614 +
  615 + Collections.sort(assetsType1, idComparator);
  616 + Collections.sort(loadedAssetsType1, idComparator);
  617 +
  618 + Assert.assertEquals(assetsType1, loadedAssetsType1);
  619 +
  620 + List<Asset> loadedAssetsType2 = new ArrayList<>();
  621 + pageLink = new TextPageLink(4);
  622 + do {
  623 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
  624 + new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
  625 + loadedAssetsType2.addAll(pageData.getData());
  626 + if (pageData.hasNext()) {
  627 + pageLink = pageData.getNextPageLink();
  628 + }
  629 + } while (pageData.hasNext());
  630 +
  631 + Collections.sort(assetsType2, idComparator);
  632 + Collections.sort(loadedAssetsType2, idComparator);
  633 +
  634 + Assert.assertEquals(assetsType2, loadedAssetsType2);
  635 +
  636 + for (Asset asset : loadedAssetsType1) {
  637 + doDelete("/api/customer/asset/" + asset.getId().getId().toString())
  638 + .andExpect(status().isOk());
  639 + }
  640 +
  641 + pageLink = new TextPageLink(4);
  642 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
  643 + new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
  644 + Assert.assertFalse(pageData.hasNext());
  645 + Assert.assertEquals(0, pageData.getData().size());
  646 +
  647 + for (Asset asset : loadedAssetsType2) {
  648 + doDelete("/api/customer/asset/" + asset.getId().getId().toString())
  649 + .andExpect(status().isOk());
  650 + }
  651 +
  652 + pageLink = new TextPageLink(4);
  653 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
  654 + new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
  655 + Assert.assertFalse(pageData.hasNext());
  656 + Assert.assertEquals(0, pageData.getData().size());
  657 + }
  658 +
  659 +}
... ...
... ... @@ -24,10 +24,7 @@ import java.util.Collections;
24 24 import java.util.List;
25 25
26 26 import org.apache.commons.lang3.RandomStringUtils;
27   -import org.thingsboard.server.common.data.Customer;
28   -import org.thingsboard.server.common.data.Device;
29   -import org.thingsboard.server.common.data.Tenant;
30   -import org.thingsboard.server.common.data.User;
  27 +import org.thingsboard.server.common.data.*;
31 28 import org.thingsboard.server.common.data.id.CustomerId;
32 29 import org.thingsboard.server.common.data.id.DeviceCredentialsId;
33 30 import org.thingsboard.server.common.data.id.DeviceId;
... ... @@ -83,6 +80,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
83 80 public void testSaveDevice() throws Exception {
84 81 Device device = new Device();
85 82 device.setName("My device");
  83 + device.setType("default");
86 84 Device savedDevice = doPost("/api/device", device, Device.class);
87 85
88 86 Assert.assertNotNull(savedDevice);
... ... @@ -114,16 +112,49 @@ public class DeviceControllerTest extends AbstractControllerTest {
114 112 public void testFindDeviceById() throws Exception {
115 113 Device device = new Device();
116 114 device.setName("My device");
  115 + device.setType("default");
117 116 Device savedDevice = doPost("/api/device", device, Device.class);
118 117 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
119 118 Assert.assertNotNull(foundDevice);
120 119 Assert.assertEquals(savedDevice, foundDevice);
121 120 }
  121 +
  122 + @Test
  123 + public void testFindDeviceTypesByTenantId() throws Exception {
  124 + List<Device> devices = new ArrayList<>();
  125 + for (int i=0;i<3;i++) {
  126 + Device device = new Device();
  127 + device.setName("My device B"+i);
  128 + device.setType("typeB");
  129 + devices.add(doPost("/api/device", device, Device.class));
  130 + }
  131 + for (int i=0;i<7;i++) {
  132 + Device device = new Device();
  133 + device.setName("My device C"+i);
  134 + device.setType("typeC");
  135 + devices.add(doPost("/api/device", device, Device.class));
  136 + }
  137 + for (int i=0;i<9;i++) {
  138 + Device device = new Device();
  139 + device.setName("My device A"+i);
  140 + device.setType("typeA");
  141 + devices.add(doPost("/api/device", device, Device.class));
  142 + }
  143 + List<TenantDeviceType> deviceTypes = doGetTyped("/api/device/types",
  144 + new TypeReference<List<TenantDeviceType>>(){});
  145 +
  146 + Assert.assertNotNull(deviceTypes);
  147 + Assert.assertEquals(3, deviceTypes.size());
  148 + Assert.assertEquals("typeA", deviceTypes.get(0).getType());
  149 + Assert.assertEquals("typeB", deviceTypes.get(1).getType());
  150 + Assert.assertEquals("typeC", deviceTypes.get(2).getType());
  151 + }
122 152
123 153 @Test
124 154 public void testDeleteDevice() throws Exception {
125 155 Device device = new Device();
126 156 device.setName("My device");
  157 + device.setType("default");
127 158 Device savedDevice = doPost("/api/device", device, Device.class);
128 159
129 160 doDelete("/api/device/"+savedDevice.getId().getId().toString())
... ... @@ -132,10 +163,20 @@ public class DeviceControllerTest extends AbstractControllerTest {
132 163 doGet("/api/device/"+savedDevice.getId().getId().toString())
133 164 .andExpect(status().isNotFound());
134 165 }
135   -
  166 +
  167 + @Test
  168 + public void testSaveDeviceWithEmptyType() throws Exception {
  169 + Device device = new Device();
  170 + device.setName("My device");
  171 + doPost("/api/device", device)
  172 + .andExpect(status().isBadRequest())
  173 + .andExpect(statusReason(containsString("Device type should be specified")));
  174 + }
  175 +
136 176 @Test
137 177 public void testSaveDeviceWithEmptyName() throws Exception {
138 178 Device device = new Device();
  179 + device.setType("default");
139 180 doPost("/api/device", device)
140 181 .andExpect(status().isBadRequest())
141 182 .andExpect(statusReason(containsString("Device name should be specified")));
... ... @@ -145,6 +186,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
145 186 public void testAssignUnassignDeviceToCustomer() throws Exception {
146 187 Device device = new Device();
147 188 device.setName("My device");
  189 + device.setType("default");
148 190 Device savedDevice = doPost("/api/device", device, Device.class);
149 191
150 192 Customer customer = new Customer();
... ... @@ -170,6 +212,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
170 212 public void testAssignDeviceToNonExistentCustomer() throws Exception {
171 213 Device device = new Device();
172 214 device.setName("My device");
  215 + device.setType("default");
173 216 Device savedDevice = doPost("/api/device", device, Device.class);
174 217
175 218 doPost("/api/customer/" + UUIDs.timeBased().toString()
... ... @@ -203,6 +246,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
203 246
204 247 Device device = new Device();
205 248 device.setName("My device");
  249 + device.setType("default");
206 250 Device savedDevice = doPost("/api/device", device, Device.class);
207 251
208 252 doPost("/api/customer/" + savedCustomer.getId().getId().toString()
... ... @@ -219,6 +263,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
219 263 public void testFindDeviceCredentialsByDeviceId() throws Exception {
220 264 Device device = new Device();
221 265 device.setName("My device");
  266 + device.setType("default");
222 267 Device savedDevice = doPost("/api/device", device, Device.class);
223 268 DeviceCredentials deviceCredentials =
224 269 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
... ... @@ -229,6 +274,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
229 274 public void testSaveDeviceCredentials() throws Exception {
230 275 Device device = new Device();
231 276 device.setName("My device");
  277 + device.setType("default");
232 278 Device savedDevice = doPost("/api/device", device, Device.class);
233 279 DeviceCredentials deviceCredentials =
234 280 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
... ... @@ -255,6 +301,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
255 301 public void testSaveDeviceCredentialsWithEmptyCredentialsType() throws Exception {
256 302 Device device = new Device();
257 303 device.setName("My device");
  304 + device.setType("default");
258 305 Device savedDevice = doPost("/api/device", device, Device.class);
259 306 DeviceCredentials deviceCredentials =
260 307 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
... ... @@ -268,6 +315,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
268 315 public void testSaveDeviceCredentialsWithEmptyCredentialsId() throws Exception {
269 316 Device device = new Device();
270 317 device.setName("My device");
  318 + device.setType("default");
271 319 Device savedDevice = doPost("/api/device", device, Device.class);
272 320 DeviceCredentials deviceCredentials =
273 321 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
... ... @@ -281,6 +329,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
281 329 public void testSaveNonExistentDeviceCredentials() throws Exception {
282 330 Device device = new Device();
283 331 device.setName("My device");
  332 + device.setType("default");
284 333 Device savedDevice = doPost("/api/device", device, Device.class);
285 334 DeviceCredentials deviceCredentials =
286 335 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
... ... @@ -298,6 +347,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
298 347 public void testSaveDeviceCredentialsWithNonExistentDevice() throws Exception {
299 348 Device device = new Device();
300 349 device.setName("My device");
  350 + device.setType("default");
301 351 Device savedDevice = doPost("/api/device", device, Device.class);
302 352 DeviceCredentials deviceCredentials =
303 353 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
... ... @@ -307,9 +357,10 @@ public class DeviceControllerTest extends AbstractControllerTest {
307 357 }
308 358
309 359 @Test
310   - public void testSaveDeviceCredentialsWithInvalidCredemtialsIdLength() throws Exception {
  360 + public void testSaveDeviceCredentialsWithInvalidCredentialsIdLength() throws Exception {
311 361 Device device = new Device();
312 362 device.setName("My device");
  363 + device.setType("default");
313 364 Device savedDevice = doPost("/api/device", device, Device.class);
314 365 DeviceCredentials deviceCredentials =
315 366 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
... ... @@ -325,6 +376,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
325 376 for (int i=0;i<178;i++) {
326 377 Device device = new Device();
327 378 device.setName("Device"+i);
  379 + device.setType("default");
328 380 devices.add(doPost("/api/device", device, Device.class));
329 381 }
330 382 List<Device> loadedDevices = new ArrayList<>();
... ... @@ -355,6 +407,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
355 407 String name = title1+suffix;
356 408 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
357 409 device.setName(name);
  410 + device.setType("default");
358 411 devicesTitle1.add(doPost("/api/device", device, Device.class));
359 412 }
360 413 String title2 = "Device title 2";
... ... @@ -365,6 +418,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
365 418 String name = title2+suffix;
366 419 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
367 420 device.setName(name);
  421 + device.setType("default");
368 422 devicesTitle2.add(doPost("/api/device", device, Device.class));
369 423 }
370 424
... ... @@ -423,6 +477,89 @@ public class DeviceControllerTest extends AbstractControllerTest {
423 477 Assert.assertFalse(pageData.hasNext());
424 478 Assert.assertEquals(0, pageData.getData().size());
425 479 }
  480 +
  481 + @Test
  482 + public void testFindTenantDevicesByType() throws Exception {
  483 + String title1 = "Device title 1";
  484 + String type1 = "typeA";
  485 + List<Device> devicesType1 = new ArrayList<>();
  486 + for (int i=0;i<143;i++) {
  487 + Device device = new Device();
  488 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  489 + String name = title1+suffix;
  490 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  491 + device.setName(name);
  492 + device.setType(type1);
  493 + devicesType1.add(doPost("/api/device", device, Device.class));
  494 + }
  495 + String title2 = "Device title 2";
  496 + String type2 = "typeB";
  497 + List<Device> devicesType2 = new ArrayList<>();
  498 + for (int i=0;i<75;i++) {
  499 + Device device = new Device();
  500 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  501 + String name = title2+suffix;
  502 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  503 + device.setName(name);
  504 + device.setType(type2);
  505 + devicesType2.add(doPost("/api/device", device, Device.class));
  506 + }
  507 +
  508 + List<Device> loadedDevicesType1 = new ArrayList<>();
  509 + TextPageLink pageLink = new TextPageLink(15);
  510 + TextPageData<Device> pageData = null;
  511 + do {
  512 + pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
  513 + new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
  514 + loadedDevicesType1.addAll(pageData.getData());
  515 + if (pageData.hasNext()) {
  516 + pageLink = pageData.getNextPageLink();
  517 + }
  518 + } while (pageData.hasNext());
  519 +
  520 + Collections.sort(devicesType1, idComparator);
  521 + Collections.sort(loadedDevicesType1, idComparator);
  522 +
  523 + Assert.assertEquals(devicesType1, loadedDevicesType1);
  524 +
  525 + List<Device> loadedDevicesType2 = new ArrayList<>();
  526 + pageLink = new TextPageLink(4);
  527 + do {
  528 + pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
  529 + new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
  530 + loadedDevicesType2.addAll(pageData.getData());
  531 + if (pageData.hasNext()) {
  532 + pageLink = pageData.getNextPageLink();
  533 + }
  534 + } while (pageData.hasNext());
  535 +
  536 + Collections.sort(devicesType2, idComparator);
  537 + Collections.sort(loadedDevicesType2, idComparator);
  538 +
  539 + Assert.assertEquals(devicesType2, loadedDevicesType2);
  540 +
  541 + for (Device device : loadedDevicesType1) {
  542 + doDelete("/api/device/"+device.getId().getId().toString())
  543 + .andExpect(status().isOk());
  544 + }
  545 +
  546 + pageLink = new TextPageLink(4);
  547 + pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
  548 + new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
  549 + Assert.assertFalse(pageData.hasNext());
  550 + Assert.assertEquals(0, pageData.getData().size());
  551 +
  552 + for (Device device : loadedDevicesType2) {
  553 + doDelete("/api/device/"+device.getId().getId().toString())
  554 + .andExpect(status().isOk());
  555 + }
  556 +
  557 + pageLink = new TextPageLink(4);
  558 + pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
  559 + new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
  560 + Assert.assertFalse(pageData.hasNext());
  561 + Assert.assertEquals(0, pageData.getData().size());
  562 + }
426 563
427 564 @Test
428 565 public void testFindCustomerDevices() throws Exception {
... ... @@ -435,6 +572,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
435 572 for (int i=0;i<128;i++) {
436 573 Device device = new Device();
437 574 device.setName("Device"+i);
  575 + device.setType("default");
438 576 device = doPost("/api/device", device, Device.class);
439 577 devices.add(doPost("/api/customer/" + customerId.getId().toString()
440 578 + "/device/" + device.getId().getId().toString(), Device.class));
... ... @@ -473,6 +611,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
473 611 String name = title1+suffix;
474 612 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
475 613 device.setName(name);
  614 + device.setType("default");
476 615 device = doPost("/api/device", device, Device.class);
477 616 devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
478 617 + "/device/" + device.getId().getId().toString(), Device.class));
... ... @@ -485,6 +624,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
485 624 String name = title2+suffix;
486 625 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
487 626 device.setName(name);
  627 + device.setType("default");
488 628 device = doPost("/api/device", device, Device.class);
489 629 devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
490 630 + "/device/" + device.getId().getId().toString(), Device.class));
... ... @@ -546,4 +686,96 @@ public class DeviceControllerTest extends AbstractControllerTest {
546 686 Assert.assertEquals(0, pageData.getData().size());
547 687 }
548 688
  689 + @Test
  690 + public void testFindCustomerDevicesByType() throws Exception {
  691 + Customer customer = new Customer();
  692 + customer.setTitle("Test customer");
  693 + customer = doPost("/api/customer", customer, Customer.class);
  694 + CustomerId customerId = customer.getId();
  695 +
  696 + String title1 = "Device title 1";
  697 + String type1 = "typeC";
  698 + List<Device> devicesType1 = new ArrayList<>();
  699 + for (int i=0;i<125;i++) {
  700 + Device device = new Device();
  701 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  702 + String name = title1+suffix;
  703 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  704 + device.setName(name);
  705 + device.setType(type1);
  706 + device = doPost("/api/device", device, Device.class);
  707 + devicesType1.add(doPost("/api/customer/" + customerId.getId().toString()
  708 + + "/device/" + device.getId().getId().toString(), Device.class));
  709 + }
  710 + String title2 = "Device title 2";
  711 + String type2 = "typeD";
  712 + List<Device> devicesType2 = new ArrayList<>();
  713 + for (int i=0;i<143;i++) {
  714 + Device device = new Device();
  715 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  716 + String name = title2+suffix;
  717 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  718 + device.setName(name);
  719 + device.setType(type2);
  720 + device = doPost("/api/device", device, Device.class);
  721 + devicesType2.add(doPost("/api/customer/" + customerId.getId().toString()
  722 + + "/device/" + device.getId().getId().toString(), Device.class));
  723 + }
  724 +
  725 + List<Device> loadedDevicesType1 = new ArrayList<>();
  726 + TextPageLink pageLink = new TextPageLink(15);
  727 + TextPageData<Device> pageData = null;
  728 + do {
  729 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
  730 + new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
  731 + loadedDevicesType1.addAll(pageData.getData());
  732 + if (pageData.hasNext()) {
  733 + pageLink = pageData.getNextPageLink();
  734 + }
  735 + } while (pageData.hasNext());
  736 +
  737 + Collections.sort(devicesType1, idComparator);
  738 + Collections.sort(loadedDevicesType1, idComparator);
  739 +
  740 + Assert.assertEquals(devicesType1, loadedDevicesType1);
  741 +
  742 + List<Device> loadedDevicesType2 = new ArrayList<>();
  743 + pageLink = new TextPageLink(4);
  744 + do {
  745 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
  746 + new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
  747 + loadedDevicesType2.addAll(pageData.getData());
  748 + if (pageData.hasNext()) {
  749 + pageLink = pageData.getNextPageLink();
  750 + }
  751 + } while (pageData.hasNext());
  752 +
  753 + Collections.sort(devicesType2, idComparator);
  754 + Collections.sort(loadedDevicesType2, idComparator);
  755 +
  756 + Assert.assertEquals(devicesType2, loadedDevicesType2);
  757 +
  758 + for (Device device : loadedDevicesType1) {
  759 + doDelete("/api/customer/device/" + device.getId().getId().toString())
  760 + .andExpect(status().isOk());
  761 + }
  762 +
  763 + pageLink = new TextPageLink(4);
  764 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
  765 + new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
  766 + Assert.assertFalse(pageData.hasNext());
  767 + Assert.assertEquals(0, pageData.getData().size());
  768 +
  769 + for (Device device : loadedDevicesType2) {
  770 + doDelete("/api/customer/device/" + device.getId().getId().toString())
  771 + .andExpect(status().isOk());
  772 + }
  773 +
  774 + pageLink = new TextPageLink(4);
  775 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
  776 + new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
  777 + Assert.assertFalse(pageData.hasNext());
  778 + Assert.assertEquals(0, pageData.getData().size());
  779 + }
  780 +
549 781 }
... ...
... ... @@ -130,7 +130,7 @@ public class TenantControllerTest extends AbstractControllerTest {
130 130 Assert.assertEquals(tenants, loadedTenants);
131 131
132 132 for (Tenant tenant : loadedTenants) {
133   - if (!tenant.getTitle().equals("Tenant")) {
  133 + if (!tenant.getTitle().equals(TEST_TENANT_NAME)) {
134 134 doDelete("/api/tenant/"+tenant.getId().getId().toString())
135 135 .andExpect(status().isOk());
136 136 }
... ...
... ... @@ -182,7 +182,7 @@ public class UserControllerTest extends AbstractControllerTest {
182 182 Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
183 183 Assert.assertNotNull(savedTenant);
184 184
185   - String email = "tenant@thingsboard.org";
  185 + String email = TENANT_ADMIN_EMAIL;
186 186 User user = new User();
187 187 user.setAuthority(Authority.TENANT_ADMIN);
188 188 user.setTenantId(savedTenant.getId());
... ...
... ... @@ -47,6 +47,7 @@ public class HttpDeviceApiTest extends AbstractControllerTest {
47 47 loginTenantAdmin();
48 48 device = new Device();
49 49 device.setName("My device");
  50 + device.setType("default");
50 51 device = doPost("/api/device", device, Device.class);
51 52
52 53 deviceCredentials =
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data;
  17 +
  18 +import org.thingsboard.server.common.data.id.TenantId;
  19 +
  20 +public class TenantDeviceType {
  21 +
  22 + private static final long serialVersionUID = 8057240243859922101L;
  23 +
  24 + private String type;
  25 + private TenantId tenantId;
  26 +
  27 + public TenantDeviceType() {
  28 + super();
  29 + }
  30 +
  31 + public TenantDeviceType(String type, TenantId tenantId) {
  32 + this.type = type;
  33 + this.tenantId = tenantId;
  34 + }
  35 +
  36 + public String getType() {
  37 + return type;
  38 + }
  39 +
  40 + public void setType(String type) {
  41 + this.type = type;
  42 + }
  43 +
  44 + public TenantId getTenantId() {
  45 + return tenantId;
  46 + }
  47 +
  48 + public void setTenantId(TenantId tenantId) {
  49 + this.tenantId = tenantId;
  50 + }
  51 +
  52 + @Override
  53 + public int hashCode() {
  54 + int result = type != null ? type.hashCode() : 0;
  55 + result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
  56 + return result;
  57 + }
  58 +
  59 + @Override
  60 + public boolean equals(Object o) {
  61 + if (this == o) return true;
  62 + if (o == null || getClass() != o.getClass()) return false;
  63 +
  64 + TenantDeviceType that = (TenantDeviceType) o;
  65 +
  66 + if (type != null ? !type.equals(that.type) : that.type != null) return false;
  67 + return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
  68 +
  69 + }
  70 +
  71 + @Override
  72 + public String toString() {
  73 + final StringBuilder sb = new StringBuilder("TenantDeviceType{");
  74 + sb.append("type='").append(type).append('\'');
  75 + sb.append(", tenantId=").append(tenantId);
  76 + sb.append('}');
  77 + return sb.toString();
  78 + }
  79 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.asset;
  17 +
  18 +import org.thingsboard.server.common.data.id.TenantId;
  19 +
  20 +public class TenantAssetType {
  21 +
  22 + private static final long serialVersionUID = 8057290243855622101L;
  23 +
  24 + private String type;
  25 + private TenantId tenantId;
  26 +
  27 + public TenantAssetType() {
  28 + super();
  29 + }
  30 +
  31 + public TenantAssetType(String type, TenantId tenantId) {
  32 + this.type = type;
  33 + this.tenantId = tenantId;
  34 + }
  35 +
  36 + public String getType() {
  37 + return type;
  38 + }
  39 +
  40 + public void setType(String type) {
  41 + this.type = type;
  42 + }
  43 +
  44 + public TenantId getTenantId() {
  45 + return tenantId;
  46 + }
  47 +
  48 + public void setTenantId(TenantId tenantId) {
  49 + this.tenantId = tenantId;
  50 + }
  51 +
  52 + @Override
  53 + public int hashCode() {
  54 + int result = type != null ? type.hashCode() : 0;
  55 + result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
  56 + return result;
  57 + }
  58 +
  59 + @Override
  60 + public boolean equals(Object o) {
  61 + if (this == o) return true;
  62 + if (o == null || getClass() != o.getClass()) return false;
  63 +
  64 + TenantAssetType that = (TenantAssetType) o;
  65 +
  66 + if (type != null ? !type.equals(that.type) : that.type != null) return false;
  67 + return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
  68 +
  69 + }
  70 +
  71 + @Override
  72 + public String toString() {
  73 + final StringBuilder sb = new StringBuilder("TenantAssetType{");
  74 + sb.append("type='").append(type).append('\'');
  75 + sb.append(", tenantId=").append(tenantId);
  76 + sb.append('}');
  77 + return sb.toString();
  78 + }
  79 +}
... ...
... ... @@ -16,12 +16,11 @@
16 16 package org.thingsboard.server.dao.asset;
17 17
18 18 import com.google.common.util.concurrent.ListenableFuture;
19   -import org.thingsboard.server.common.data.Device;
20 19 import org.thingsboard.server.common.data.asset.Asset;
21 20 import org.thingsboard.server.common.data.page.TextPageLink;
22 21 import org.thingsboard.server.dao.Dao;
23 22 import org.thingsboard.server.dao.model.AssetEntity;
24   -import org.thingsboard.server.dao.model.DeviceEntity;
  23 +import org.thingsboard.server.dao.model.TenantAssetTypeEntity;
25 24
26 25 import java.util.List;
27 26 import java.util.Optional;
... ... @@ -51,6 +50,16 @@ public interface AssetDao extends Dao<AssetEntity> {
51 50 List<AssetEntity> findAssetsByTenantId(UUID tenantId, TextPageLink pageLink);
52 51
53 52 /**
  53 + * Find assets by tenantId, type and page link.
  54 + *
  55 + * @param tenantId the tenantId
  56 + * @param type the type
  57 + * @param pageLink the page link
  58 + * @return the list of asset objects
  59 + */
  60 + List<AssetEntity> findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink);
  61 +
  62 + /**
54 63 * Find assets by tenantId and assets Ids.
55 64 *
56 65 * @param tenantId the tenantId
... ... @@ -70,6 +79,17 @@ public interface AssetDao extends Dao<AssetEntity> {
70 79 List<AssetEntity> findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
71 80
72 81 /**
  82 + * Find assets by tenantId, customerId, type and page link.
  83 + *
  84 + * @param tenantId the tenantId
  85 + * @param customerId the customerId
  86 + * @param type the type
  87 + * @param pageLink the page link
  88 + * @return the list of asset objects
  89 + */
  90 + List<AssetEntity> findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink);
  91 +
  92 + /**
73 93 * Find assets by tenantId, customerId and assets Ids.
74 94 *
75 95 * @param tenantId the tenantId
... ... @@ -87,4 +107,12 @@ public interface AssetDao extends Dao<AssetEntity> {
87 107 * @return the optional asset object
88 108 */
89 109 Optional<AssetEntity> findAssetsByTenantIdAndName(UUID tenantId, String name);
  110 +
  111 + /**
  112 + * Find tenants asset types.
  113 + *
  114 + * @return the list of tenant asset type objects
  115 + */
  116 + ListenableFuture<List<TenantAssetTypeEntity>> findTenantAssetTypesAsync();
  117 +
90 118 }
... ...
... ... @@ -15,7 +15,12 @@
15 15 */
16 16 package org.thingsboard.server.dao.asset;
17 17
  18 +import com.datastax.driver.core.ResultSet;
  19 +import com.datastax.driver.core.ResultSetFuture;
18 20 import com.datastax.driver.core.querybuilder.Select;
  21 +import com.datastax.driver.mapping.Result;
  22 +import com.google.common.base.Function;
  23 +import com.google.common.util.concurrent.Futures;
19 24 import com.google.common.util.concurrent.ListenableFuture;
20 25 import lombok.extern.slf4j.Slf4j;
21 26 import org.springframework.stereotype.Component;
... ... @@ -23,7 +28,9 @@ import org.thingsboard.server.common.data.asset.Asset;
23 28 import org.thingsboard.server.common.data.page.TextPageLink;
24 29 import org.thingsboard.server.dao.AbstractSearchTextDao;
25 30 import org.thingsboard.server.dao.model.AssetEntity;
  31 +import org.thingsboard.server.dao.model.TenantAssetTypeEntity;
26 32
  33 +import javax.annotation.Nullable;
27 34 import java.util.*;
28 35
29 36 import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
... ... @@ -60,6 +67,16 @@ public class AssetDaoImpl extends AbstractSearchTextDao<AssetEntity> implements
60 67 }
61 68
62 69 @Override
  70 + public List<AssetEntity> findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) {
  71 + log.debug("Try to find assets by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
  72 + List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
  73 + Arrays.asList(eq(ASSET_TYPE_PROPERTY, type),
  74 + eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink);
  75 + log.trace("Found assets [{}] by tenantId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, type, pageLink);
  76 + return assetEntities;
  77 + }
  78 +
  79 + @Override
63 80 public ListenableFuture<List<AssetEntity>> findAssetsByTenantIdAndIdsAsync(UUID tenantId, List<UUID> assetIds) {
64 81 log.debug("Try to find assets by tenantId [{}] and asset Ids [{}]", tenantId, assetIds);
65 82 Select select = select().from(getColumnFamilyName());
... ... @@ -82,6 +99,19 @@ public class AssetDaoImpl extends AbstractSearchTextDao<AssetEntity> implements
82 99 }
83 100
84 101 @Override
  102 + public List<AssetEntity> findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) {
  103 + log.debug("Try to find assets by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
  104 + List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
  105 + Arrays.asList(eq(ASSET_TYPE_PROPERTY, type),
  106 + eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
  107 + eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
  108 + pageLink);
  109 +
  110 + log.trace("Found assets [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, customerId, type, pageLink);
  111 + return assetEntities;
  112 + }
  113 +
  114 + @Override
85 115 public ListenableFuture<List<AssetEntity>> findAssetsByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List<UUID> assetIds) {
86 116 log.debug("Try to find assets by tenantId [{}], customerId [{}] and asset Ids [{}]", tenantId, customerId, assetIds);
87 117 Select select = select().from(getColumnFamilyName());
... ... @@ -101,4 +131,24 @@ public class AssetDaoImpl extends AbstractSearchTextDao<AssetEntity> implements
101 131 return Optional.ofNullable(findOneByStatement(query));
102 132 }
103 133
  134 + @Override
  135 + public ListenableFuture<List<TenantAssetTypeEntity>> findTenantAssetTypesAsync() {
  136 + Select statement = select().distinct().column(ASSET_TYPE_PROPERTY).column(ASSET_TENANT_ID_PROPERTY).from(ASSET_TYPES_BY_TENANT_VIEW_NAME);
  137 + statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
  138 + ResultSetFuture resultSetFuture = getSession().executeAsync(statement);
  139 + ListenableFuture<List<TenantAssetTypeEntity>> result = Futures.transform(resultSetFuture, new Function<ResultSet, List<TenantAssetTypeEntity>>() {
  140 + @Nullable
  141 + @Override
  142 + public List<TenantAssetTypeEntity> apply(@Nullable ResultSet resultSet) {
  143 + Result<TenantAssetTypeEntity> result = cluster.getMapper(TenantAssetTypeEntity.class).map(resultSet);
  144 + if (result != null) {
  145 + return result.all();
  146 + } else {
  147 + return Collections.emptyList();
  148 + }
  149 + }
  150 + });
  151 + return result;
  152 + }
  153 +
104 154 }
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.asset;
17 17
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 19 import org.thingsboard.server.common.data.asset.Asset;
  20 +import org.thingsboard.server.common.data.asset.TenantAssetType;
20 21 import org.thingsboard.server.common.data.id.AssetId;
21 22 import org.thingsboard.server.common.data.id.CustomerId;
22 23 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -34,7 +35,7 @@ public interface AssetService {
34 35
35 36 Optional<Asset> findAssetByTenantIdAndName(TenantId tenantId, String name);
36 37
37   - Asset saveAsset(Asset device);
  38 + Asset saveAsset(Asset asset);
38 39
39 40 Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId);
40 41
... ... @@ -44,16 +45,21 @@ public interface AssetService {
44 45
45 46 TextPageData<Asset> findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink);
46 47
  48 + TextPageData<Asset> findAssetsByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink);
  49 +
47 50 ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List<AssetId> assetIds);
48 51
49 52 void deleteAssetsByTenantId(TenantId tenantId);
50 53
51 54 TextPageData<Asset> findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
52 55
  56 + TextPageData<Asset> findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink);
  57 +
53 58 ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<AssetId> assetIds);
54 59
55 60 void unassignCustomerAssets(TenantId tenantId, CustomerId customerId);
56 61
57 62 ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query);
58 63
  64 + ListenableFuture<List<TenantAssetType>> findAssetTypesByTenantId(TenantId tenantId);
59 65 }
... ...
... ... @@ -26,6 +26,7 @@ import org.springframework.stereotype.Service;
26 26 import org.springframework.util.StringUtils;
27 27 import org.thingsboard.server.common.data.EntityType;
28 28 import org.thingsboard.server.common.data.asset.Asset;
  29 +import org.thingsboard.server.common.data.asset.TenantAssetType;
29 30 import org.thingsboard.server.common.data.id.AssetId;
30 31 import org.thingsboard.server.common.data.id.CustomerId;
31 32 import org.thingsboard.server.common.data.id.EntityId;
... ... @@ -36,10 +37,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
36 37 import org.thingsboard.server.dao.customer.CustomerDao;
37 38 import org.thingsboard.server.dao.entity.BaseEntityService;
38 39 import org.thingsboard.server.dao.exception.DataValidationException;
39   -import org.thingsboard.server.dao.model.AssetEntity;
40   -import org.thingsboard.server.dao.model.CustomerEntity;
41   -import org.thingsboard.server.dao.model.TenantEntity;
42   -import org.thingsboard.server.dao.relation.EntityRelationsQuery;
  40 +import org.thingsboard.server.dao.model.*;
43 41 import org.thingsboard.server.dao.relation.EntitySearchDirection;
44 42 import org.thingsboard.server.dao.service.DataValidator;
45 43 import org.thingsboard.server.dao.service.PaginatedRemover;
... ... @@ -132,7 +130,18 @@ public class BaseAssetService extends BaseEntityService implements AssetService
132 130 validatePageLink(pageLink, "Incorrect page link " + pageLink);
133 131 List<AssetEntity> assetEntities = assetDao.findAssetsByTenantId(tenantId.getId(), pageLink);
134 132 List<Asset> assets = convertDataList(assetEntities);
135   - return new TextPageData<Asset>(assets, pageLink);
  133 + return new TextPageData<>(assets, pageLink);
  134 + }
  135 +
  136 + @Override
  137 + public TextPageData<Asset> findAssetsByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink) {
  138 + log.trace("Executing findAssetsByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
  139 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  140 + validateString(type, "Incorrect type " + type);
  141 + validatePageLink(pageLink, "Incorrect page link " + pageLink);
  142 + List<AssetEntity> assetEntities = assetDao.findAssetsByTenantIdAndType(tenantId.getId(), type, pageLink);
  143 + List<Asset> assets = convertDataList(assetEntities);
  144 + return new TextPageData<>(assets, pageLink);
136 145 }
137 146
138 147 @Override
... ... @@ -159,7 +168,19 @@ public class BaseAssetService extends BaseEntityService implements AssetService
159 168 validatePageLink(pageLink, "Incorrect page link " + pageLink);
160 169 List<AssetEntity> assetEntities = assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
161 170 List<Asset> assets = convertDataList(assetEntities);
162   - return new TextPageData<Asset>(assets, pageLink);
  171 + return new TextPageData<>(assets, pageLink);
  172 + }
  173 +
  174 + @Override
  175 + public TextPageData<Asset> findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink) {
  176 + log.trace("Executing findAssetsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
  177 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  178 + validateId(customerId, "Incorrect customerId " + customerId);
  179 + validateString(type, "Incorrect type " + type);
  180 + validatePageLink(pageLink, "Incorrect page link " + pageLink);
  181 + List<AssetEntity> assetEntities = assetDao.findAssetsByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
  182 + List<Asset> assets = convertDataList(assetEntities);
  183 + return new TextPageData<>(assets, pageLink);
163 184 }
164 185
165 186 @Override
... ... @@ -207,6 +228,25 @@ public class BaseAssetService extends BaseEntityService implements AssetService
207 228 return assets;
208 229 }
209 230
  231 + @Override
  232 + public ListenableFuture<List<TenantAssetType>> findAssetTypesByTenantId(TenantId tenantId) {
  233 + log.trace("Executing findAssetTypesByTenantId, tenantId [{}]", tenantId);
  234 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  235 + ListenableFuture<List<TenantAssetTypeEntity>> tenantAssetTypeEntities = assetDao.findTenantAssetTypesAsync();
  236 + ListenableFuture<List<TenantAssetType>> tenantAssetTypes = Futures.transform(tenantAssetTypeEntities,
  237 + (Function<List<TenantAssetTypeEntity>, List<TenantAssetType>>) assetTypeEntities -> {
  238 + List<TenantAssetType> assetTypes = new ArrayList<>();
  239 + for (TenantAssetTypeEntity assetTypeEntity : assetTypeEntities) {
  240 + if (assetTypeEntity.getTenantId().equals(tenantId.getId())) {
  241 + assetTypes.add(assetTypeEntity.toTenantAssetType());
  242 + }
  243 + }
  244 + assetTypes.sort((TenantAssetType o1, TenantAssetType o2) -> o1.getType().compareTo(o2.getType()));
  245 + return assetTypes;
  246 + });
  247 + return tenantAssetTypes;
  248 + }
  249 +
210 250 private DataValidator<Asset> assetValidator =
211 251 new DataValidator<Asset>() {
212 252
... ... @@ -232,6 +272,9 @@ public class BaseAssetService extends BaseEntityService implements AssetService
232 272
233 273 @Override
234 274 protected void validateDataImpl(Asset asset) {
  275 + if (StringUtils.isEmpty(asset.getType())) {
  276 + throw new DataValidationException("Asset type should be specified!");
  277 + }
235 278 if (StringUtils.isEmpty(asset.getName())) {
236 279 throw new DataValidationException("Asset name should be specified!");
237 280 }
... ...
... ... @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.Device;
24 24 import org.thingsboard.server.common.data.page.TextPageLink;
25 25 import org.thingsboard.server.dao.Dao;
26 26 import org.thingsboard.server.dao.model.DeviceEntity;
  27 +import org.thingsboard.server.dao.model.TenantDeviceTypeEntity;
27 28
28 29 /**
29 30 * The Interface DeviceDao.
... ... @@ -49,6 +50,16 @@ public interface DeviceDao extends Dao<DeviceEntity> {
49 50 List<DeviceEntity> findDevicesByTenantId(UUID tenantId, TextPageLink pageLink);
50 51
51 52 /**
  53 + * Find devices by tenantId, type and page link.
  54 + *
  55 + * @param tenantId the tenantId
  56 + * @param type the type
  57 + * @param pageLink the page link
  58 + * @return the list of device objects
  59 + */
  60 + List<DeviceEntity> findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink);
  61 +
  62 + /**
52 63 * Find devices by tenantId and devices Ids.
53 64 *
54 65 * @param tenantId the tenantId
... ... @@ -68,6 +79,18 @@ public interface DeviceDao extends Dao<DeviceEntity> {
68 79 List<DeviceEntity> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
69 80
70 81 /**
  82 + * Find devices by tenantId, customerId, type and page link.
  83 + *
  84 + * @param tenantId the tenantId
  85 + * @param customerId the customerId
  86 + * @param type the type
  87 + * @param pageLink the page link
  88 + * @return the list of device objects
  89 + */
  90 + List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink);
  91 +
  92 +
  93 + /**
71 94 * Find devices by tenantId, customerId and devices Ids.
72 95 *
73 96 * @param tenantId the tenantId
... ... @@ -85,4 +108,11 @@ public interface DeviceDao extends Dao<DeviceEntity> {
85 108 * @return the optional device object
86 109 */
87 110 Optional<DeviceEntity> findDevicesByTenantIdAndName(UUID tenantId, String name);
  111 +
  112 + /**
  113 + * Find tenants device types.
  114 + *
  115 + * @return the list of tenant device type objects
  116 + */
  117 + ListenableFuture<List<TenantDeviceTypeEntity>> findTenantDeviceTypesAsync();
88 118 }
... ...
... ... @@ -22,7 +22,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.*;
22 22
23 23 import java.util.*;
24 24
  25 +import com.datastax.driver.core.ResultSet;
  26 +import com.datastax.driver.core.ResultSetFuture;
25 27 import com.datastax.driver.core.querybuilder.Select;
  28 +import com.datastax.driver.mapping.Result;
  29 +import com.google.common.base.Function;
  30 +import com.google.common.util.concurrent.Futures;
26 31 import com.google.common.util.concurrent.ListenableFuture;
27 32 import lombok.extern.slf4j.Slf4j;
28 33 import org.springframework.stereotype.Component;
... ... @@ -32,6 +37,9 @@ import org.thingsboard.server.dao.AbstractSearchTextDao;
32 37 import org.thingsboard.server.dao.model.DeviceEntity;
33 38 import org.slf4j.Logger;
34 39 import org.slf4j.LoggerFactory;
  40 +import org.thingsboard.server.dao.model.TenantDeviceTypeEntity;
  41 +
  42 +import javax.annotation.Nullable;
35 43
36 44 @Component
37 45 @Slf4j
... ... @@ -64,6 +72,16 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
64 72 }
65 73
66 74 @Override
  75 + public List<DeviceEntity> findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) {
  76 + log.debug("Try to find devices by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
  77 + List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
  78 + Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type),
  79 + eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), pageLink);
  80 + log.trace("Found devices [{}] by tenantId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, type, pageLink);
  81 + return deviceEntities;
  82 + }
  83 +
  84 + @Override
67 85 public ListenableFuture<List<DeviceEntity>> findDevicesByTenantIdAndIdsAsync(UUID tenantId, List<UUID> deviceIds) {
68 86 log.debug("Try to find devices by tenantId [{}] and device Ids [{}]", tenantId, deviceIds);
69 87 Select select = select().from(getColumnFamilyName());
... ... @@ -75,7 +93,7 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
75 93
76 94 @Override
77 95 public List<DeviceEntity> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
78   - log.debug("Try to find devices by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
  96 + log.debug("Try to find devices by tenantId [{}], customerId [{}] and pageLink [{}]", tenantId, customerId, pageLink);
79 97 List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
80 98 Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
81 99 eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
... ... @@ -86,6 +104,19 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
86 104 }
87 105
88 106 @Override
  107 + public List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) {
  108 + log.debug("Try to find devices by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
  109 + List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
  110 + Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type),
  111 + eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
  112 + eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
  113 + pageLink);
  114 +
  115 + log.trace("Found devices [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, type, pageLink);
  116 + return deviceEntities;
  117 + }
  118 +
  119 + @Override
89 120 public ListenableFuture<List<DeviceEntity>> findDevicesByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List<UUID> deviceIds) {
90 121 log.debug("Try to find devices by tenantId [{}], customerId [{}] and device Ids [{}]", tenantId, customerId, deviceIds);
91 122 Select select = select().from(getColumnFamilyName());
... ... @@ -105,4 +136,24 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
105 136 return Optional.ofNullable(findOneByStatement(query));
106 137 }
107 138
  139 + @Override
  140 + public ListenableFuture<List<TenantDeviceTypeEntity>> findTenantDeviceTypesAsync() {
  141 + Select statement = select().distinct().column(DEVICE_TYPE_PROPERTY).column(DEVICE_TENANT_ID_PROPERTY).from(DEVICE_TYPES_BY_TENANT_VIEW_NAME);
  142 + statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
  143 + ResultSetFuture resultSetFuture = getSession().executeAsync(statement);
  144 + ListenableFuture<List<TenantDeviceTypeEntity>> result = Futures.transform(resultSetFuture, new Function<ResultSet, List<TenantDeviceTypeEntity>>() {
  145 + @Nullable
  146 + @Override
  147 + public List<TenantDeviceTypeEntity> apply(@Nullable ResultSet resultSet) {
  148 + Result<TenantDeviceTypeEntity> result = cluster.getMapper(TenantDeviceTypeEntity.class).map(resultSet);
  149 + if (result != null) {
  150 + return result.all();
  151 + } else {
  152 + return Collections.emptyList();
  153 + }
  154 + }
  155 + });
  156 + return result;
  157 + }
  158 +
108 159 }
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.device;
17 17
18 18 import com.google.common.util.concurrent.ListenableFuture;
19 19 import org.thingsboard.server.common.data.Device;
  20 +import org.thingsboard.server.common.data.TenantDeviceType;
20 21 import org.thingsboard.server.common.data.id.CustomerId;
21 22 import org.thingsboard.server.common.data.id.DeviceId;
22 23 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -44,16 +45,22 @@ public interface DeviceService {
44 45
45 46 TextPageData<Device> findDevicesByTenantId(TenantId tenantId, TextPageLink pageLink);
46 47
  48 + TextPageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink);
  49 +
47 50 ListenableFuture<List<Device>> findDevicesByTenantIdAndIdsAsync(TenantId tenantId, List<DeviceId> deviceIds);
48 51
49 52 void deleteDevicesByTenantId(TenantId tenantId);
50 53
51 54 TextPageData<Device> findDevicesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
52 55
  56 + TextPageData<Device> findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink);
  57 +
53 58 ListenableFuture<List<Device>> findDevicesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<DeviceId> deviceIds);
54 59
55 60 void unassignCustomerDevices(TenantId tenantId, CustomerId customerId);
56 61
57 62 ListenableFuture<List<Device>> findDevicesByQuery(DeviceSearchQuery query);
58 63
  64 + ListenableFuture<List<TenantDeviceType>> findDeviceTypesByTenantId(TenantId tenantId);
  65 +
59 66 }
... ...
... ... @@ -26,6 +26,7 @@ import org.springframework.stereotype.Service;
26 26 import org.springframework.util.StringUtils;
27 27 import org.thingsboard.server.common.data.Device;
28 28 import org.thingsboard.server.common.data.EntityType;
  29 +import org.thingsboard.server.common.data.TenantDeviceType;
29 30 import org.thingsboard.server.common.data.id.CustomerId;
30 31 import org.thingsboard.server.common.data.id.DeviceId;
31 32 import org.thingsboard.server.common.data.id.EntityId;
... ... @@ -40,6 +41,7 @@ import org.thingsboard.server.dao.entity.BaseEntityService;
40 41 import org.thingsboard.server.dao.exception.DataValidationException;
41 42 import org.thingsboard.server.dao.model.CustomerEntity;
42 43 import org.thingsboard.server.dao.model.DeviceEntity;
  44 +import org.thingsboard.server.dao.model.TenantDeviceTypeEntity;
43 45 import org.thingsboard.server.dao.model.TenantEntity;
44 46 import org.thingsboard.server.dao.relation.EntitySearchDirection;
45 47 import org.thingsboard.server.dao.service.DataValidator;
... ... @@ -47,9 +49,7 @@ import org.thingsboard.server.dao.service.PaginatedRemover;
47 49 import org.thingsboard.server.dao.tenant.TenantDao;
48 50
49 51 import javax.annotation.Nullable;
50   -import java.util.ArrayList;
51   -import java.util.List;
52   -import java.util.Optional;
  52 +import java.util.*;
53 53 import java.util.stream.Collectors;
54 54
55 55 import static org.thingsboard.server.dao.DaoUtil.*;
... ... @@ -148,7 +148,18 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
148 148 validatePageLink(pageLink, "Incorrect page link " + pageLink);
149 149 List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantId(tenantId.getId(), pageLink);
150 150 List<Device> devices = convertDataList(deviceEntities);
151   - return new TextPageData<Device>(devices, pageLink);
  151 + return new TextPageData<>(devices, pageLink);
  152 + }
  153 +
  154 + @Override
  155 + public TextPageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink) {
  156 + log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
  157 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  158 + validateString(type, "Incorrect type " + type);
  159 + validatePageLink(pageLink, "Incorrect page link " + pageLink);
  160 + List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndType(tenantId.getId(), type, pageLink);
  161 + List<Device> devices = convertDataList(deviceEntities);
  162 + return new TextPageData<>(devices, pageLink);
152 163 }
153 164
154 165 @Override
... ... @@ -176,7 +187,19 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
176 187 validatePageLink(pageLink, "Incorrect page link " + pageLink);
177 188 List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
178 189 List<Device> devices = convertDataList(deviceEntities);
179   - return new TextPageData<Device>(devices, pageLink);
  190 + return new TextPageData<>(devices, pageLink);
  191 + }
  192 +
  193 + @Override
  194 + public TextPageData<Device> findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink) {
  195 + log.trace("Executing findDevicesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
  196 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  197 + validateId(customerId, "Incorrect customerId " + customerId);
  198 + validateString(type, "Incorrect type " + type);
  199 + validatePageLink(pageLink, "Incorrect page link " + pageLink);
  200 + List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
  201 + List<Device> devices = convertDataList(deviceEntities);
  202 + return new TextPageData<>(devices, pageLink);
180 203 }
181 204
182 205 @Override
... ... @@ -224,6 +247,25 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
224 247 return devices;
225 248 }
226 249
  250 + @Override
  251 + public ListenableFuture<List<TenantDeviceType>> findDeviceTypesByTenantId(TenantId tenantId) {
  252 + log.trace("Executing findDeviceTypesByTenantId, tenantId [{}]", tenantId);
  253 + validateId(tenantId, "Incorrect tenantId " + tenantId);
  254 + ListenableFuture<List<TenantDeviceTypeEntity>> tenantDeviceTypeEntities = deviceDao.findTenantDeviceTypesAsync();
  255 + ListenableFuture<List<TenantDeviceType>> tenantDeviceTypes = Futures.transform(tenantDeviceTypeEntities,
  256 + (Function<List<TenantDeviceTypeEntity>, List<TenantDeviceType>>) deviceTypeEntities -> {
  257 + List<TenantDeviceType> deviceTypes = new ArrayList<>();
  258 + for (TenantDeviceTypeEntity deviceTypeEntity : deviceTypeEntities) {
  259 + if (deviceTypeEntity.getTenantId().equals(tenantId.getId())) {
  260 + deviceTypes.add(deviceTypeEntity.toTenantDeviceType());
  261 + }
  262 + }
  263 + deviceTypes.sort((TenantDeviceType o1, TenantDeviceType o2) -> o1.getType().compareTo(o2.getType()));
  264 + return deviceTypes;
  265 + });
  266 + return tenantDeviceTypes;
  267 + }
  268 +
227 269 private DataValidator<Device> deviceValidator =
228 270 new DataValidator<Device>() {
229 271
... ... @@ -249,6 +291,9 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
249 291
250 292 @Override
251 293 protected void validateDataImpl(Device device) {
  294 + if (StringUtils.isEmpty(device.getType())) {
  295 + throw new DataValidationException("Device type should be specified!");
  296 + }
252 297 if (StringUtils.isEmpty(device.getName())) {
253 298 throw new DataValidationException("Device name should be specified!");
254 299 }
... ...
... ... @@ -49,12 +49,13 @@ public final class AssetEntity implements SearchTextEntity<Asset> {
49 49 @Column(name = ASSET_CUSTOMER_ID_PROPERTY)
50 50 private UUID customerId;
51 51
52   - @Column(name = ASSET_NAME_PROPERTY)
53   - private String name;
54   -
  52 + @PartitionKey(value = 3)
55 53 @Column(name = ASSET_TYPE_PROPERTY)
56 54 private String type;
57 55
  56 + @Column(name = ASSET_NAME_PROPERTY)
  57 + private String name;
  58 +
58 59 @Column(name = SEARCH_TEXT_PROPERTY)
59 60 private String searchText;
60 61
... ...
... ... @@ -49,12 +49,13 @@ public final class DeviceEntity implements SearchTextEntity<Device> {
49 49 @Column(name = DEVICE_CUSTOMER_ID_PROPERTY)
50 50 private UUID customerId;
51 51
52   - @Column(name = DEVICE_NAME_PROPERTY)
53   - private String name;
54   -
  52 + @PartitionKey(value = 3)
55 53 @Column(name = DEVICE_TYPE_PROPERTY)
56 54 private String type;
57 55
  56 + @Column(name = DEVICE_NAME_PROPERTY)
  57 + private String name;
  58 +
58 59 @Column(name = SEARCH_TEXT_PROPERTY)
59 60 private String searchText;
60 61
... ...
... ... @@ -124,8 +124,11 @@ public class ModelConstants {
124 124 public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
125 125
126 126 public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
  127 + public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
127 128 public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text";
  129 + public static final String DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_by_type_and_search_text";
128 130 public static final String DEVICE_BY_TENANT_AND_NAME_VIEW_NAME = "device_by_tenant_and_name";
  131 + public static final String DEVICE_TYPES_BY_TENANT_VIEW_NAME = "device_types_by_tenant";
129 132
130 133 /**
131 134 * Cassandra asset constants.
... ... @@ -138,8 +141,11 @@ public class ModelConstants {
138 141 public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
139 142
140 143 public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text";
  144 + public static final String ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_by_type_and_search_text";
141 145 public static final String ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_and_search_text";
  146 + public static final String ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_by_type_and_search_text";
142 147 public static final String ASSET_BY_TENANT_AND_NAME_VIEW_NAME = "asset_by_tenant_and_name";
  148 + public static final String ASSET_TYPES_BY_TENANT_VIEW_NAME = "asset_types_by_tenant";
143 149
144 150 /**
145 151 * Cassandra alarm constants.
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package org.thingsboard.server.dao.model;
  18 +
  19 +import com.datastax.driver.mapping.annotations.Column;
  20 +import com.datastax.driver.mapping.annotations.PartitionKey;
  21 +import com.datastax.driver.mapping.annotations.Table;
  22 +import com.datastax.driver.mapping.annotations.Transient;
  23 +import org.thingsboard.server.common.data.asset.TenantAssetType;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +
  26 +import java.util.UUID;
  27 +
  28 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  29 +
  30 +@Table(name = ASSET_TYPES_BY_TENANT_VIEW_NAME)
  31 +public class TenantAssetTypeEntity {
  32 +
  33 + @Transient
  34 + private static final long serialVersionUID = -1268181161886910152L;
  35 +
  36 + @PartitionKey(value = 0)
  37 + @Column(name = ASSET_TYPE_PROPERTY)
  38 + private String type;
  39 +
  40 + @PartitionKey(value = 1)
  41 + @Column(name = ASSET_TENANT_ID_PROPERTY)
  42 + private UUID tenantId;
  43 +
  44 + public TenantAssetTypeEntity() {
  45 + super();
  46 + }
  47 +
  48 + public TenantAssetTypeEntity(TenantAssetType tenantAssetType) {
  49 + this.type = tenantAssetType.getType();
  50 + if (tenantAssetType.getTenantId() != null) {
  51 + this.tenantId = tenantAssetType.getTenantId().getId();
  52 + }
  53 + }
  54 +
  55 + public String getType() {
  56 + return type;
  57 + }
  58 +
  59 + public void setType(String type) {
  60 + this.type = type;
  61 + }
  62 +
  63 + public UUID getTenantId() {
  64 + return tenantId;
  65 + }
  66 +
  67 + public void setTenantId(UUID tenantId) {
  68 + this.tenantId = tenantId;
  69 + }
  70 +
  71 + @Override
  72 + public int hashCode() {
  73 + int result = type != null ? type.hashCode() : 0;
  74 + result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
  75 + return result;
  76 + }
  77 +
  78 + @Override
  79 + public boolean equals(Object o) {
  80 + if (this == o) return true;
  81 + if (o == null || getClass() != o.getClass()) return false;
  82 +
  83 + TenantAssetTypeEntity that = (TenantAssetTypeEntity) o;
  84 +
  85 + if (type != null ? !type.equals(that.type) : that.type != null) return false;
  86 + return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
  87 +
  88 + }
  89 +
  90 + @Override
  91 + public String toString() {
  92 + final StringBuilder sb = new StringBuilder("TenantAssetTypeEntity{");
  93 + sb.append("type='").append(type).append('\'');
  94 + sb.append(", tenantId=").append(tenantId);
  95 + sb.append('}');
  96 + return sb.toString();
  97 + }
  98 +
  99 + public TenantAssetType toTenantAssetType() {
  100 + TenantAssetType tenantAssetType = new TenantAssetType();
  101 + tenantAssetType.setType(type);
  102 + if (tenantId != null) {
  103 + tenantAssetType.setTenantId(new TenantId(tenantId));
  104 + }
  105 + return tenantAssetType;
  106 + }
  107 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +package org.thingsboard.server.dao.model;
  18 +
  19 +import com.datastax.driver.mapping.annotations.Column;
  20 +import com.datastax.driver.mapping.annotations.PartitionKey;
  21 +import com.datastax.driver.mapping.annotations.Table;
  22 +import com.datastax.driver.mapping.annotations.Transient;
  23 +import org.thingsboard.server.common.data.TenantDeviceType;
  24 +import org.thingsboard.server.common.data.id.TenantId;
  25 +
  26 +import java.util.UUID;
  27 +
  28 +import static org.thingsboard.server.dao.model.ModelConstants.*;
  29 +
  30 +@Table(name = DEVICE_TYPES_BY_TENANT_VIEW_NAME)
  31 +public class TenantDeviceTypeEntity {
  32 +
  33 + @Transient
  34 + private static final long serialVersionUID = -1268181166886910152L;
  35 +
  36 + @PartitionKey(value = 0)
  37 + @Column(name = DEVICE_TYPE_PROPERTY)
  38 + private String type;
  39 +
  40 + @PartitionKey(value = 1)
  41 + @Column(name = DEVICE_TENANT_ID_PROPERTY)
  42 + private UUID tenantId;
  43 +
  44 + public TenantDeviceTypeEntity() {
  45 + super();
  46 + }
  47 +
  48 + public TenantDeviceTypeEntity(TenantDeviceType tenantDeviceType) {
  49 + this.type = tenantDeviceType.getType();
  50 + if (tenantDeviceType.getTenantId() != null) {
  51 + this.tenantId = tenantDeviceType.getTenantId().getId();
  52 + }
  53 + }
  54 +
  55 + public String getType() {
  56 + return type;
  57 + }
  58 +
  59 + public void setType(String type) {
  60 + this.type = type;
  61 + }
  62 +
  63 + public UUID getTenantId() {
  64 + return tenantId;
  65 + }
  66 +
  67 + public void setTenantId(UUID tenantId) {
  68 + this.tenantId = tenantId;
  69 + }
  70 +
  71 + @Override
  72 + public int hashCode() {
  73 + int result = type != null ? type.hashCode() : 0;
  74 + result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
  75 + return result;
  76 + }
  77 +
  78 + @Override
  79 + public boolean equals(Object o) {
  80 + if (this == o) return true;
  81 + if (o == null || getClass() != o.getClass()) return false;
  82 +
  83 + TenantDeviceTypeEntity that = (TenantDeviceTypeEntity) o;
  84 +
  85 + if (type != null ? !type.equals(that.type) : that.type != null) return false;
  86 + return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
  87 +
  88 + }
  89 +
  90 + @Override
  91 + public String toString() {
  92 + final StringBuilder sb = new StringBuilder("TenantDeviceTypeEntity{");
  93 + sb.append("type='").append(type).append('\'');
  94 + sb.append(", tenantId=").append(tenantId);
  95 + sb.append('}');
  96 + return sb.toString();
  97 + }
  98 +
  99 + public TenantDeviceType toTenantDeviceType() {
  100 + TenantDeviceType tenantDeviceType = new TenantDeviceType();
  101 + tenantDeviceType.setType(type);
  102 + if (tenantId != null) {
  103 + tenantDeviceType.setTenantId(new TenantId(tenantId));
  104 + }
  105 + return tenantDeviceType;
  106 + }
  107 +}
... ...
... ... @@ -149,66 +149,73 @@ VALUES (
149 149
150 150 /** Demo device **/
151 151
152   -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
  152 +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
153 153 VALUES (
154 154 minTimeuuid ( '2016-11-01 01:02:05+0000' ),
155 155 minTimeuuid ( '2016-11-01 01:02:01+0000' ),
156 156 minTimeuuid ( '2016-11-01 01:02:03+0000' ),
  157 + 'default',
157 158 'Test Device A1',
158 159 'test device a1'
159 160 );
160 161
161   -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
  162 +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
162 163 VALUES (
163 164 minTimeuuid ( '2016-11-01 01:02:05+0001' ),
164 165 minTimeuuid ( '2016-11-01 01:02:01+0000' ),
165 166 minTimeuuid ( '2016-11-01 01:02:03+0000' ),
  167 + 'default',
166 168 'Test Device A2',
167 169 'test device a2'
168 170 );
169 171
170   -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
  172 +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
171 173 VALUES (
172 174 minTimeuuid ( '2016-11-01 01:02:05+0002' ),
173 175 minTimeuuid ( '2016-11-01 01:02:01+0000' ),
174 176 minTimeuuid ( '2016-11-01 01:02:03+0000' ),
  177 + 'default',
175 178 'Test Device A3',
176 179 'test device a3'
177 180 );
178 181
179   -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
  182 +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
180 183 VALUES (
181 184 minTimeuuid ( '2016-11-01 01:02:05+0003' ),
182 185 minTimeuuid ( '2016-11-01 01:02:01+0000' ),
183 186 minTimeuuid ( '2016-11-01 01:02:03+0001' ),
  187 + 'default',
184 188 'Test Device B1',
185 189 'test device b1'
186 190 );
187 191
188   -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
  192 +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
189 193 VALUES (
190 194 minTimeuuid ( '2016-11-01 01:02:05+0004' ),
191 195 minTimeuuid ( '2016-11-01 01:02:01+0000' ),
192 196 minTimeuuid ( '2016-11-01 01:02:03+0002' ),
  197 + 'default',
193 198 'Test Device C1',
194 199 'test device c1'
195 200 );
196 201
197   -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text, additional_info)
  202 +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text, additional_info)
198 203 VALUES (
199 204 c8f1a6f0-b993-11e6-8a04-9ff4e1b7933c,
200 205 minTimeuuid ( '2016-11-01 01:02:01+0000' ),
201 206 minTimeuuid ( 0 ),
  207 + 'default',
202 208 'DHT11 Demo Device',
203 209 'dht11 demo device',
204 210 '{"description":"Demo device that is used in sample applications that upload data from DHT11 temperature and humidity sensor"}'
205 211 );
206 212
207   -INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text, additional_info)
  213 +INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text, additional_info)
208 214 VALUES (
209 215 c8f1a6f0-b993-11e6-8a04-9ff4e1b7933d,
210 216 minTimeuuid ( '2016-11-01 01:02:01+0000' ),
211 217 minTimeuuid ( 0 ),
  218 + 'default',
212 219 'Raspberry Pi Demo Device',
213 220 'raspberry pi demo device',
214 221 '{"description":"Demo device that is used in Raspberry Pi GPIO control sample application"}'
... ...
... ... @@ -152,36 +152,57 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.customer_by_tenant_and_search
152 152 WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
153 153
154 154 CREATE TABLE IF NOT EXISTS thingsboard.device (
155   - id timeuuid,
156   - tenant_id timeuuid,
157   - customer_id timeuuid,
158   - name text,
159   - type text,
160   - search_text text,
161   - additional_info text,
162   - PRIMARY KEY (id, tenant_id, customer_id)
  155 + id timeuuid,
  156 + tenant_id timeuuid,
  157 + customer_id timeuuid,
  158 + name text,
  159 + type text,
  160 + search_text text,
  161 + additional_info text,
  162 + PRIMARY KEY (id, tenant_id, customer_id, type)
163 163 );
164 164
165 165 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_name AS
166   - SELECT *
167   - from thingsboard.device
168   - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
169   - PRIMARY KEY ( tenant_id, name, id, customer_id)
170   - WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
  166 + SELECT *
  167 + from thingsboard.device
  168 + 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
  169 + PRIMARY KEY ( tenant_id, name, id, customer_id, type)
  170 + WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
171 171
172 172 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_search_text AS
173   - SELECT *
174   - from thingsboard.device
175   - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
176   - PRIMARY KEY ( tenant_id, search_text, id, customer_id)
177   - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
  173 + SELECT *
  174 + from thingsboard.device
  175 + 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
  176 + PRIMARY KEY ( tenant_id, search_text, id, customer_id, type)
  177 + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
  178 +
  179 +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_by_type_and_search_text AS
  180 + SELECT *
  181 + from thingsboard.device
  182 + 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
  183 + PRIMARY KEY ( tenant_id, type, search_text, id, customer_id)
  184 + WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC);
178 185
179 186 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_and_search_text AS
180   - SELECT *
181   - from thingsboard.device
182   - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
183   - PRIMARY KEY ( customer_id, tenant_id, search_text, id )
184   - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
  187 + SELECT *
  188 + from thingsboard.device
  189 + 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
  190 + PRIMARY KEY ( customer_id, tenant_id, search_text, id, type )
  191 + WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
  192 +
  193 +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_by_type_and_search_text AS
  194 + SELECT *
  195 + from thingsboard.device
  196 + 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
  197 + PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
  198 + WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
  199 +
  200 +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_types_by_tenant AS
  201 + SELECT *
  202 + from thingsboard.device
  203 + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL
  204 + PRIMARY KEY ( (type, tenant_id), id, customer_id)
  205 + WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC);
185 206
186 207 CREATE TABLE IF NOT EXISTS thingsboard.device_credentials (
187 208 id timeuuid PRIMARY KEY,
... ... @@ -203,38 +224,58 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_credentials_by_credent
203 224 WHERE credentials_id IS NOT NULL AND id IS NOT NULL
204 225 PRIMARY KEY ( credentials_id, id );
205 226
206   -
207 227 CREATE TABLE IF NOT EXISTS thingsboard.asset (
208   - id timeuuid,
209   - tenant_id timeuuid,
210   - customer_id timeuuid,
211   - name text,
212   - type text,
213   - search_text text,
214   - additional_info text,
215   - PRIMARY KEY (id, tenant_id, customer_id)
  228 + id timeuuid,
  229 + tenant_id timeuuid,
  230 + customer_id timeuuid,
  231 + name text,
  232 + type text,
  233 + search_text text,
  234 + additional_info text,
  235 + PRIMARY KEY (id, tenant_id, customer_id, type)
216 236 );
217 237
218 238 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_name AS
219   - SELECT *
220   - from thingsboard.asset
221   - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
222   - PRIMARY KEY ( tenant_id, name, id, customer_id)
223   - WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
  239 + SELECT *
  240 + from thingsboard.asset
  241 + 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
  242 + PRIMARY KEY ( tenant_id, name, id, customer_id, type)
  243 + WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
224 244
225 245 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_search_text AS
226   - SELECT *
227   - from thingsboard.asset
228   - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
229   - PRIMARY KEY ( tenant_id, search_text, id, customer_id)
230   - WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
  246 + SELECT *
  247 + from thingsboard.asset
  248 + 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
  249 + PRIMARY KEY ( tenant_id, search_text, id, customer_id, type)
  250 + WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
  251 +
  252 +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_by_type_and_search_text AS
  253 + SELECT *
  254 + from thingsboard.asset
  255 + 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
  256 + PRIMARY KEY ( tenant_id, type, search_text, id, customer_id)
  257 + WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC);
231 258
232 259 CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_and_search_text AS
233   - SELECT *
234   - from thingsboard.asset
235   - WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
236   - PRIMARY KEY ( customer_id, tenant_id, search_text, id )
237   - WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
  260 + SELECT *
  261 + from thingsboard.asset
  262 + 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
  263 + PRIMARY KEY ( customer_id, tenant_id, search_text, id, type )
  264 + WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
  265 +
  266 +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_by_type_and_search_text AS
  267 + SELECT *
  268 + from thingsboard.asset
  269 + 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
  270 + PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
  271 + WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
  272 +
  273 +CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_types_by_tenant AS
  274 + SELECT *
  275 + from thingsboard.asset
  276 + WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL
  277 + PRIMARY KEY ( (type, tenant_id), id, customer_id)
  278 + WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC);
238 279
239 280 CREATE TABLE IF NOT EXISTS thingsboard.alarm (
240 281 id timeuuid,
... ...
... ... @@ -32,7 +32,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
32 32 import org.springframework.test.context.support.AnnotationConfigContextLoader;
33 33 import org.thingsboard.server.common.data.BaseData;
34 34 import org.thingsboard.server.common.data.Event;
35   -import org.thingsboard.server.common.data.alarm.AlarmStatus;
36 35 import org.thingsboard.server.common.data.id.EntityId;
37 36 import org.thingsboard.server.common.data.id.TenantId;
38 37 import org.thingsboard.server.common.data.id.UUIDBased;
... ... @@ -42,6 +41,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
42 41 import org.thingsboard.server.common.data.plugin.PluginMetaData;
43 42 import org.thingsboard.server.common.data.rule.RuleMetaData;
44 43 import org.thingsboard.server.dao.alarm.AlarmService;
  44 +import org.thingsboard.server.dao.asset.AssetService;
45 45 import org.thingsboard.server.dao.component.ComponentDescriptorService;
46 46 import org.thingsboard.server.dao.customer.CustomerService;
47 47 import org.thingsboard.server.dao.dashboard.DashboardService;
... ... @@ -90,6 +90,9 @@ public abstract class AbstractServiceTest {
90 90 protected DeviceService deviceService;
91 91
92 92 @Autowired
  93 + protected AssetService assetService;
  94 +
  95 + @Autowired
93 96 protected DeviceCredentialsService deviceCredentialsService;
94 97
95 98 @Autowired
... ...
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.service;
  17 +
  18 +import com.datastax.driver.core.utils.UUIDs;
  19 +import org.apache.commons.lang3.RandomStringUtils;
  20 +import org.junit.After;
  21 +import org.junit.Assert;
  22 +import org.junit.Before;
  23 +import org.junit.Test;
  24 +import org.thingsboard.server.common.data.Customer;
  25 +import org.thingsboard.server.common.data.Tenant;
  26 +import org.thingsboard.server.common.data.asset.Asset;
  27 +import org.thingsboard.server.common.data.asset.TenantAssetType;
  28 +import org.thingsboard.server.common.data.id.CustomerId;
  29 +import org.thingsboard.server.common.data.id.TenantId;
  30 +import org.thingsboard.server.common.data.page.TextPageData;
  31 +import org.thingsboard.server.common.data.page.TextPageLink;
  32 +import org.thingsboard.server.dao.exception.DataValidationException;
  33 +
  34 +import java.util.ArrayList;
  35 +import java.util.Collections;
  36 +import java.util.List;
  37 +
  38 +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
  39 +
  40 +public class BaseAssetServiceTest extends AbstractServiceTest {
  41 +
  42 + private IdComparator<Asset> idComparator = new IdComparator<>();
  43 +
  44 + private TenantId tenantId;
  45 +
  46 + @Before
  47 + public void before() {
  48 + Tenant tenant = new Tenant();
  49 + tenant.setTitle("My tenant");
  50 + Tenant savedTenant = tenantService.saveTenant(tenant);
  51 + Assert.assertNotNull(savedTenant);
  52 + tenantId = savedTenant.getId();
  53 + }
  54 +
  55 + @After
  56 + public void after() {
  57 + tenantService.deleteTenant(tenantId);
  58 + }
  59 +
  60 + @Test
  61 + public void testSaveAsset() {
  62 + Asset asset = new Asset();
  63 + asset.setTenantId(tenantId);
  64 + asset.setName("My asset");
  65 + asset.setType("default");
  66 + Asset savedAsset = assetService.saveAsset(asset);
  67 +
  68 + Assert.assertNotNull(savedAsset);
  69 + Assert.assertNotNull(savedAsset.getId());
  70 + Assert.assertTrue(savedAsset.getCreatedTime() > 0);
  71 + Assert.assertEquals(asset.getTenantId(), savedAsset.getTenantId());
  72 + Assert.assertNotNull(savedAsset.getCustomerId());
  73 + Assert.assertEquals(NULL_UUID, savedAsset.getCustomerId().getId());
  74 + Assert.assertEquals(asset.getName(), savedAsset.getName());
  75 +
  76 + savedAsset.setName("My new asset");
  77 +
  78 + assetService.saveAsset(savedAsset);
  79 + Asset foundAsset = assetService.findAssetById(savedAsset.getId());
  80 + Assert.assertEquals(foundAsset.getName(), savedAsset.getName());
  81 +
  82 + assetService.deleteAsset(savedAsset.getId());
  83 + }
  84 +
  85 + @Test(expected = DataValidationException.class)
  86 + public void testSaveAssetWithEmptyName() {
  87 + Asset asset = new Asset();
  88 + asset.setTenantId(tenantId);
  89 + asset.setType("default");
  90 + assetService.saveAsset(asset);
  91 + }
  92 +
  93 + @Test(expected = DataValidationException.class)
  94 + public void testSaveAssetWithEmptyTenant() {
  95 + Asset asset = new Asset();
  96 + asset.setName("My asset");
  97 + asset.setType("default");
  98 + assetService.saveAsset(asset);
  99 + }
  100 +
  101 + @Test(expected = DataValidationException.class)
  102 + public void testSaveAssetWithInvalidTenant() {
  103 + Asset asset = new Asset();
  104 + asset.setName("My asset");
  105 + asset.setType("default");
  106 + asset.setTenantId(new TenantId(UUIDs.timeBased()));
  107 + assetService.saveAsset(asset);
  108 + }
  109 +
  110 + @Test(expected = DataValidationException.class)
  111 + public void testAssignAssetToNonExistentCustomer() {
  112 + Asset asset = new Asset();
  113 + asset.setName("My asset");
  114 + asset.setType("default");
  115 + asset.setTenantId(tenantId);
  116 + asset = assetService.saveAsset(asset);
  117 + try {
  118 + assetService.assignAssetToCustomer(asset.getId(), new CustomerId(UUIDs.timeBased()));
  119 + } finally {
  120 + assetService.deleteAsset(asset.getId());
  121 + }
  122 + }
  123 +
  124 + @Test(expected = DataValidationException.class)
  125 + public void testAssignAssetToCustomerFromDifferentTenant() {
  126 + Asset asset = new Asset();
  127 + asset.setName("My asset");
  128 + asset.setType("default");
  129 + asset.setTenantId(tenantId);
  130 + asset = assetService.saveAsset(asset);
  131 + Tenant tenant = new Tenant();
  132 + tenant.setTitle("Test different tenant");
  133 + tenant = tenantService.saveTenant(tenant);
  134 + Customer customer = new Customer();
  135 + customer.setTenantId(tenant.getId());
  136 + customer.setTitle("Test different customer");
  137 + customer = customerService.saveCustomer(customer);
  138 + try {
  139 + assetService.assignAssetToCustomer(asset.getId(), customer.getId());
  140 + } finally {
  141 + assetService.deleteAsset(asset.getId());
  142 + tenantService.deleteTenant(tenant.getId());
  143 + }
  144 + }
  145 +
  146 + @Test
  147 + public void testFindAssetById() {
  148 + Asset asset = new Asset();
  149 + asset.setTenantId(tenantId);
  150 + asset.setName("My asset");
  151 + asset.setType("default");
  152 + Asset savedAsset = assetService.saveAsset(asset);
  153 + Asset foundAsset = assetService.findAssetById(savedAsset.getId());
  154 + Assert.assertNotNull(foundAsset);
  155 + Assert.assertEquals(savedAsset, foundAsset);
  156 + assetService.deleteAsset(savedAsset.getId());
  157 + }
  158 +
  159 + @Test
  160 + public void testFindAssetTypesByTenantId() throws Exception {
  161 + List<Asset> assets = new ArrayList<>();
  162 + try {
  163 + for (int i=0;i<3;i++) {
  164 + Asset asset = new Asset();
  165 + asset.setTenantId(tenantId);
  166 + asset.setName("My asset B"+i);
  167 + asset.setType("typeB");
  168 + assets.add(assetService.saveAsset(asset));
  169 + }
  170 + for (int i=0;i<7;i++) {
  171 + Asset asset = new Asset();
  172 + asset.setTenantId(tenantId);
  173 + asset.setName("My asset C"+i);
  174 + asset.setType("typeC");
  175 + assets.add(assetService.saveAsset(asset));
  176 + }
  177 + for (int i=0;i<9;i++) {
  178 + Asset asset = new Asset();
  179 + asset.setTenantId(tenantId);
  180 + asset.setName("My asset A"+i);
  181 + asset.setType("typeA");
  182 + assets.add(assetService.saveAsset(asset));
  183 + }
  184 + List<TenantAssetType> assetTypes = assetService.findAssetTypesByTenantId(tenantId).get();
  185 + Assert.assertNotNull(assetTypes);
  186 + Assert.assertEquals(3, assetTypes.size());
  187 + Assert.assertEquals("typeA", assetTypes.get(0).getType());
  188 + Assert.assertEquals("typeB", assetTypes.get(1).getType());
  189 + Assert.assertEquals("typeC", assetTypes.get(2).getType());
  190 + } finally {
  191 + assets.forEach((asset) -> { assetService.deleteAsset(asset.getId()); });
  192 + }
  193 + }
  194 +
  195 + @Test
  196 + public void testDeleteAsset() {
  197 + Asset asset = new Asset();
  198 + asset.setTenantId(tenantId);
  199 + asset.setName("My asset");
  200 + asset.setType("default");
  201 + Asset savedAsset = assetService.saveAsset(asset);
  202 + Asset foundAsset = assetService.findAssetById(savedAsset.getId());
  203 + Assert.assertNotNull(foundAsset);
  204 + assetService.deleteAsset(savedAsset.getId());
  205 + foundAsset = assetService.findAssetById(savedAsset.getId());
  206 + Assert.assertNull(foundAsset);
  207 + }
  208 +
  209 + @Test
  210 + public void testFindAssetsByTenantId() {
  211 + Tenant tenant = new Tenant();
  212 + tenant.setTitle("Test tenant");
  213 + tenant = tenantService.saveTenant(tenant);
  214 +
  215 + TenantId tenantId = tenant.getId();
  216 +
  217 + List<Asset> assets = new ArrayList<>();
  218 + for (int i=0;i<178;i++) {
  219 + Asset asset = new Asset();
  220 + asset.setTenantId(tenantId);
  221 + asset.setName("Asset"+i);
  222 + asset.setType("default");
  223 + assets.add(assetService.saveAsset(asset));
  224 + }
  225 +
  226 + List<Asset> loadedAssets = new ArrayList<>();
  227 + TextPageLink pageLink = new TextPageLink(23);
  228 + TextPageData<Asset> pageData = null;
  229 + do {
  230 + pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
  231 + loadedAssets.addAll(pageData.getData());
  232 + if (pageData.hasNext()) {
  233 + pageLink = pageData.getNextPageLink();
  234 + }
  235 + } while (pageData.hasNext());
  236 +
  237 + Collections.sort(assets, idComparator);
  238 + Collections.sort(loadedAssets, idComparator);
  239 +
  240 + Assert.assertEquals(assets, loadedAssets);
  241 +
  242 + assetService.deleteAssetsByTenantId(tenantId);
  243 +
  244 + pageLink = new TextPageLink(33);
  245 + pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
  246 + Assert.assertFalse(pageData.hasNext());
  247 + Assert.assertTrue(pageData.getData().isEmpty());
  248 +
  249 + tenantService.deleteTenant(tenantId);
  250 + }
  251 +
  252 + @Test
  253 + public void testFindAssetsByTenantIdAndName() {
  254 + String title1 = "Asset title 1";
  255 + List<Asset> assetsTitle1 = new ArrayList<>();
  256 + for (int i=0;i<143;i++) {
  257 + Asset asset = new Asset();
  258 + asset.setTenantId(tenantId);
  259 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  260 + String name = title1+suffix;
  261 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  262 + asset.setName(name);
  263 + asset.setType("default");
  264 + assetsTitle1.add(assetService.saveAsset(asset));
  265 + }
  266 + String title2 = "Asset title 2";
  267 + List<Asset> assetsTitle2 = new ArrayList<>();
  268 + for (int i=0;i<175;i++) {
  269 + Asset asset = new Asset();
  270 + asset.setTenantId(tenantId);
  271 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  272 + String name = title2+suffix;
  273 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  274 + asset.setName(name);
  275 + asset.setType("default");
  276 + assetsTitle2.add(assetService.saveAsset(asset));
  277 + }
  278 +
  279 + List<Asset> loadedAssetsTitle1 = new ArrayList<>();
  280 + TextPageLink pageLink = new TextPageLink(15, title1);
  281 + TextPageData<Asset> pageData = null;
  282 + do {
  283 + pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
  284 + loadedAssetsTitle1.addAll(pageData.getData());
  285 + if (pageData.hasNext()) {
  286 + pageLink = pageData.getNextPageLink();
  287 + }
  288 + } while (pageData.hasNext());
  289 +
  290 + Collections.sort(assetsTitle1, idComparator);
  291 + Collections.sort(loadedAssetsTitle1, idComparator);
  292 +
  293 + Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
  294 +
  295 + List<Asset> loadedAssetsTitle2 = new ArrayList<>();
  296 + pageLink = new TextPageLink(4, title2);
  297 + do {
  298 + pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
  299 + loadedAssetsTitle2.addAll(pageData.getData());
  300 + if (pageData.hasNext()) {
  301 + pageLink = pageData.getNextPageLink();
  302 + }
  303 + } while (pageData.hasNext());
  304 +
  305 + Collections.sort(assetsTitle2, idComparator);
  306 + Collections.sort(loadedAssetsTitle2, idComparator);
  307 +
  308 + Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
  309 +
  310 + for (Asset asset : loadedAssetsTitle1) {
  311 + assetService.deleteAsset(asset.getId());
  312 + }
  313 +
  314 + pageLink = new TextPageLink(4, title1);
  315 + pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
  316 + Assert.assertFalse(pageData.hasNext());
  317 + Assert.assertEquals(0, pageData.getData().size());
  318 +
  319 + for (Asset asset : loadedAssetsTitle2) {
  320 + assetService.deleteAsset(asset.getId());
  321 + }
  322 +
  323 + pageLink = new TextPageLink(4, title2);
  324 + pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
  325 + Assert.assertFalse(pageData.hasNext());
  326 + Assert.assertEquals(0, pageData.getData().size());
  327 + }
  328 +
  329 + @Test
  330 + public void testFindAssetsByTenantIdAndType() {
  331 + String title1 = "Asset title 1";
  332 + String type1 = "typeA";
  333 + List<Asset> assetsType1 = new ArrayList<>();
  334 + for (int i=0;i<143;i++) {
  335 + Asset asset = new Asset();
  336 + asset.setTenantId(tenantId);
  337 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  338 + String name = title1+suffix;
  339 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  340 + asset.setName(name);
  341 + asset.setType(type1);
  342 + assetsType1.add(assetService.saveAsset(asset));
  343 + }
  344 + String title2 = "Asset title 2";
  345 + String type2 = "typeB";
  346 + List<Asset> assetsType2 = new ArrayList<>();
  347 + for (int i=0;i<175;i++) {
  348 + Asset asset = new Asset();
  349 + asset.setTenantId(tenantId);
  350 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  351 + String name = title2+suffix;
  352 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  353 + asset.setName(name);
  354 + asset.setType(type2);
  355 + assetsType2.add(assetService.saveAsset(asset));
  356 + }
  357 +
  358 + List<Asset> loadedAssetsType1 = new ArrayList<>();
  359 + TextPageLink pageLink = new TextPageLink(15);
  360 + TextPageData<Asset> pageData = null;
  361 + do {
  362 + pageData = assetService.findAssetsByTenantIdAndType(tenantId, type1, pageLink);
  363 + loadedAssetsType1.addAll(pageData.getData());
  364 + if (pageData.hasNext()) {
  365 + pageLink = pageData.getNextPageLink();
  366 + }
  367 + } while (pageData.hasNext());
  368 +
  369 + Collections.sort(assetsType1, idComparator);
  370 + Collections.sort(loadedAssetsType1, idComparator);
  371 +
  372 + Assert.assertEquals(assetsType1, loadedAssetsType1);
  373 +
  374 + List<Asset> loadedAssetsType2 = new ArrayList<>();
  375 + pageLink = new TextPageLink(4);
  376 + do {
  377 + pageData = assetService.findAssetsByTenantIdAndType(tenantId, type2, pageLink);
  378 + loadedAssetsType2.addAll(pageData.getData());
  379 + if (pageData.hasNext()) {
  380 + pageLink = pageData.getNextPageLink();
  381 + }
  382 + } while (pageData.hasNext());
  383 +
  384 + Collections.sort(assetsType2, idComparator);
  385 + Collections.sort(loadedAssetsType2, idComparator);
  386 +
  387 + Assert.assertEquals(assetsType2, loadedAssetsType2);
  388 +
  389 + for (Asset asset : loadedAssetsType1) {
  390 + assetService.deleteAsset(asset.getId());
  391 + }
  392 +
  393 + pageLink = new TextPageLink(4);
  394 + pageData = assetService.findAssetsByTenantIdAndType(tenantId, type1, pageLink);
  395 + Assert.assertFalse(pageData.hasNext());
  396 + Assert.assertEquals(0, pageData.getData().size());
  397 +
  398 + for (Asset asset : loadedAssetsType2) {
  399 + assetService.deleteAsset(asset.getId());
  400 + }
  401 +
  402 + pageLink = new TextPageLink(4);
  403 + pageData = assetService.findAssetsByTenantIdAndType(tenantId, type2, pageLink);
  404 + Assert.assertFalse(pageData.hasNext());
  405 + Assert.assertEquals(0, pageData.getData().size());
  406 + }
  407 +
  408 + @Test
  409 + public void testFindAssetsByTenantIdAndCustomerId() {
  410 + Tenant tenant = new Tenant();
  411 + tenant.setTitle("Test tenant");
  412 + tenant = tenantService.saveTenant(tenant);
  413 +
  414 + TenantId tenantId = tenant.getId();
  415 +
  416 + Customer customer = new Customer();
  417 + customer.setTitle("Test customer");
  418 + customer.setTenantId(tenantId);
  419 + customer = customerService.saveCustomer(customer);
  420 + CustomerId customerId = customer.getId();
  421 +
  422 + List<Asset> assets = new ArrayList<>();
  423 + for (int i=0;i<278;i++) {
  424 + Asset asset = new Asset();
  425 + asset.setTenantId(tenantId);
  426 + asset.setName("Asset"+i);
  427 + asset.setType("default");
  428 + asset = assetService.saveAsset(asset);
  429 + assets.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
  430 + }
  431 +
  432 + List<Asset> loadedAssets = new ArrayList<>();
  433 + TextPageLink pageLink = new TextPageLink(23);
  434 + TextPageData<Asset> pageData = null;
  435 + do {
  436 + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  437 + loadedAssets.addAll(pageData.getData());
  438 + if (pageData.hasNext()) {
  439 + pageLink = pageData.getNextPageLink();
  440 + }
  441 + } while (pageData.hasNext());
  442 +
  443 + Collections.sort(assets, idComparator);
  444 + Collections.sort(loadedAssets, idComparator);
  445 +
  446 + Assert.assertEquals(assets, loadedAssets);
  447 +
  448 + assetService.unassignCustomerAssets(tenantId, customerId);
  449 +
  450 + pageLink = new TextPageLink(33);
  451 + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  452 + Assert.assertFalse(pageData.hasNext());
  453 + Assert.assertTrue(pageData.getData().isEmpty());
  454 +
  455 + tenantService.deleteTenant(tenantId);
  456 + }
  457 +
  458 + @Test
  459 + public void testFindAssetsByTenantIdCustomerIdAndName() {
  460 +
  461 + Customer customer = new Customer();
  462 + customer.setTitle("Test customer");
  463 + customer.setTenantId(tenantId);
  464 + customer = customerService.saveCustomer(customer);
  465 + CustomerId customerId = customer.getId();
  466 +
  467 + String title1 = "Asset title 1";
  468 + List<Asset> assetsTitle1 = new ArrayList<>();
  469 + for (int i=0;i<175;i++) {
  470 + Asset asset = new Asset();
  471 + asset.setTenantId(tenantId);
  472 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  473 + String name = title1+suffix;
  474 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  475 + asset.setName(name);
  476 + asset.setType("default");
  477 + asset = assetService.saveAsset(asset);
  478 + assetsTitle1.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
  479 + }
  480 + String title2 = "Asset title 2";
  481 + List<Asset> assetsTitle2 = new ArrayList<>();
  482 + for (int i=0;i<143;i++) {
  483 + Asset asset = new Asset();
  484 + asset.setTenantId(tenantId);
  485 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  486 + String name = title2+suffix;
  487 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  488 + asset.setName(name);
  489 + asset.setType("default");
  490 + asset = assetService.saveAsset(asset);
  491 + assetsTitle2.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
  492 + }
  493 +
  494 + List<Asset> loadedAssetsTitle1 = new ArrayList<>();
  495 + TextPageLink pageLink = new TextPageLink(15, title1);
  496 + TextPageData<Asset> pageData = null;
  497 + do {
  498 + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  499 + loadedAssetsTitle1.addAll(pageData.getData());
  500 + if (pageData.hasNext()) {
  501 + pageLink = pageData.getNextPageLink();
  502 + }
  503 + } while (pageData.hasNext());
  504 +
  505 + Collections.sort(assetsTitle1, idComparator);
  506 + Collections.sort(loadedAssetsTitle1, idComparator);
  507 +
  508 + Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
  509 +
  510 + List<Asset> loadedAssetsTitle2 = new ArrayList<>();
  511 + pageLink = new TextPageLink(4, title2);
  512 + do {
  513 + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  514 + loadedAssetsTitle2.addAll(pageData.getData());
  515 + if (pageData.hasNext()) {
  516 + pageLink = pageData.getNextPageLink();
  517 + }
  518 + } while (pageData.hasNext());
  519 +
  520 + Collections.sort(assetsTitle2, idComparator);
  521 + Collections.sort(loadedAssetsTitle2, idComparator);
  522 +
  523 + Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
  524 +
  525 + for (Asset asset : loadedAssetsTitle1) {
  526 + assetService.deleteAsset(asset.getId());
  527 + }
  528 +
  529 + pageLink = new TextPageLink(4, title1);
  530 + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  531 + Assert.assertFalse(pageData.hasNext());
  532 + Assert.assertEquals(0, pageData.getData().size());
  533 +
  534 + for (Asset asset : loadedAssetsTitle2) {
  535 + assetService.deleteAsset(asset.getId());
  536 + }
  537 +
  538 + pageLink = new TextPageLink(4, title2);
  539 + pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
  540 + Assert.assertFalse(pageData.hasNext());
  541 + Assert.assertEquals(0, pageData.getData().size());
  542 + customerService.deleteCustomer(customerId);
  543 + }
  544 +
  545 + @Test
  546 + public void testFindAssetsByTenantIdCustomerIdAndType() {
  547 +
  548 + Customer customer = new Customer();
  549 + customer.setTitle("Test customer");
  550 + customer.setTenantId(tenantId);
  551 + customer = customerService.saveCustomer(customer);
  552 + CustomerId customerId = customer.getId();
  553 +
  554 + String title1 = "Asset title 1";
  555 + String type1 = "typeC";
  556 + List<Asset> assetsType1 = new ArrayList<>();
  557 + for (int i=0;i<175;i++) {
  558 + Asset asset = new Asset();
  559 + asset.setTenantId(tenantId);
  560 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  561 + String name = title1+suffix;
  562 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  563 + asset.setName(name);
  564 + asset.setType(type1);
  565 + asset = assetService.saveAsset(asset);
  566 + assetsType1.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
  567 + }
  568 + String title2 = "Asset title 2";
  569 + String type2 = "typeD";
  570 + List<Asset> assetsType2 = new ArrayList<>();
  571 + for (int i=0;i<143;i++) {
  572 + Asset asset = new Asset();
  573 + asset.setTenantId(tenantId);
  574 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  575 + String name = title2+suffix;
  576 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  577 + asset.setName(name);
  578 + asset.setType(type2);
  579 + asset = assetService.saveAsset(asset);
  580 + assetsType2.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
  581 + }
  582 +
  583 + List<Asset> loadedAssetsType1 = new ArrayList<>();
  584 + TextPageLink pageLink = new TextPageLink(15);
  585 + TextPageData<Asset> pageData = null;
  586 + do {
  587 + pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
  588 + loadedAssetsType1.addAll(pageData.getData());
  589 + if (pageData.hasNext()) {
  590 + pageLink = pageData.getNextPageLink();
  591 + }
  592 + } while (pageData.hasNext());
  593 +
  594 + Collections.sort(assetsType1, idComparator);
  595 + Collections.sort(loadedAssetsType1, idComparator);
  596 +
  597 + Assert.assertEquals(assetsType1, loadedAssetsType1);
  598 +
  599 + List<Asset> loadedAssetsType2 = new ArrayList<>();
  600 + pageLink = new TextPageLink(4);
  601 + do {
  602 + pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
  603 + loadedAssetsType2.addAll(pageData.getData());
  604 + if (pageData.hasNext()) {
  605 + pageLink = pageData.getNextPageLink();
  606 + }
  607 + } while (pageData.hasNext());
  608 +
  609 + Collections.sort(assetsType2, idComparator);
  610 + Collections.sort(loadedAssetsType2, idComparator);
  611 +
  612 + Assert.assertEquals(assetsType2, loadedAssetsType2);
  613 +
  614 + for (Asset asset : loadedAssetsType1) {
  615 + assetService.deleteAsset(asset.getId());
  616 + }
  617 +
  618 + pageLink = new TextPageLink(4);
  619 + pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
  620 + Assert.assertFalse(pageData.hasNext());
  621 + Assert.assertEquals(0, pageData.getData().size());
  622 +
  623 + for (Asset asset : loadedAssetsType2) {
  624 + assetService.deleteAsset(asset.getId());
  625 + }
  626 +
  627 + pageLink = new TextPageLink(4);
  628 + pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
  629 + Assert.assertFalse(pageData.hasNext());
  630 + Assert.assertEquals(0, pageData.getData().size());
  631 + customerService.deleteCustomer(customerId);
  632 + }
  633 +
  634 +}
... ...
... ... @@ -58,6 +58,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
58 58 public void testSaveDeviceCredentialsWithEmptyDevice() {
59 59 Device device = new Device();
60 60 device.setName("My device");
  61 + device.setType("default");
61 62 device.setTenantId(tenantId);
62 63 device = deviceService.saveDevice(device);
63 64 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
... ... @@ -73,6 +74,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
73 74 public void testSaveDeviceCredentialsWithEmptyCredentialsType() {
74 75 Device device = new Device();
75 76 device.setName("My device");
  77 + device.setType("default");
76 78 device.setTenantId(tenantId);
77 79 device = deviceService.saveDevice(device);
78 80 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
... ... @@ -88,6 +90,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
88 90 public void testSaveDeviceCredentialsWithEmptyCredentialsId() {
89 91 Device device = new Device();
90 92 device.setName("My device");
  93 + device.setType("default");
91 94 device.setTenantId(tenantId);
92 95 device = deviceService.saveDevice(device);
93 96 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
... ... @@ -103,6 +106,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
103 106 public void testSaveNonExistentDeviceCredentials() {
104 107 Device device = new Device();
105 108 device.setName("My device");
  109 + device.setType("default");
106 110 device.setTenantId(tenantId);
107 111 device = deviceService.saveDevice(device);
108 112 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
... ... @@ -122,6 +126,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
122 126 public void testSaveDeviceCredentialsWithNonExistentDevice() {
123 127 Device device = new Device();
124 128 device.setName("My device");
  129 + device.setType("default");
125 130 device.setTenantId(tenantId);
126 131 device = deviceService.saveDevice(device);
127 132 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
... ... @@ -137,6 +142,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
137 142 public void testSaveDeviceCredentialsWithInvalidCredemtialsIdLength() {
138 143 Device device = new Device();
139 144 device.setName("My device");
  145 + device.setType("default");
140 146 device.setTenantId(tenantId);
141 147 device = deviceService.saveDevice(device);
142 148 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
... ... @@ -153,6 +159,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
153 159 Device device = new Device();
154 160 device.setTenantId(tenantId);
155 161 device.setName("My device");
  162 + device.setType("default");
156 163 Device savedDevice = deviceService.saveDevice(device);
157 164 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId());
158 165 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
... ... @@ -166,6 +173,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
166 173 Device device = new Device();
167 174 device.setTenantId(tenantId);
168 175 device.setName("My device");
  176 + device.setType("default");
169 177 Device savedDevice = deviceService.saveDevice(device);
170 178 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId());
171 179 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
... ... @@ -181,6 +189,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
181 189 Device device = new Device();
182 190 device.setTenantId(tenantId);
183 191 device.setName("My device");
  192 + device.setType("default");
184 193 Device savedDevice = deviceService.saveDevice(device);
185 194 DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId());
186 195 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
... ...
... ... @@ -24,6 +24,7 @@ import org.junit.Test;
24 24 import org.thingsboard.server.common.data.Customer;
25 25 import org.thingsboard.server.common.data.Device;
26 26 import org.thingsboard.server.common.data.Tenant;
  27 +import org.thingsboard.server.common.data.TenantDeviceType;
27 28 import org.thingsboard.server.common.data.id.CustomerId;
28 29 import org.thingsboard.server.common.data.id.DeviceCredentialsId;
29 30 import org.thingsboard.server.common.data.id.DeviceId;
... ... @@ -37,6 +38,7 @@ import org.thingsboard.server.dao.exception.DataValidationException;
37 38 import java.util.ArrayList;
38 39 import java.util.Collections;
39 40 import java.util.List;
  41 +import java.util.concurrent.Executors;
40 42
41 43 import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
42 44
... ... @@ -65,6 +67,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
65 67 Device device = new Device();
66 68 device.setTenantId(tenantId);
67 69 device.setName("My device");
  70 + device.setType("default");
68 71 Device savedDevice = deviceService.saveDevice(device);
69 72
70 73 Assert.assertNotNull(savedDevice);
... ... @@ -95,6 +98,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
95 98 @Test(expected = DataValidationException.class)
96 99 public void testSaveDeviceWithEmptyName() {
97 100 Device device = new Device();
  101 + device.setType("default");
98 102 device.setTenantId(tenantId);
99 103 deviceService.saveDevice(device);
100 104 }
... ... @@ -103,6 +107,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
103 107 public void testSaveDeviceWithEmptyTenant() {
104 108 Device device = new Device();
105 109 device.setName("My device");
  110 + device.setType("default");
106 111 deviceService.saveDevice(device);
107 112 }
108 113
... ... @@ -110,6 +115,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
110 115 public void testSaveDeviceWithInvalidTenant() {
111 116 Device device = new Device();
112 117 device.setName("My device");
  118 + device.setType("default");
113 119 device.setTenantId(new TenantId(UUIDs.timeBased()));
114 120 deviceService.saveDevice(device);
115 121 }
... ... @@ -118,6 +124,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
118 124 public void testAssignDeviceToNonExistentCustomer() {
119 125 Device device = new Device();
120 126 device.setName("My device");
  127 + device.setType("default");
121 128 device.setTenantId(tenantId);
122 129 device = deviceService.saveDevice(device);
123 130 try {
... ... @@ -131,6 +138,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
131 138 public void testAssignDeviceToCustomerFromDifferentTenant() {
132 139 Device device = new Device();
133 140 device.setName("My device");
  141 + device.setType("default");
134 142 device.setTenantId(tenantId);
135 143 device = deviceService.saveDevice(device);
136 144 Tenant tenant = new Tenant();
... ... @@ -153,18 +161,56 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
153 161 Device device = new Device();
154 162 device.setTenantId(tenantId);
155 163 device.setName("My device");
  164 + device.setType("default");
156 165 Device savedDevice = deviceService.saveDevice(device);
157 166 Device foundDevice = deviceService.findDeviceById(savedDevice.getId());
158 167 Assert.assertNotNull(foundDevice);
159 168 Assert.assertEquals(savedDevice, foundDevice);
160 169 deviceService.deleteDevice(savedDevice.getId());
161 170 }
  171 +
  172 + @Test
  173 + public void testFindDeviceTypesByTenantId() throws Exception {
  174 + List<Device> devices = new ArrayList<>();
  175 + try {
  176 + for (int i=0;i<3;i++) {
  177 + Device device = new Device();
  178 + device.setTenantId(tenantId);
  179 + device.setName("My device B"+i);
  180 + device.setType("typeB");
  181 + devices.add(deviceService.saveDevice(device));
  182 + }
  183 + for (int i=0;i<7;i++) {
  184 + Device device = new Device();
  185 + device.setTenantId(tenantId);
  186 + device.setName("My device C"+i);
  187 + device.setType("typeC");
  188 + devices.add(deviceService.saveDevice(device));
  189 + }
  190 + for (int i=0;i<9;i++) {
  191 + Device device = new Device();
  192 + device.setTenantId(tenantId);
  193 + device.setName("My device A"+i);
  194 + device.setType("typeA");
  195 + devices.add(deviceService.saveDevice(device));
  196 + }
  197 + List<TenantDeviceType> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId).get();
  198 + Assert.assertNotNull(deviceTypes);
  199 + Assert.assertEquals(3, deviceTypes.size());
  200 + Assert.assertEquals("typeA", deviceTypes.get(0).getType());
  201 + Assert.assertEquals("typeB", deviceTypes.get(1).getType());
  202 + Assert.assertEquals("typeC", deviceTypes.get(2).getType());
  203 + } finally {
  204 + devices.forEach((device) -> { deviceService.deleteDevice(device.getId()); });
  205 + }
  206 + }
162 207
163 208 @Test
164 209 public void testDeleteDevice() {
165 210 Device device = new Device();
166 211 device.setTenantId(tenantId);
167 212 device.setName("My device");
  213 + device.setType("default");
168 214 Device savedDevice = deviceService.saveDevice(device);
169 215 Device foundDevice = deviceService.findDeviceById(savedDevice.getId());
170 216 Assert.assertNotNull(foundDevice);
... ... @@ -188,6 +234,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
188 234 Device device = new Device();
189 235 device.setTenantId(tenantId);
190 236 device.setName("Device"+i);
  237 + device.setType("default");
191 238 devices.add(deviceService.saveDevice(device));
192 239 }
193 240
... ... @@ -216,7 +263,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
216 263
217 264 tenantService.deleteTenant(tenantId);
218 265 }
219   -
  266 +
220 267 @Test
221 268 public void testFindDevicesByTenantIdAndName() {
222 269 String title1 = "Device title 1";
... ... @@ -228,6 +275,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
228 275 String name = title1+suffix;
229 276 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
230 277 device.setName(name);
  278 + device.setType("default");
231 279 devicesTitle1.add(deviceService.saveDevice(device));
232 280 }
233 281 String title2 = "Device title 2";
... ... @@ -239,6 +287,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
239 287 String name = title2+suffix;
240 288 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
241 289 device.setName(name);
  290 + device.setType("default");
242 291 devicesTitle2.add(deviceService.saveDevice(device));
243 292 }
244 293
... ... @@ -291,6 +340,85 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
291 340 Assert.assertFalse(pageData.hasNext());
292 341 Assert.assertEquals(0, pageData.getData().size());
293 342 }
  343 +
  344 + @Test
  345 + public void testFindDevicesByTenantIdAndType() {
  346 + String title1 = "Device title 1";
  347 + String type1 = "typeA";
  348 + List<Device> devicesType1 = new ArrayList<>();
  349 + for (int i=0;i<143;i++) {
  350 + Device device = new Device();
  351 + device.setTenantId(tenantId);
  352 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  353 + String name = title1+suffix;
  354 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  355 + device.setName(name);
  356 + device.setType(type1);
  357 + devicesType1.add(deviceService.saveDevice(device));
  358 + }
  359 + String title2 = "Device title 2";
  360 + String type2 = "typeB";
  361 + List<Device> devicesType2 = new ArrayList<>();
  362 + for (int i=0;i<175;i++) {
  363 + Device device = new Device();
  364 + device.setTenantId(tenantId);
  365 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  366 + String name = title2+suffix;
  367 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  368 + device.setName(name);
  369 + device.setType(type2);
  370 + devicesType2.add(deviceService.saveDevice(device));
  371 + }
  372 +
  373 + List<Device> loadedDevicesType1 = new ArrayList<>();
  374 + TextPageLink pageLink = new TextPageLink(15);
  375 + TextPageData<Device> pageData = null;
  376 + do {
  377 + pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type1, pageLink);
  378 + loadedDevicesType1.addAll(pageData.getData());
  379 + if (pageData.hasNext()) {
  380 + pageLink = pageData.getNextPageLink();
  381 + }
  382 + } while (pageData.hasNext());
  383 +
  384 + Collections.sort(devicesType1, idComparator);
  385 + Collections.sort(loadedDevicesType1, idComparator);
  386 +
  387 + Assert.assertEquals(devicesType1, loadedDevicesType1);
  388 +
  389 + List<Device> loadedDevicesType2 = new ArrayList<>();
  390 + pageLink = new TextPageLink(4);
  391 + do {
  392 + pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type2, pageLink);
  393 + loadedDevicesType2.addAll(pageData.getData());
  394 + if (pageData.hasNext()) {
  395 + pageLink = pageData.getNextPageLink();
  396 + }
  397 + } while (pageData.hasNext());
  398 +
  399 + Collections.sort(devicesType2, idComparator);
  400 + Collections.sort(loadedDevicesType2, idComparator);
  401 +
  402 + Assert.assertEquals(devicesType2, loadedDevicesType2);
  403 +
  404 + for (Device device : loadedDevicesType1) {
  405 + deviceService.deleteDevice(device.getId());
  406 + }
  407 +
  408 + pageLink = new TextPageLink(4);
  409 + pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type1, pageLink);
  410 + Assert.assertFalse(pageData.hasNext());
  411 + Assert.assertEquals(0, pageData.getData().size());
  412 +
  413 + for (Device device : loadedDevicesType2) {
  414 + deviceService.deleteDevice(device.getId());
  415 + }
  416 +
  417 + pageLink = new TextPageLink(4);
  418 + pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type2, pageLink);
  419 + Assert.assertFalse(pageData.hasNext());
  420 + Assert.assertEquals(0, pageData.getData().size());
  421 + }
294 422
295 423 @Test
296 424 public void testFindDevicesByTenantIdAndCustomerId() {
... ... @@ -311,6 +439,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
311 439 Device device = new Device();
312 440 device.setTenantId(tenantId);
313 441 device.setName("Device"+i);
  442 + device.setType("default");
314 443 device = deviceService.saveDevice(device);
315 444 devices.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
316 445 }
... ... @@ -359,6 +488,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
359 488 String name = title1+suffix;
360 489 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
361 490 device.setName(name);
  491 + device.setType("default");
362 492 device = deviceService.saveDevice(device);
363 493 devicesTitle1.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
364 494 }
... ... @@ -371,6 +501,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
371 501 String name = title2+suffix;
372 502 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
373 503 device.setName(name);
  504 + device.setType("default");
374 505 device = deviceService.saveDevice(device);
375 506 devicesTitle2.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
376 507 }
... ... @@ -425,4 +556,94 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
425 556 Assert.assertEquals(0, pageData.getData().size());
426 557 customerService.deleteCustomer(customerId);
427 558 }
  559 +
  560 + @Test
  561 + public void testFindDevicesByTenantIdCustomerIdAndType() {
  562 +
  563 + Customer customer = new Customer();
  564 + customer.setTitle("Test customer");
  565 + customer.setTenantId(tenantId);
  566 + customer = customerService.saveCustomer(customer);
  567 + CustomerId customerId = customer.getId();
  568 +
  569 + String title1 = "Device title 1";
  570 + String type1 = "typeC";
  571 + List<Device> devicesType1 = new ArrayList<>();
  572 + for (int i=0;i<175;i++) {
  573 + Device device = new Device();
  574 + device.setTenantId(tenantId);
  575 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  576 + String name = title1+suffix;
  577 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  578 + device.setName(name);
  579 + device.setType(type1);
  580 + device = deviceService.saveDevice(device);
  581 + devicesType1.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
  582 + }
  583 + String title2 = "Device title 2";
  584 + String type2 = "typeD";
  585 + List<Device> devicesType2 = new ArrayList<>();
  586 + for (int i=0;i<143;i++) {
  587 + Device device = new Device();
  588 + device.setTenantId(tenantId);
  589 + String suffix = RandomStringUtils.randomAlphanumeric(15);
  590 + String name = title2+suffix;
  591 + name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
  592 + device.setName(name);
  593 + device.setType(type2);
  594 + device = deviceService.saveDevice(device);
  595 + devicesType2.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
  596 + }
  597 +
  598 + List<Device> loadedDevicesType1 = new ArrayList<>();
  599 + TextPageLink pageLink = new TextPageLink(15);
  600 + TextPageData<Device> pageData = null;
  601 + do {
  602 + pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
  603 + loadedDevicesType1.addAll(pageData.getData());
  604 + if (pageData.hasNext()) {
  605 + pageLink = pageData.getNextPageLink();
  606 + }
  607 + } while (pageData.hasNext());
  608 +
  609 + Collections.sort(devicesType1, idComparator);
  610 + Collections.sort(loadedDevicesType1, idComparator);
  611 +
  612 + Assert.assertEquals(devicesType1, loadedDevicesType1);
  613 +
  614 + List<Device> loadedDevicesType2 = new ArrayList<>();
  615 + pageLink = new TextPageLink(4);
  616 + do {
  617 + pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
  618 + loadedDevicesType2.addAll(pageData.getData());
  619 + if (pageData.hasNext()) {
  620 + pageLink = pageData.getNextPageLink();
  621 + }
  622 + } while (pageData.hasNext());
  623 +
  624 + Collections.sort(devicesType2, idComparator);
  625 + Collections.sort(loadedDevicesType2, idComparator);
  626 +
  627 + Assert.assertEquals(devicesType2, loadedDevicesType2);
  628 +
  629 + for (Device device : loadedDevicesType1) {
  630 + deviceService.deleteDevice(device.getId());
  631 + }
  632 +
  633 + pageLink = new TextPageLink(4);
  634 + pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
  635 + Assert.assertFalse(pageData.hasNext());
  636 + Assert.assertEquals(0, pageData.getData().size());
  637 +
  638 + for (Device device : loadedDevicesType2) {
  639 + deviceService.deleteDevice(device.getId());
  640 + }
  641 +
  642 + pageLink = new TextPageLink(4);
  643 + pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
  644 + Assert.assertFalse(pageData.hasNext());
  645 + Assert.assertEquals(0, pageData.getData().size());
  646 + customerService.deleteCustomer(customerId);
  647 + }
  648 +
428 649 }
... ...
... ... @@ -31,7 +31,8 @@ function AssetService($http, $q, customerService, userService) {
31 31 getTenantAssets: getTenantAssets,
32 32 getCustomerAssets: getCustomerAssets,
33 33 findByQuery: findByQuery,
34   - fetchAssetsByNameFilter: fetchAssetsByNameFilter
  34 + fetchAssetsByNameFilter: fetchAssetsByNameFilter,
  35 + getAssetTypes: getAssetTypes
35 36 }
36 37
37 38 return service;
... ... @@ -152,7 +153,7 @@ function AssetService($http, $q, customerService, userService) {
152 153 return deferred.promise;
153 154 }
154 155
155   - function getTenantAssets(pageLink, applyCustomersInfo, config) {
  156 + function getTenantAssets(pageLink, applyCustomersInfo, config, type) {
156 157 var deferred = $q.defer();
157 158 var url = '/api/tenant/assets?limit=' + pageLink.limit;
158 159 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -164,6 +165,9 @@ function AssetService($http, $q, customerService, userService) {
164 165 if (angular.isDefined(pageLink.textOffset)) {
165 166 url += '&textOffset=' + pageLink.textOffset;
166 167 }
  168 + if (angular.isDefined(type) && type.length) {
  169 + url += '&type=' + type;
  170 + }
167 171 $http.get(url, config).then(function success(response) {
168 172 if (applyCustomersInfo) {
169 173 customerService.applyAssignedCustomersInfo(response.data.data).then(
... ... @@ -184,7 +188,7 @@ function AssetService($http, $q, customerService, userService) {
184 188 return deferred.promise;
185 189 }
186 190
187   - function getCustomerAssets(customerId, pageLink, applyCustomersInfo, config) {
  191 + function getCustomerAssets(customerId, pageLink, applyCustomersInfo, config, type) {
188 192 var deferred = $q.defer();
189 193 var url = '/api/customer/' + customerId + '/assets?limit=' + pageLink.limit;
190 194 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -196,6 +200,9 @@ function AssetService($http, $q, customerService, userService) {
196 200 if (angular.isDefined(pageLink.textOffset)) {
197 201 url += '&textOffset=' + pageLink.textOffset;
198 202 }
  203 + if (angular.isDefined(type) && type.length) {
  204 + url += '&type=' + type;
  205 + }
199 206 $http.get(url, config).then(function success(response) {
200 207 if (applyCustomersInfo) {
201 208 customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
... ... @@ -258,4 +265,15 @@ function AssetService($http, $q, customerService, userService) {
258 265 return deferred.promise;
259 266 }
260 267
  268 + function getAssetTypes() {
  269 + var deferred = $q.defer();
  270 + var url = '/api/asset/types';
  271 + $http.get(url).then(function success(response) {
  272 + deferred.resolve(response.data);
  273 + }, function fail() {
  274 + deferred.reject();
  275 + });
  276 + return deferred.promise;
  277 + }
  278 +
261 279 }
... ...
... ... @@ -41,12 +41,13 @@ function DeviceService($http, $q, attributeService, customerService, types) {
41 41 deleteDeviceAttributes: deleteDeviceAttributes,
42 42 sendOneWayRpcCommand: sendOneWayRpcCommand,
43 43 sendTwoWayRpcCommand: sendTwoWayRpcCommand,
44   - findByQuery: findByQuery
  44 + findByQuery: findByQuery,
  45 + getDeviceTypes: getDeviceTypes
45 46 }
46 47
47 48 return service;
48 49
49   - function getTenantDevices(pageLink, applyCustomersInfo, config) {
  50 + function getTenantDevices(pageLink, applyCustomersInfo, config, type) {
50 51 var deferred = $q.defer();
51 52 var url = '/api/tenant/devices?limit=' + pageLink.limit;
52 53 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -58,6 +59,9 @@ function DeviceService($http, $q, attributeService, customerService, types) {
58 59 if (angular.isDefined(pageLink.textOffset)) {
59 60 url += '&textOffset=' + pageLink.textOffset;
60 61 }
  62 + if (angular.isDefined(type) && type.length) {
  63 + url += '&type=' + type;
  64 + }
61 65 $http.get(url, config).then(function success(response) {
62 66 if (applyCustomersInfo) {
63 67 customerService.applyAssignedCustomersInfo(response.data.data).then(
... ... @@ -78,7 +82,7 @@ function DeviceService($http, $q, attributeService, customerService, types) {
78 82 return deferred.promise;
79 83 }
80 84
81   - function getCustomerDevices(customerId, pageLink, applyCustomersInfo, config) {
  85 + function getCustomerDevices(customerId, pageLink, applyCustomersInfo, config, type) {
82 86 var deferred = $q.defer();
83 87 var url = '/api/customer/' + customerId + '/devices?limit=' + pageLink.limit;
84 88 if (angular.isDefined(pageLink.textSearch)) {
... ... @@ -90,6 +94,9 @@ function DeviceService($http, $q, attributeService, customerService, types) {
90 94 if (angular.isDefined(pageLink.textOffset)) {
91 95 url += '&textOffset=' + pageLink.textOffset;
92 96 }
  97 + if (angular.isDefined(type) && type.length) {
  98 + url += '&type=' + type;
  99 + }
93 100 $http.get(url, config).then(function success(response) {
94 101 if (applyCustomersInfo) {
95 102 customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
... ... @@ -286,4 +293,15 @@ function DeviceService($http, $q, attributeService, customerService, types) {
286 293 return deferred.promise;
287 294 }
288 295
  296 + function getDeviceTypes() {
  297 + var deferred = $q.defer();
  298 + var url = '/api/device/types';
  299 + $http.get(url).then(function success(response) {
  300 + deferred.resolve(response.data);
  301 + }, function fail() {
  302 + deferred.reject();
  303 + });
  304 + return deferred.promise;
  305 + }
  306 +
289 307 }
... ...
... ... @@ -36,7 +36,8 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
36 36 saveRelatedEntity: saveRelatedEntity,
37 37 getRelatedEntity: getRelatedEntity,
38 38 deleteRelatedEntity: deleteRelatedEntity,
39   - moveEntity: moveEntity
  39 + moveEntity: moveEntity,
  40 + copyEntity: copyEntity
40 41 };
41 42
42 43 return service;
... ... @@ -626,6 +627,32 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
626 627 return deferred.promise;
627 628 }
628 629
  630 + function copyEntity(entity, targetParentId, keys) {
  631 + var deferred = $q.defer();
  632 + if (!entity.id && !entity.id.id) {
  633 + deferred.reject();
  634 + } else {
  635 + getRelatedEntity(entity.id, keys).then(
  636 + function success(relatedEntity) {
  637 + delete relatedEntity.id.id;
  638 + relatedEntity.name = entity.name;
  639 + saveRelatedEntity(relatedEntity, targetParentId, keys).then(
  640 + function success(savedEntity) {
  641 + deferred.resolve(savedEntity);
  642 + },
  643 + function fail() {
  644 + deferred.reject();
  645 + }
  646 + );
  647 + },
  648 + function fail() {
  649 + deferred.reject();
  650 + }
  651 + );
  652 + }
  653 + return deferred.promise;
  654 + }
  655 +
629 656 function saveEntityPromise(entity) {
630 657 var entityType = entity.id.entityType;
631 658 if (!entity.id.id) {
... ...
... ... @@ -160,10 +160,6 @@ export default function AppConfig($provide,
160 160 indigoTheme();
161 161 }
162 162
163   - $mdThemingProvider.theme('tb-search-input', 'default')
164   - .primaryPalette('tb-primary')
165   - .backgroundPalette('tb-primary');
166   -
167 163 $mdThemingProvider.setDefaultTheme('default');
168 164 //$mdThemingProvider.alwaysWatchTheme(true);
169 165 }
... ...
... ... @@ -15,5 +15,8 @@
15 15 limitations under the License.
16 16
17 17 -->
18   -<div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'asset.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div>
19   -<div class="tb-small" ng-show="vm.isPublic()">{{'asset.public' | translate}}</div>
  18 +<div flex layout="column" style="margin-top: -10px;">
  19 + <div flex style="text-transform: uppercase; padding-bottom: 10px;">{{vm.item.type}}</div>
  20 + <div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'asset.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div>
  21 + <div class="tb-small" ng-show="vm.isPublic()">{{'asset.public' | translate}}</div>
  22 +</div>
... ...
... ... @@ -56,13 +56,13 @@
56 56 <div translate ng-message="required">asset.name-required</div>
57 57 </div>
58 58 </md-input-container>
59   - <md-input-container class="md-block">
60   - <label translate>asset.type</label>
61   - <input required name="type" ng-model="asset.type">
62   - <div ng-messages="theForm.name.$error">
63   - <div translate ng-message="required">asset.type-required</div>
64   - </div>
65   - </md-input-container>
  59 + <tb-entity-subtype-autocomplete
  60 + ng-disabled="loading || !isEdit"
  61 + tb-required="true"
  62 + the-form="theForm"
  63 + ng-model="asset.type"
  64 + entity-type="types.entityType.asset">
  65 + </tb-entity-subtype-autocomplete>
66 66 <md-input-container class="md-block">
67 67 <label translate>asset.description</label>
68 68 <textarea ng-model="asset.additionalInfo.description" rows="2"></textarea>
... ...
... ... @@ -47,7 +47,8 @@ export function AssetCardController(types) {
47 47
48 48
49 49 /*@ngInject*/
50   -export function AssetController(userService, assetService, customerService, $state, $stateParams, $document, $mdDialog, $q, $translate, types) {
  50 +export function AssetController($rootScope, userService, assetService, customerService, $state, $stateParams,
  51 + $document, $mdDialog, $q, $translate, types) {
51 52
52 53 var customerId = $stateParams.customerId;
53 54
... ... @@ -129,8 +130,8 @@ export function AssetController(userService, assetService, customerService, $sta
129 130 }
130 131
131 132 if (vm.assetsScope === 'tenant') {
132   - fetchAssetsFunction = function (pageLink) {
133   - return assetService.getTenantAssets(pageLink, true);
  133 + fetchAssetsFunction = function (pageLink, assetType) {
  134 + return assetService.getTenantAssets(pageLink, true, null, assetType);
134 135 };
135 136 deleteAssetFunction = function (assetId) {
136 137 return assetService.deleteAsset(assetId);
... ... @@ -229,8 +230,8 @@ export function AssetController(userService, assetService, customerService, $sta
229 230
230 231
231 232 } else if (vm.assetsScope === 'customer' || vm.assetsScope === 'customer_user') {
232   - fetchAssetsFunction = function (pageLink) {
233   - return assetService.getCustomerAssets(customerId, pageLink, true);
  233 + fetchAssetsFunction = function (pageLink, assetType) {
  234 + return assetService.getCustomerAssets(customerId, pageLink, true, null, assetType);
234 235 };
235 236 deleteAssetFunction = function (assetId) {
236 237 return assetService.unassignAssetFromCustomer(assetId);
... ... @@ -333,6 +334,7 @@ export function AssetController(userService, assetService, customerService, $sta
333 334 var deferred = $q.defer();
334 335 assetService.saveAsset(asset).then(
335 336 function success(savedAsset) {
  337 + $rootScope.$broadcast('assetSaved');
336 338 var assets = [ savedAsset ];
337 339 customerService.applyAssignedCustomersInfo(assets).then(
338 340 function success(items) {
... ...
... ... @@ -25,6 +25,7 @@ export default function AssetDirective($compile, $templateCache, toast, $transla
25 25 var template = $templateCache.get(assetFieldsetTemplate);
26 26 element.html(template);
27 27
  28 + scope.types = types;
28 29 scope.isAssignedToCustomer = false;
29 30 scope.isPublic = false;
30 31 scope.assignedCustomer = null;
... ...
... ... @@ -20,7 +20,7 @@ import assetsTemplate from './assets.tpl.html';
20 20 /* eslint-enable import/no-unresolved, import/default */
21 21
22 22 /*@ngInject*/
23   -export default function AssetRoutes($stateProvider) {
  23 +export default function AssetRoutes($stateProvider, types) {
24 24 $stateProvider
25 25 .state('home.assets', {
26 26 url: '/assets',
... ... @@ -37,6 +37,8 @@ export default function AssetRoutes($stateProvider) {
37 37 data: {
38 38 assetsType: 'tenant',
39 39 searchEnabled: true,
  40 + searchByEntitySubtype: true,
  41 + searchEntityType: types.entityType.asset,
40 42 pageTitle: 'asset.assets'
41 43 },
42 44 ncyBreadcrumb: {
... ... @@ -58,6 +60,8 @@ export default function AssetRoutes($stateProvider) {
58 60 data: {
59 61 assetsType: 'customer',
60 62 searchEnabled: true,
  63 + searchByEntitySubtype: true,
  64 + searchEntityType: types.entityType.asset,
61 65 pageTitle: 'customer.assets'
62 66 },
63 67 ncyBreadcrumb: {
... ...
... ... @@ -179,6 +179,7 @@ function DashboardUtils(types, utils, timeService) {
179 179 dashboard.configuration.settings.showEntitiesSelect = true;
180 180 dashboard.configuration.settings.showDashboardTimewindow = true;
181 181 dashboard.configuration.settings.showDashboardExport = true;
  182 + dashboard.configuration.settings.toolbarAlwaysOpen = false;
182 183 } else {
183 184 if (angular.isUndefined(dashboard.configuration.settings.stateControllerId)) {
184 185 dashboard.configuration.settings.stateControllerId = 'default';
... ...
1   -/*
2   - * Copyright © 2016-2017 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -import './datasource-device.scss';
17   -
18   -import 'md-color-picker';
19   -import tinycolor from 'tinycolor2';
20   -import $ from 'jquery';
21   -import thingsboardTypes from '../common/types.constant';
22   -import thingsboardDatakeyConfigDialog from './datakey-config-dialog.controller';
23   -import thingsboardTruncate from './truncate.filter';
24   -
25   -/* eslint-disable import/no-unresolved, import/default */
26   -
27   -import datasourceDeviceTemplate from './datasource-device.tpl.html';
28   -import datakeyConfigDialogTemplate from './datakey-config-dialog.tpl.html';
29   -
30   -/* eslint-enable import/no-unresolved, import/default */
31   -
32   -/* eslint-disable angular/angularelement */
33   -
34   -export default angular.module('thingsboard.directives.datasourceDevice', [thingsboardTruncate, thingsboardTypes, thingsboardDatakeyConfigDialog])
35   - .directive('tbDatasourceDevice', DatasourceDevice)
36   - .name;
37   -
38   -/*@ngInject*/
39   -function DatasourceDevice($compile, $templateCache, $q, $mdDialog, $window, $document, $mdColorPicker, $mdConstant, types) {
40   -
41   - var linker = function (scope, element, attrs, ngModelCtrl) {
42   - var template = $templateCache.get(datasourceDeviceTemplate);
43   - element.html(template);
44   -
45   - scope.ngModelCtrl = ngModelCtrl;
46   - scope.types = types;
47   -
48   - scope.selectedTimeseriesDataKey = null;
49   - scope.timeseriesDataKeySearchText = null;
50   -
51   - scope.selectedAttributeDataKey = null;
52   - scope.attributeDataKeySearchText = null;
53   -
54   - scope.updateValidity = function () {
55   - if (ngModelCtrl.$viewValue) {
56   - var value = ngModelCtrl.$viewValue;
57   - var dataValid = angular.isDefined(value) && value != null;
58   - ngModelCtrl.$setValidity('deviceData', dataValid);
59   - if (dataValid) {
60   - ngModelCtrl.$setValidity('deviceAlias',
61   - angular.isDefined(value.deviceAliasId) &&
62   - value.deviceAliasId != null);
63   - ngModelCtrl.$setValidity('deviceKeys',
64   - angular.isDefined(value.dataKeys) &&
65   - value.dataKeys != null &&
66   - value.dataKeys.length > 0);
67   - }
68   - }
69   - };
70   -
71   - scope.$watch('deviceAlias', function () {
72   - if (ngModelCtrl.$viewValue) {
73   - if (scope.deviceAlias) {
74   - ngModelCtrl.$viewValue.deviceAliasId = scope.deviceAlias.id;
75   - } else {
76   - ngModelCtrl.$viewValue.deviceAliasId = null;
77   - }
78   - scope.updateValidity();
79   - scope.selectedDeviceAliasChange();
80   - }
81   - });
82   -
83   - scope.$watch('timeseriesDataKeys', function () {
84   - if (ngModelCtrl.$viewValue) {
85   - var dataKeys = [];
86   - dataKeys = dataKeys.concat(scope.timeseriesDataKeys);
87   - dataKeys = dataKeys.concat(scope.attributeDataKeys);
88   - ngModelCtrl.$viewValue.dataKeys = dataKeys;
89   - scope.updateValidity();
90   - }
91   - }, true);
92   -
93   - scope.$watch('attributeDataKeys', function () {
94   - if (ngModelCtrl.$viewValue) {
95   - var dataKeys = [];
96   - dataKeys = dataKeys.concat(scope.timeseriesDataKeys);
97   - dataKeys = dataKeys.concat(scope.attributeDataKeys);
98   - ngModelCtrl.$viewValue.dataKeys = dataKeys;
99   - scope.updateValidity();
100   - }
101   - }, true);
102   -
103   - ngModelCtrl.$render = function () {
104   - if (ngModelCtrl.$viewValue) {
105   - var deviceAliasId = ngModelCtrl.$viewValue.deviceAliasId;
106   - if (scope.deviceAliases[deviceAliasId]) {
107   - scope.deviceAlias = {id: deviceAliasId, alias: scope.deviceAliases[deviceAliasId].alias,
108   - deviceId: scope.deviceAliases[deviceAliasId].deviceId};
109   - } else {
110   - scope.deviceAlias = null;
111   - }
112   - var timeseriesDataKeys = [];
113   - var attributeDataKeys = [];
114   - for (var d in ngModelCtrl.$viewValue.dataKeys) {
115   - var dataKey = ngModelCtrl.$viewValue.dataKeys[d];
116   - if (dataKey.type === types.dataKeyType.timeseries) {
117   - timeseriesDataKeys.push(dataKey);
118   - } else if (dataKey.type === types.dataKeyType.attribute) {
119   - attributeDataKeys.push(dataKey);
120   - }
121   - }
122   - scope.timeseriesDataKeys = timeseriesDataKeys;
123   - scope.attributeDataKeys = attributeDataKeys;
124   - }
125   - };
126   -
127   - scope.textIsNotEmpty = function(text) {
128   - return (text && text != null && text.length > 0) ? true : false;
129   - }
130   -
131   - scope.selectedDeviceAliasChange = function () {
132   - if (!scope.timeseriesDataKeySearchText || scope.timeseriesDataKeySearchText === '') {
133   - scope.timeseriesDataKeySearchText = scope.timeseriesDataKeySearchText === '' ? null : '';
134   - }
135   - if (!scope.attributeDataKeySearchText || scope.attributeDataKeySearchText === '') {
136   - scope.attributeDataKeySearchText = scope.attributeDataKeySearchText === '' ? null : '';
137   - }
138   - };
139   -
140   - scope.transformTimeseriesDataKeyChip = function (chip) {
141   - return scope.generateDataKey({chip: chip, type: types.dataKeyType.timeseries});
142   - };
143   -
144   - scope.transformAttributeDataKeyChip = function (chip) {
145   - return scope.generateDataKey({chip: chip, type: types.dataKeyType.attribute});
146   - };
147   -
148   - scope.showColorPicker = function (event, dataKey) {
149   - $mdColorPicker.show({
150   - value: dataKey.color,
151   - defaultValue: '#fff',
152   - random: tinycolor.random(),
153   - clickOutsideToClose: false,
154   - hasBackdrop: false,
155   - skipHide: true,
156   - preserveScope: false,
157   -
158   - mdColorAlphaChannel: true,
159   - mdColorSpectrum: true,
160   - mdColorSliders: true,
161   - mdColorGenericPalette: false,
162   - mdColorMaterialPalette: true,
163   - mdColorHistory: false,
164   - mdColorDefaultTab: 2,
165   -
166   - $event: event
167   -
168   - }).then(function (color) {
169   - dataKey.color = color;
170   - ngModelCtrl.$setDirty();
171   - });
172   - }
173   -
174   - scope.editDataKey = function (event, dataKey, index) {
175   -
176   - $mdDialog.show({
177   - controller: 'DatakeyConfigDialogController',
178   - controllerAs: 'vm',
179   - templateUrl: datakeyConfigDialogTemplate,
180   - locals: {
181   - dataKey: angular.copy(dataKey),
182   - dataKeySettingsSchema: scope.datakeySettingsSchema,
183   - deviceAlias: scope.deviceAlias,
184   - deviceAliases: scope.deviceAliases
185   - },
186   - parent: angular.element($document[0].body),
187   - fullscreen: true,
188   - targetEvent: event,
189   - skipHide: true,
190   - onComplete: function () {
191   - var w = angular.element($window);
192   - w.triggerHandler('resize');
193   - }
194   - }).then(function (dataKey) {
195   - if (dataKey.type === types.dataKeyType.timeseries) {
196   - scope.timeseriesDataKeys[index] = dataKey;
197   - } else if (dataKey.type === types.dataKeyType.attribute) {
198   - scope.attributeDataKeys[index] = dataKey;
199   - }
200   - ngModelCtrl.$setDirty();
201   - }, function () {
202   - });
203   - };
204   -
205   - scope.dataKeysSearch = function (searchText, type) {
206   - if (scope.deviceAlias) {
207   - var deferred = $q.defer();
208   - scope.fetchDeviceKeys({deviceAliasId: scope.deviceAlias.id, query: searchText, type: type})
209   - .then(function (dataKeys) {
210   - deferred.resolve(dataKeys);
211   - }, function (e) {
212   - deferred.reject(e);
213   - });
214   - return deferred.promise;
215   - } else {
216   - return $q.when([]);
217   - }
218   - };
219   -
220   - scope.createKey = function (event, chipsId) {
221   - var chipsChild = $(chipsId, element)[0].firstElementChild;
222   - var el = angular.element(chipsChild);
223   - var chipBuffer = el.scope().$mdChipsCtrl.getChipBuffer();
224   - event.preventDefault();
225   - event.stopPropagation();
226   - el.scope().$mdChipsCtrl.appendChip(chipBuffer.trim());
227   - el.scope().$mdChipsCtrl.resetChipBuffer();
228   - }
229   -
230   - $compile(element.contents())(scope);
231   - }
232   -
233   - return {
234   - restrict: "E",
235   - require: "^ngModel",
236   - scope: {
237   - widgetType: '=',
238   - deviceAliases: '=',
239   - datakeySettingsSchema: '=',
240   - generateDataKey: '&',
241   - fetchDeviceKeys: '&',
242   - onCreateDeviceAlias: '&'
243   - },
244   - link: linker
245   - };
246   -}
247   -
248   -/* eslint-enable angular/angularelement */
1   -/**
2   - * Copyright © 2016-2017 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -@import '../../scss/constants';
17   -
18   -.tb-device-alias-autocomplete, .tb-timeseries-datakey-autocomplete, .tb-attribute-datakey-autocomplete {
19   - .tb-not-found {
20   - display: block;
21   - line-height: 1.5;
22   - height: 48px;
23   - .tb-no-entries {
24   - line-height: 48px;
25   - }
26   - }
27   - li {
28   - height: auto !important;
29   - white-space: normal !important;
30   - }
31   -}
32   -
33   -tb-datasource-device {
34   - @media (min-width: $layout-breakpoint-gt-sm) {
35   - padding-left: 4px;
36   - padding-right: 4px;
37   - }
38   - tb-device-alias-select {
39   - @media (min-width: $layout-breakpoint-gt-sm) {
40   - width: 200px;
41   - max-width: 200px;
42   - }
43   - }
44   -}
\ No newline at end of file
1   -<!--
2   -
3   - Copyright © 2016-2017 The Thingsboard Authors
4   -
5   - Licensed under the Apache License, Version 2.0 (the "License");
6   - you may not use this file except in compliance with the License.
7   - You may obtain a copy of the License at
8   -
9   - http://www.apache.org/licenses/LICENSE-2.0
10   -
11   - Unless required by applicable law or agreed to in writing, software
12   - distributed under the License is distributed on an "AS IS" BASIS,
13   - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   - See the License for the specific language governing permissions and
15   - limitations under the License.
16   -
17   --->
18   -<section flex layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
19   - <tb-device-alias-select
20   - tb-required="true"
21   - device-aliases="deviceAliases"
22   - ng-model="deviceAlias"
23   - on-create-device-alias="onCreateDeviceAlias({event: event, alias: alias})">
24   - </tb-device-alias-select>
25   - <section flex layout='column'>
26   - <section flex layout='column' layout-align="center" style="padding-left: 4px;">
27   - <md-chips flex
28   - id="timeseries_datakey_chips"
29   - ng-required="true"
30   - ng-model="timeseriesDataKeys" md-autocomplete-snap
31   - md-transform-chip="transformTimeseriesDataKeyChip($chip)"
32   - md-require-match="false">
33   - <md-autocomplete
34   - md-no-cache="true"
35   - id="timeseries_datakey"
36   - md-selected-item="selectedTimeseriesDataKey"
37   - md-search-text="timeseriesDataKeySearchText"
38   - md-items="item in dataKeysSearch(timeseriesDataKeySearchText, types.dataKeyType.timeseries)"
39   - md-item-text="item.name"
40   - md-min-length="0"
41   - placeholder="{{'datakey.timeseries' | translate }}"
42   - md-menu-class="tb-timeseries-datakey-autocomplete">
43   - <span md-highlight-text="timeseriesDataKeySearchText" md-highlight-flags="^i">{{item}}</span>
44   - <md-not-found>
45   - <div class="tb-not-found">
46   - <div class="tb-no-entries" ng-if="!textIsNotEmpty(timeseriesDataKeySearchText)">
47   - <span translate>device.no-keys-found</span>
48   - </div>
49   - <div ng-if="textIsNotEmpty(timeseriesDataKeySearchText)">
50   - <span translate translate-values='{ key: "{{timeseriesDataKeySearchText | truncate:true:6:&apos;...&apos;}}" }'>device.no-key-matching</span>
51   - <span>
52   - <a translate ng-click="createKey($event, '#timeseries_datakey_chips')">device.create-new-key</a>
53   - </span>
54   - </div>
55   - </div>
56   - </md-not-found>
57   - </md-autocomplete>
58   - <md-chip-template>
59   - <div layout="row" layout-align="start center" class="tb-attribute-chip">
60   - <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
61   - <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
62   - </div>
63   - <div layout="row" flex>
64   - <div class="tb-chip-label">
65   - {{$chip.label}}
66   - </div>
67   - <div class="tb-chip-separator">: </div>
68   - <div class="tb-chip-label">
69   - <strong ng-if="!$chip.postFuncBody">{{$chip.name}}</strong>
70   - <strong ng-if="$chip.postFuncBody">f({{$chip.name}})</strong>
71   - </div>
72   - </div>
73   - <md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32">
74   - <md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon>
75   - </md-button>
76   - </div>
77   - </md-chip-template>
78   - </md-chips>
79   - <md-chips flex ng-if="widgetType === types.widgetType.latest.value"
80   - id="attribute_datakey_chips"
81   - ng-required="true"
82   - ng-model="attributeDataKeys" md-autocomplete-snap
83   - md-transform-chip="transformAttributeDataKeyChip($chip)"
84   - md-require-match="false">
85   - <md-autocomplete
86   - md-no-cache="true"
87   - id="attribute_datakey"
88   - md-selected-item="selectedAttributeDataKey"
89   - md-search-text="attributeDataKeySearchText"
90   - md-items="item in dataKeysSearch(attributeDataKeySearchText, types.dataKeyType.attribute)"
91   - md-item-text="item.name"
92   - md-min-length="0"
93   - placeholder="{{'datakey.attributes' | translate }}"
94   - md-menu-class="tb-attribute-datakey-autocomplete">
95   - <span md-highlight-text="attributeDataKeySearchText" md-highlight-flags="^i">{{item}}</span>
96   - <md-not-found>
97   - <div class="tb-not-found">
98   - <div class="tb-no-entries" ng-if="!textIsNotEmpty(attributeDataKeySearchText)">
99   - <span translate>device.no-keys-found</span>
100   - </div>
101   - <div ng-if="textIsNotEmpty(attributeDataKeySearchText)">
102   - <span translate translate-values='{ key: "{{attributeDataKeySearchText | truncate:true:6:&apos;...&apos;}}" }'>device.no-key-matching</span>
103   - <span>
104   - <a translate ng-click="createKey($event, '#attribute_datakey_chips')">device.create-new-key</a>
105   - </span>
106   - </div>
107   - </div>
108   - </md-not-found>
109   - </md-autocomplete>
110   - <md-chip-template>
111   - <div layout="row" layout-align="start center" class="tb-attribute-chip">
112   - <div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
113   - <div class="tb-color-result" ng-style="{background: $chip.color}"></div>
114   - </div>
115   - <div layout="row" flex>
116   - <div class="tb-chip-label">
117   - {{$chip.label}}
118   - </div>
119   - <div class="tb-chip-separator">: </div>
120   - <div class="tb-chip-label">
121   - <strong ng-if="!$chip.postFuncBody">{{$chip.name}}</strong>
122   - <strong ng-if="$chip.postFuncBody">f({{$chip.name}})</strong>
123   - </div>
124   - </div>
125   - <md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32">
126   - <md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon>
127   - </md-button>
128   - </div>
129   - </md-chip-template>
130   - </md-chips>
131   - </section>
132   - <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert">
133   - <div translate ng-message="deviceKeys" ng-if="widgetType === types.widgetType.timeseries.value" class="tb-error-message">datakey.timeseries-required</div>
134   - <div translate ng-message="deviceKeys" ng-if="widgetType === types.widgetType.latest.value" class="tb-error-message">datakey.timeseries-or-attributes-required</div>
135   - </div>
136   - </section>
137   -</section>
1   -/*
2   - * Copyright © 2016-2017 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -import $ from 'jquery';
17   -
18   -import './device-alias-select.scss';
19   -
20   -/* eslint-disable import/no-unresolved, import/default */
21   -
22   -import deviceAliasSelectTemplate from './device-alias-select.tpl.html';
23   -
24   -/* eslint-enable import/no-unresolved, import/default */
25   -
26   -
27   -/* eslint-disable angular/angularelement */
28   -
29   -export default angular.module('thingsboard.directives.deviceAliasSelect', [])
30   - .directive('tbDeviceAliasSelect', DeviceAliasSelect)
31   - .name;
32   -
33   -/*@ngInject*/
34   -function DeviceAliasSelect($compile, $templateCache, $mdConstant) {
35   -
36   - var linker = function (scope, element, attrs, ngModelCtrl) {
37   - var template = $templateCache.get(deviceAliasSelectTemplate);
38   - element.html(template);
39   -
40   - scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
41   -
42   - scope.ngModelCtrl = ngModelCtrl;
43   - scope.deviceAliasList = [];
44   - scope.deviceAlias = null;
45   -
46   - scope.updateValidity = function () {
47   - var value = ngModelCtrl.$viewValue;
48   - var valid = angular.isDefined(value) && value != null || !scope.tbRequired;
49   - ngModelCtrl.$setValidity('deviceAlias', valid);
50   - };
51   -
52   - scope.$watch('deviceAliases', function () {
53   - scope.deviceAliasList = [];
54   - for (var aliasId in scope.deviceAliases) {
55   - var deviceAlias = {id: aliasId, alias: scope.deviceAliases[aliasId].alias, deviceId: scope.deviceAliases[aliasId].deviceId};
56   - scope.deviceAliasList.push(deviceAlias);
57   - }
58   - }, true);
59   -
60   - scope.$watch('deviceAlias', function () {
61   - scope.updateView();
62   - });
63   -
64   - scope.deviceAliasSearch = function (deviceAliasSearchText) {
65   - return deviceAliasSearchText ? scope.deviceAliasList.filter(
66   - scope.createFilterForDeviceAlias(deviceAliasSearchText)) : scope.deviceAliasList;
67   - };
68   -
69   - scope.createFilterForDeviceAlias = function (query) {
70   - var lowercaseQuery = angular.lowercase(query);
71   - return function filterFn(deviceAlias) {
72   - return (angular.lowercase(deviceAlias.alias).indexOf(lowercaseQuery) === 0);
73   - };
74   - };
75   -
76   - scope.updateView = function () {
77   - ngModelCtrl.$setViewValue(scope.deviceAlias);
78   - scope.updateValidity();
79   - }
80   -
81   - ngModelCtrl.$render = function () {
82   - if (ngModelCtrl.$viewValue) {
83   - scope.deviceAlias = ngModelCtrl.$viewValue;
84   - }
85   - }
86   -
87   - scope.textIsNotEmpty = function(text) {
88   - return (text && text != null && text.length > 0) ? true : false;
89   - }
90   -
91   - scope.deviceAliasEnter = function($event) {
92   - if ($event.keyCode === $mdConstant.KEY_CODE.ENTER) {
93   - $event.preventDefault();
94   - if (!scope.deviceAlias) {
95   - var found = scope.deviceAliasSearch(scope.deviceAliasSearchText);
96   - found = found.length > 0;
97   - if (!found) {
98   - scope.createDeviceAlias($event, scope.deviceAliasSearchText);
99   - }
100   - }
101   - }
102   - }
103   -
104   - scope.createDeviceAlias = function (event, alias) {
105   - var autoChild = $('#device-autocomplete', element)[0].firstElementChild;
106   - var el = angular.element(autoChild);
107   - el.scope().$mdAutocompleteCtrl.hidden = true;
108   - el.scope().$mdAutocompleteCtrl.hasNotFound = false;
109   - event.preventDefault();
110   - var promise = scope.onCreateDeviceAlias({event: event, alias: alias});
111   - if (promise) {
112   - promise.then(
113   - function success(newAlias) {
114   - el.scope().$mdAutocompleteCtrl.hasNotFound = true;
115   - if (newAlias) {
116   - scope.deviceAliasList.push(newAlias);
117   - scope.deviceAlias = newAlias;
118   - }
119   - },
120   - function fail() {
121   - el.scope().$mdAutocompleteCtrl.hasNotFound = true;
122   - }
123   - );
124   - } else {
125   - el.scope().$mdAutocompleteCtrl.hasNotFound = true;
126   - }
127   - };
128   -
129   - $compile(element.contents())(scope);
130   - }
131   -
132   - return {
133   - restrict: "E",
134   - require: "^ngModel",
135   - link: linker,
136   - scope: {
137   - tbRequired: '=?',
138   - deviceAliases: '=',
139   - onCreateDeviceAlias: '&'
140   - }
141   - };
142   -}
143   -
144   -/* eslint-enable angular/angularelement */
1   -<!--
2   -
3   - Copyright © 2016-2017 The Thingsboard Authors
4   -
5   - Licensed under the Apache License, Version 2.0 (the "License");
6   - you may not use this file except in compliance with the License.
7   - You may obtain a copy of the License at
8   -
9   - http://www.apache.org/licenses/LICENSE-2.0
10   -
11   - Unless required by applicable law or agreed to in writing, software
12   - distributed under the License is distributed on an "AS IS" BASIS,
13   - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   - See the License for the specific language governing permissions and
15   - limitations under the License.
16   -
17   --->
18   -<section layout='column'>
19   - <md-autocomplete id="device-autocomplete"
20   - md-input-name="device_alias"
21   - ng-required="tbRequired"
22   - ng-model="deviceAlias"
23   - md-selected-item="deviceAlias"
24   - md-search-text="deviceAliasSearchText"
25   - md-items="item in deviceAliasSearch(deviceAliasSearchText)"
26   - md-item-text="item.alias"
27   - tb-keydown="deviceAliasEnter($event)"
28   - tb-keypress="deviceAliasEnter($event)"
29   - md-min-length="0"
30   - placeholder="{{ 'device.device-alias' | translate }}"
31   - md-menu-class="tb-device-alias-autocomplete">
32   - <md-item-template>
33   - <span md-highlight-text="deviceAliasSearchText" md-highlight-flags="^i">{{item.alias}}</span>
34   - </md-item-template>
35   - <md-not-found>
36   - <div class="tb-not-found">
37   - <div class="tb-no-entries" ng-if="!textIsNotEmpty(deviceAliasSearchText)">
38   - <span translate>device.no-aliases-found</span>
39   - </div>
40   - <div ng-if="textIsNotEmpty(deviceAliasSearchText)">
41   - <span translate translate-values='{ alias: "{{deviceAliasSearchText | truncate:true:6:&apos;...&apos;}}" }'>device.no-alias-matching</span>
42   - <span>
43   - <a translate ng-click="createDeviceAlias($event, deviceAliasSearchText)">device.create-new-alias</a>
44   - </span>
45   - </div>
46   - </div>
47   - </md-not-found>
48   - </md-autocomplete>
49   - <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert">
50   - <div translate ng-message="deviceAlias" class="tb-error-message">device.alias-required</div>
51   - </div>
52   -</section>
1   -/*
2   - * Copyright © 2016-2017 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -
17   -/* eslint-disable import/no-unresolved, import/default */
18   -
19   -import deviceFilterTemplate from './device-filter.tpl.html';
20   -
21   -/* eslint-enable import/no-unresolved, import/default */
22   -
23   -import './device-filter.scss';
24   -
25   -export default angular.module('thingsboard.directives.deviceFilter', [])
26   - .directive('tbDeviceFilter', DeviceFilter)
27   - .name;
28   -
29   -/*@ngInject*/
30   -function DeviceFilter($compile, $templateCache, $q, deviceService) {
31   -
32   - var linker = function (scope, element, attrs, ngModelCtrl) {
33   -
34   - var template = $templateCache.get(deviceFilterTemplate);
35   - element.html(template);
36   -
37   - scope.ngModelCtrl = ngModelCtrl;
38   -
39   - scope.fetchDevices = function(searchText, limit) {
40   - var pageLink = {limit: limit, textSearch: searchText};
41   -
42   - var deferred = $q.defer();
43   -
44   - deviceService.getTenantDevices(pageLink, false).then(function success(result) {
45   - deferred.resolve(result.data);
46   - }, function fail() {
47   - deferred.reject();
48   - });
49   -
50   - return deferred.promise;
51   - }
52   -
53   - scope.updateValidity = function() {
54   - if (ngModelCtrl.$viewValue) {
55   - var value = ngModelCtrl.$viewValue;
56   - var valid;
57   - if (value.useFilter) {
58   - ngModelCtrl.$setValidity('deviceList', true);
59   - if (angular.isDefined(value.deviceNameFilter) && value.deviceNameFilter.length > 0) {
60   - ngModelCtrl.$setValidity('deviceNameFilter', true);
61   - valid = angular.isDefined(scope.model.matchingFilterDevice) && scope.model.matchingFilterDevice != null;
62   - ngModelCtrl.$setValidity('deviceNameFilterDeviceMatch', valid);
63   - } else {
64   - ngModelCtrl.$setValidity('deviceNameFilter', false);
65   - }
66   - } else {
67   - ngModelCtrl.$setValidity('deviceNameFilter', true);
68   - ngModelCtrl.$setValidity('deviceNameFilterDeviceMatch', true);
69   - valid = angular.isDefined(value.deviceList) && value.deviceList.length > 0;
70   - ngModelCtrl.$setValidity('deviceList', valid);
71   - }
72   - }
73   - }
74   -
75   - ngModelCtrl.$render = function () {
76   - destroyWatchers();
77   - scope.model = {
78   - useFilter: false,
79   - deviceList: [],
80   - deviceNameFilter: ''
81   - }
82   - if (ngModelCtrl.$viewValue) {
83   - var value = ngModelCtrl.$viewValue;
84   - var model = scope.model;
85   - model.useFilter = value.useFilter === true ? true: false;
86   - model.deviceList = [];
87   - model.deviceNameFilter = value.deviceNameFilter || '';
88   - processDeviceNameFilter(model.deviceNameFilter).then(
89   - function(device) {
90   - scope.model.matchingFilterDevice = device;
91   - if (value.deviceList && value.deviceList.length > 0) {
92   - deviceService.getDevices(value.deviceList).then(function (devices) {
93   - model.deviceList = devices;
94   - updateMatchingDevice();
95   - initWatchers();
96   - });
97   - } else {
98   - updateMatchingDevice();
99   - initWatchers();
100   - }
101   - }
102   - )
103   - }
104   - }
105   -
106   - function updateMatchingDevice() {
107   - if (scope.model.useFilter) {
108   - scope.model.matchingDevice = scope.model.matchingFilterDevice;
109   - } else {
110   - if (scope.model.deviceList && scope.model.deviceList.length > 0) {
111   - scope.model.matchingDevice = scope.model.deviceList[0];
112   - } else {
113   - scope.model.matchingDevice = null;
114   - }
115   - }
116   - }
117   -
118   - function processDeviceNameFilter(deviceNameFilter) {
119   - var deferred = $q.defer();
120   - if (angular.isDefined(deviceNameFilter) && deviceNameFilter.length > 0) {
121   - scope.fetchDevices(deviceNameFilter, 1).then(function (devices) {
122   - if (devices && devices.length > 0) {
123   - deferred.resolve(devices[0]);
124   - } else {
125   - deferred.resolve(null);
126   - }
127   - });
128   - } else {
129   - deferred.resolve(null);
130   - }
131   - return deferred.promise;
132   - }
133   -
134   - function destroyWatchers() {
135   - if (scope.deviceListDeregistration) {
136   - scope.deviceListDeregistration();
137   - scope.deviceListDeregistration = null;
138   - }
139   - if (scope.useFilterDeregistration) {
140   - scope.useFilterDeregistration();
141   - scope.useFilterDeregistration = null;
142   - }
143   - if (scope.deviceNameFilterDeregistration) {
144   - scope.deviceNameFilterDeregistration();
145   - scope.deviceNameFilterDeregistration = null;
146   - }
147   - if (scope.matchingDeviceDeregistration) {
148   - scope.matchingDeviceDeregistration();
149   - scope.matchingDeviceDeregistration = null;
150   - }
151   - }
152   -
153   - function initWatchers() {
154   - scope.deviceListDeregistration = scope.$watch('model.deviceList', function () {
155   - if (ngModelCtrl.$viewValue) {
156   - var value = ngModelCtrl.$viewValue;
157   - value.deviceList = [];
158   - if (scope.model.deviceList && scope.model.deviceList.length > 0) {
159   - for (var i in scope.model.deviceList) {
160   - value.deviceList.push(scope.model.deviceList[i].id.id);
161   - }
162   - }
163   - updateMatchingDevice();
164   - ngModelCtrl.$setViewValue(value);
165   - scope.updateValidity();
166   - }
167   - }, true);
168   - scope.useFilterDeregistration = scope.$watch('model.useFilter', function () {
169   - if (ngModelCtrl.$viewValue) {
170   - var value = ngModelCtrl.$viewValue;
171   - value.useFilter = scope.model.useFilter;
172   - updateMatchingDevice();
173   - ngModelCtrl.$setViewValue(value);
174   - scope.updateValidity();
175   - }
176   - });
177   - scope.deviceNameFilterDeregistration = scope.$watch('model.deviceNameFilter', function (newNameFilter, prevNameFilter) {
178   - if (ngModelCtrl.$viewValue) {
179   - if (!angular.equals(newNameFilter, prevNameFilter)) {
180   - var value = ngModelCtrl.$viewValue;
181   - value.deviceNameFilter = scope.model.deviceNameFilter;
182   - processDeviceNameFilter(value.deviceNameFilter).then(
183   - function(device) {
184   - scope.model.matchingFilterDevice = device;
185   - updateMatchingDevice();
186   - ngModelCtrl.$setViewValue(value);
187   - scope.updateValidity();
188   - }
189   - );
190   - }
191   - }
192   - });
193   -
194   - scope.matchingDeviceDeregistration = scope.$watch('model.matchingDevice', function (newMatchingDevice, prevMatchingDevice) {
195   - if (!angular.equals(newMatchingDevice, prevMatchingDevice)) {
196   - if (scope.onMatchingDeviceChange) {
197   - scope.onMatchingDeviceChange({device: newMatchingDevice});
198   - }
199   - }
200   - });
201   - }
202   -
203   - $compile(element.contents())(scope);
204   -
205   - }
206   -
207   - return {
208   - restrict: "E",
209   - require: "^ngModel",
210   - link: linker,
211   - scope: {
212   - isEdit: '=',
213   - onMatchingDeviceChange: '&'
214   - }
215   - };
216   -
217   -}
1   -/**
2   - * Copyright © 2016-2017 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -.tb-device-filter {
17   - #device_list_chips {
18   - .md-chips {
19   - padding-bottom: 1px;
20   - }
21   - }
22   - .device-name-filter-input {
23   - margin-top: 10px;
24   - margin-bottom: 0px;
25   - .md-errors-spacer {
26   - min-height: 0px;
27   - }
28   - }
29   - .tb-filter-switch {
30   - padding-left: 10px;
31   - .filter-switch {
32   - margin: 0;
33   - }
34   - .filter-label {
35   - margin: 5px 0;
36   - }
37   - }
38   - .tb-error-messages {
39   - margin-top: -11px;
40   - height: 35px;
41   - .tb-error-message {
42   - padding-left: 1px;
43   - }
44   - }
45   -}
\ No newline at end of file
1   -<!--
2   -
3   - Copyright © 2016-2017 The Thingsboard Authors
4   -
5   - Licensed under the Apache License, Version 2.0 (the "License");
6   - you may not use this file except in compliance with the License.
7   - You may obtain a copy of the License at
8   -
9   - http://www.apache.org/licenses/LICENSE-2.0
10   -
11   - Unless required by applicable law or agreed to in writing, software
12   - distributed under the License is distributed on an "AS IS" BASIS,
13   - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   - See the License for the specific language governing permissions and
15   - limitations under the License.
16   -
17   --->
18   -<section layout='column' class="tb-device-filter">
19   - <section layout='row'>
20   - <section layout="column" flex ng-show="!model.useFilter">
21   - <md-chips flex
22   - id="device_list_chips"
23   - ng-required="!useFilter"
24   - ng-model="model.deviceList" md-autocomplete-snap
25   - md-require-match="true">
26   - <md-autocomplete
27   - md-no-cache="true"
28   - id="device"
29   - md-selected-item="selectedDevice"
30   - md-search-text="deviceSearchText"
31   - md-items="item in fetchDevices(deviceSearchText, 10)"
32   - md-item-text="item.name"
33   - md-min-length="0"
34   - placeholder="{{ 'device.device-list' | translate }}">
35   - <md-item-template>
36   - <span md-highlight-text="deviceSearchText" md-highlight-flags="^i">{{item.name}}</span>
37   - </md-item-template>
38   - <md-not-found>
39   - <span translate translate-values='{ device: deviceSearchText }'>device.no-devices-matching</span>
40   - </md-not-found>
41   - </md-autocomplete>
42   - <md-chip-template>
43   - <span>
44   - <strong>{{$chip.name}}</strong>
45   - </span>
46   - </md-chip-template>
47   - </md-chips>
48   - </section>
49   - <section layout="row" flex ng-show="model.useFilter">
50   - <md-input-container flex class="device-name-filter-input">
51   - <label translate>device.name-starts-with</label>
52   - <input ng-model="model.deviceNameFilter" aria-label="{{ 'device.name-starts-with' | translate }}">
53   - </md-input-container>
54   - </section>
55   - <section class="tb-filter-switch" layout="column" layout-align="center center">
56   - <label class="tb-small filter-label" translate>device.use-device-name-filter</label>
57   - <md-switch class="filter-switch" ng-model="model.useFilter" aria-label="use-filter-switcher">
58   - </md-switch>
59   - </section>
60   - </section>
61   - <div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert">
62   - <div translate ng-message="deviceList" class="tb-error-message">device.device-list-empty</div>
63   - <div translate ng-message="deviceNameFilter" class="tb-error-message">device.device-name-filter-required</div>
64   - <div translate translate-values='{ device: model.deviceNameFilter }' ng-message="deviceNameFilterDeviceMatch"
65   - class="tb-error-message">device.device-name-filter-no-device-matched</div>
66   - </div>
67   -</section>
\ No newline at end of file