Browse Source

Merge remote-tracking branch 'upstream/master' into feature/mqtt-plugin

pull/137/head^2
volodymyr-babak 9 years ago
parent
commit
bdc048c548
  1. 29
      application/src/main/java/org/thingsboard/server/controller/AssetController.java
  2. 30
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  3. 16
      application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
  4. 36
      application/src/main/resources/thingsboard.yml
  5. 8
      application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
  6. 659
      application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java
  7. 244
      application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java
  8. 2
      application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java
  9. 2
      application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java
  10. 1
      application/src/test/java/org/thingsboard/server/system/HttpDeviceApiTest.java
  11. 7
      common/data/src/main/java/org/thingsboard/server/common/data/Customer.java
  12. 7
      common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java
  13. 3
      common/data/src/main/java/org/thingsboard/server/common/data/Device.java
  14. 13
      common/data/src/main/java/org/thingsboard/server/common/data/HasName.java
  15. 7
      common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java
  16. 79
      common/data/src/main/java/org/thingsboard/server/common/data/TenantDeviceType.java
  17. 7
      common/data/src/main/java/org/thingsboard/server/common/data/User.java
  18. 7
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
  19. 4
      common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java
  20. 79
      common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java
  21. 4
      common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java
  22. 10
      common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java
  23. 59
      common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java
  24. 8
      common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java
  25. 4
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  26. 66
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  27. 32
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
  28. 50
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java
  29. 8
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
  30. 59
      dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
  31. 1
      dao/src/main/java/org/thingsboard/server/dao/cache/ServiceCacheConfiguration.java
  32. 6
      dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java
  33. 5
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java
  34. 24
      dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java
  35. 30
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  36. 53
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java
  37. 7
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  38. 59
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  39. 44
      dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java
  40. 93
      dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java
  41. 43
      dao/src/main/java/org/thingsboard/server/dao/entity/EntityService.java
  42. 7
      dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java
  43. 7
      dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java
  44. 6
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  45. 107
      dao/src/main/java/org/thingsboard/server/dao/model/TenantAssetTypeEntity.java
  46. 107
      dao/src/main/java/org/thingsboard/server/dao/model/TenantDeviceTypeEntity.java
  47. 6
      dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java
  48. 43
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
  49. 3
      dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java
  50. 6
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java
  51. 6
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  52. 3
      dao/src/main/java/org/thingsboard/server/dao/user/UserService.java
  53. 15
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java
  54. 21
      dao/src/main/resources/demo-data.cql
  55. 145
      dao/src/main/resources/schema.cql
  56. 4
      dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java
  57. 634
      dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java
  58. 9
      dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceImplTest.java
  59. 223
      dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceImplTest.java
  60. 6
      docker/.env
  61. 132
      docker/cassandra/cassandra.yaml
  62. 32
      docker/common/common.yaml
  63. 2
      docker/docker-compose.static.yml
  64. 28
      docker/docker-compose.yml
  65. 4
      docker/tb-cassandra-schema/Dockerfile
  66. 13
      docker/tb-cassandra-schema/Makefile
  67. 16
      docker/tb-cassandra-schema/install-schema.sh
  68. 33
      docker/tb-cassandra-schema/tb-cassandra-schema.yaml
  69. 3
      docker/tb.env
  70. 4
      docker/tb/Dockerfile
  71. 11
      docker/tb/Makefile
  72. 6
      docker/tb/run-application.sh
  73. 121
      docker/tb/tb.yaml
  74. 5
      docker/thingsboard-db-schema.env
  75. 28
      docker/thingsboard-db-schema/build_and_deploy.sh
  76. 26
      docker/thingsboard/build_and_deploy.sh
  77. 71
      docker/zookeeper/Dockerfile
  78. 9
      docker/zookeeper/Makefile
  79. 153
      docker/zookeeper/zk-gen-config.sh
  80. 25
      docker/zookeeper/zk-ok.sh
  81. 190
      docker/zookeeper/zookeeper.yaml
  82. 2
      transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java
  83. 24
      ui/src/app/api/asset.service.js
  84. 24
      ui/src/app/api/device.service.js
  85. 13
      ui/src/app/api/entity-relation.service.js
  86. 142
      ui/src/app/api/entity.service.js
  87. 4
      ui/src/app/app.config.js
  88. 7
      ui/src/app/asset/asset-card.tpl.html
  89. 14
      ui/src/app/asset/asset-fieldset.tpl.html
  90. 12
      ui/src/app/asset/asset.controller.js
  91. 1
      ui/src/app/asset/asset.directive.js
  92. 6
      ui/src/app/asset/asset.routes.js
  93. 6
      ui/src/app/asset/assets.tpl.html
  94. 1
      ui/src/app/common/dashboard-utils.service.js
  95. 5
      ui/src/app/common/types.constant.js
  96. 26
      ui/src/app/common/utils.service.js
  97. 2
      ui/src/app/components/dashboard-autocomplete.tpl.html
  98. 248
      ui/src/app/components/datasource-device.directive.js
  99. 137
      ui/src/app/components/datasource-device.tpl.html
  100. 144
      ui/src/app/components/device-alias-select.directive.js

29
application/src/main/java/org/thingsboard/server/controller/AssetController.java

@ -21,6 +21,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.TenantAssetType;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
@ -136,13 +137,18 @@ public class AssetController extends BaseController {
@ResponseBody
public TextPageData<Asset> getTenantAssets(
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
if (type != null && type.trim().length()>0) {
return checkNotNull(assetService.findAssetsByTenantIdAndType(tenantId, type, pageLink));
} else {
return checkNotNull(assetService.findAssetsByTenantId(tenantId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
@ -167,6 +173,7 @@ public class AssetController extends BaseController {
public TextPageData<Asset> getCustomerAssets(
@PathVariable("customerId") String strCustomerId,
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
@ -176,7 +183,11 @@ public class AssetController extends BaseController {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
if (type != null && type.trim().length()>0) {
return checkNotNull(assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
} else {
return checkNotNull(assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
@ -231,4 +242,18 @@ public class AssetController extends BaseController {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/asset/types", method = RequestMethod.GET)
@ResponseBody
public List<TenantAssetType> getAssetTypes() throws ThingsboardException {
try {
SecurityUser user = getCurrentUser();
TenantId tenantId = user.getTenantId();
ListenableFuture<List<TenantAssetType>> assetTypes = assetService.findAssetTypesByTenantId(tenantId);
return checkNotNull(assetTypes.get());
} catch (Exception e) {
throw handleException(e);
}
}
}

30
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -21,6 +21,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.TenantDeviceType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
@ -166,13 +167,18 @@ public class DeviceController extends BaseController {
@ResponseBody
public TextPageData<Device> getTenantDevices(
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
try {
TenantId tenantId = getCurrentUser().getTenantId();
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink));
if (type != null && type.trim().length()>0) {
return checkNotNull(deviceService.findDevicesByTenantIdAndType(tenantId, type, pageLink));
} else {
return checkNotNull(deviceService.findDevicesByTenantId(tenantId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
@ -197,6 +203,7 @@ public class DeviceController extends BaseController {
public TextPageData<Device> getCustomerDevices(
@PathVariable("customerId") String strCustomerId,
@RequestParam int limit,
@RequestParam(required = false) String type,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String idOffset,
@RequestParam(required = false) String textOffset) throws ThingsboardException {
@ -206,7 +213,11 @@ public class DeviceController extends BaseController {
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
checkCustomerId(customerId);
TextPageLink pageLink = createPageLink(limit, textSearch, idOffset, textOffset);
return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink));
if (type != null && type.trim().length()>0) {
return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink));
} else {
return checkNotNull(deviceService.findDevicesByTenantIdAndCustomerId(tenantId, customerId, pageLink));
}
} catch (Exception e) {
throw handleException(e);
}
@ -261,4 +272,19 @@ public class DeviceController extends BaseController {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/types", method = RequestMethod.GET)
@ResponseBody
public List<TenantDeviceType> getDeviceTypes() throws ThingsboardException {
try {
SecurityUser user = getCurrentUser();
TenantId tenantId = user.getTenantId();
ListenableFuture<List<TenantDeviceType>> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId);
return checkNotNull(deviceTypes.get());
} catch (Exception e) {
throw handleException(e);
}
}
}

16
application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java

@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.dao.relation.EntityRelationsQuery;
import org.thingsboard.server.exception.ThingsboardErrorCode;
import org.thingsboard.server.exception.ThingsboardException;
@ -127,6 +128,21 @@ public class EntityRelationController extends BaseController {
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {"fromId", "fromType"})
@ResponseBody
public List<EntityRelationInfo> findInfoByFrom(@RequestParam("fromId") String strFromId, @RequestParam("fromType") String strFromType) throws ThingsboardException {
checkParameter("fromId", strFromId);
checkParameter("fromType", strFromType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strFromType, strFromId);
checkEntityId(entityId);
try {
return checkNotNull(relationService.findInfoByFrom(entityId).get());
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.GET, params = {"fromId", "fromType", "relationType"})
@ResponseBody

36
application/src/main/resources/thingsboard.yml

@ -19,12 +19,18 @@ server:
address: "${HTTP_BIND_ADDRESS:0.0.0.0}"
# Server bind port
port: "${HTTP_BIND_PORT:8080}"
# Uncomment the following section to enable ssl
# ssl:
# key-store: classpath:keystore/keystore.p12
# key-store-password: thingsboard
# keyStoreType: PKCS12
# keyAlias: tomcat
# Server SSL configuration
ssl:
# Enable/disable SSL support
enabled: "${SSL_ENABLED:false}"
# Path to the key store that holds the SSL certificate
key-store: "${SSL_KEY_STORE:classpath:keystore/keystore.p12}"
# Password used to access the key store
key-store-password: "${SSL_KEY_STORE_PASSWORD:thingsboard}"
# Type of the key store
key-store-type: "${SSL_KEY_STORE_TYPE:PKCS12}"
# Alias that identifies the key in the key store
key-alias: "${SSL_KEY_ALIAS:tomcat}"
# Zookeeper connection parameters. Used for service discovery.
zk:
@ -79,12 +85,18 @@ mqtt:
leak_detector_level: "${NETTY_LEASK_DETECTOR_LVL:DISABLED}"
boss_group_thread_count: "${NETTY_BOSS_GROUP_THREADS:1}"
worker_group_thread_count: "${NETTY_WORKER_GROUP_THREADS:12}"
# Uncomment the following lines to enable ssl for MQTT
# ssl:
# key_store: mqttserver.jks
# key_store_password: server_ks_password
# key_password: server_key_password
# key_store_type: JKS
# MQTT SSL configuration
ssl:
# Enable/disable SSL support
enabled: "${MQTT_SSL_ENABLED:false}"
# Path to the key store that holds the SSL certificate
key_store: "${MQTT_SSL_KEY_STORE:mqttserver.jks}"
# Password used to access the key store
key_store_password: "${MQTT_SSL_KEY_STORE_PASSWORD:server_ks_password}"
# Password used to access the key
key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
# Type of the key store
key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
# CoAP server parameters
coap:

8
application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java

@ -98,13 +98,15 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppC
@IntegrationTest("server.port:0")
public abstract class AbstractControllerTest {
protected static final String TEST_TENANT_NAME = "TEST TENANT";
protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org";
private static final String SYS_ADMIN_PASSWORD = "sysadmin";
protected static final String TENANT_ADMIN_EMAIL = "tenant@thingsboard.org";
protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org";
private static final String TENANT_ADMIN_PASSWORD = "tenant";
protected static final String CUSTOMER_USER_EMAIL = "customer@thingsboard.org";
protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
private static final String CUSTOMER_USER_PASSWORD = "customer";
protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
@ -147,7 +149,7 @@ public abstract class AbstractControllerTest {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("Tenant");
tenant.setTitle(TEST_TENANT_NAME);
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
tenantId = savedTenant.getId();

659
application/src/test/java/org/thingsboard/server/controller/AssetControllerTest.java

@ -0,0 +1,659 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller;
import static org.hamcrest.Matchers.containsString;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.RandomStringUtils;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.TenantAssetType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.model.ModelConstants;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.datastax.driver.core.utils.UUIDs;
import com.fasterxml.jackson.core.type.TypeReference;
public class AssetControllerTest extends AbstractControllerTest {
private IdComparator<Asset> idComparator = new IdComparator<>();
private Tenant savedTenant;
private User tenantAdmin;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
}
@After
public void afterTest() throws Exception {
loginSysAdmin();
doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testSaveAsset() throws Exception {
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
Assert.assertNotNull(savedAsset);
Assert.assertNotNull(savedAsset.getId());
Assert.assertTrue(savedAsset.getCreatedTime() > 0);
Assert.assertEquals(savedTenant.getId(), savedAsset.getTenantId());
Assert.assertNotNull(savedAsset.getCustomerId());
Assert.assertEquals(NULL_UUID, savedAsset.getCustomerId().getId());
Assert.assertEquals(asset.getName(), savedAsset.getName());
savedAsset.setName("My new asset");
doPost("/api/asset", savedAsset, Asset.class);
Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertEquals(foundAsset.getName(), savedAsset.getName());
}
@Test
public void testFindAssetById() throws Exception {
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertNotNull(foundAsset);
Assert.assertEquals(savedAsset, foundAsset);
}
@Test
public void testFindAssetTypesByTenantId() throws Exception {
List<Asset> assets = new ArrayList<>();
for (int i=0;i<3;i++) {
Asset asset = new Asset();
asset.setName("My asset B"+i);
asset.setType("typeB");
assets.add(doPost("/api/asset", asset, Asset.class));
}
for (int i=0;i<7;i++) {
Asset asset = new Asset();
asset.setName("My asset C"+i);
asset.setType("typeC");
assets.add(doPost("/api/asset", asset, Asset.class));
}
for (int i=0;i<9;i++) {
Asset asset = new Asset();
asset.setName("My asset A"+i);
asset.setType("typeA");
assets.add(doPost("/api/asset", asset, Asset.class));
}
List<TenantAssetType> assetTypes = doGetTyped("/api/asset/types",
new TypeReference<List<TenantAssetType>>(){});
Assert.assertNotNull(assetTypes);
Assert.assertEquals(3, assetTypes.size());
Assert.assertEquals("typeA", assetTypes.get(0).getType());
Assert.assertEquals("typeB", assetTypes.get(1).getType());
Assert.assertEquals("typeC", assetTypes.get(2).getType());
}
@Test
public void testDeleteAsset() throws Exception {
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
doDelete("/api/asset/"+savedAsset.getId().getId().toString())
.andExpect(status().isOk());
doGet("/api/asset/"+savedAsset.getId().getId().toString())
.andExpect(status().isNotFound());
}
@Test
public void testSaveAssetWithEmptyType() throws Exception {
Asset asset = new Asset();
asset.setName("My asset");
doPost("/api/asset", asset)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Asset type should be specified")));
}
@Test
public void testSaveAssetWithEmptyName() throws Exception {
Asset asset = new Asset();
asset.setType("default");
doPost("/api/asset", asset)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Asset name should be specified")));
}
@Test
public void testAssignUnassignAssetToCustomer() throws Exception {
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
Customer customer = new Customer();
customer.setTitle("My customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
Asset assignedAsset = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertEquals(savedCustomer.getId(), assignedAsset.getCustomerId());
Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertEquals(savedCustomer.getId(), foundAsset.getCustomerId());
Asset unassignedAsset =
doDelete("/api/customer/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertEquals(ModelConstants.NULL_UUID, unassignedAsset.getCustomerId().getId());
foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertEquals(ModelConstants.NULL_UUID, foundAsset.getCustomerId().getId());
}
@Test
public void testAssignAssetToNonExistentCustomer() throws Exception {
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
doPost("/api/customer/" + UUIDs.timeBased().toString()
+ "/asset/" + savedAsset.getId().getId().toString())
.andExpect(status().isNotFound());
}
@Test
public void testAssignAssetToCustomerFromDifferentTenant() throws Exception {
loginSysAdmin();
Tenant tenant2 = new Tenant();
tenant2.setTitle("Different tenant");
Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class);
Assert.assertNotNull(savedTenant2);
User tenantAdmin2 = new User();
tenantAdmin2.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin2.setTenantId(savedTenant2.getId());
tenantAdmin2.setEmail("tenant3@thingsboard.org");
tenantAdmin2.setFirstName("Joe");
tenantAdmin2.setLastName("Downs");
tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1");
Customer customer = new Customer();
customer.setTitle("Different customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
login(tenantAdmin.getEmail(), "testPassword1");
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString())
.andExpect(status().isForbidden());
loginSysAdmin();
doDelete("/api/tenant/"+savedTenant2.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testFindTenantAssets() throws Exception {
List<Asset> assets = new ArrayList<>();
for (int i=0;i<178;i++) {
Asset asset = new Asset();
asset.setName("Asset"+i);
asset.setType("default");
assets.add(doPost("/api/asset", asset, Asset.class));
}
List<Asset> loadedAssets = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(23);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/assets?",
new TypeReference<TextPageData<Asset>>(){}, pageLink);
loadedAssets.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assets, idComparator);
Collections.sort(loadedAssets, idComparator);
Assert.assertEquals(assets, loadedAssets);
}
@Test
public void testFindTenantAssetsByName() throws Exception {
String title1 = "Asset title 1";
List<Asset> assetsTitle1 = new ArrayList<>();
for (int i=0;i<143;i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
assetsTitle1.add(doPost("/api/asset", asset, Asset.class));
}
String title2 = "Asset title 2";
List<Asset> assetsTitle2 = new ArrayList<>();
for (int i=0;i<75;i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
assetsTitle2.add(doPost("/api/asset", asset, Asset.class));
}
List<Asset> loadedAssetsTitle1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/assets?",
new TypeReference<TextPageData<Asset>>(){}, pageLink);
loadedAssetsTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsTitle1, idComparator);
Collections.sort(loadedAssetsTitle1, idComparator);
Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
List<Asset> loadedAssetsTitle2 = new ArrayList<>();
pageLink = new TextPageLink(4, title2);
do {
pageData = doGetTypedWithPageLink("/api/tenant/assets?",
new TypeReference<TextPageData<Asset>>(){}, pageLink);
loadedAssetsTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsTitle2, idComparator);
Collections.sort(loadedAssetsTitle2, idComparator);
Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
for (Asset asset : loadedAssetsTitle1) {
doDelete("/api/asset/"+asset.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4, title1);
pageData = doGetTypedWithPageLink("/api/tenant/assets?",
new TypeReference<TextPageData<Asset>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Asset asset : loadedAssetsTitle2) {
doDelete("/api/asset/"+asset.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4, title2);
pageData = doGetTypedWithPageLink("/api/tenant/assets?",
new TypeReference<TextPageData<Asset>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindTenantAssetsByType() throws Exception {
String title1 = "Asset title 1";
String type1 = "typeA";
List<Asset> assetsType1 = new ArrayList<>();
for (int i=0;i<143;i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type1);
assetsType1.add(doPost("/api/asset", asset, Asset.class));
}
String title2 = "Asset title 2";
String type2 = "typeB";
List<Asset> assetsType2 = new ArrayList<>();
for (int i=0;i<75;i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type2);
assetsType2.add(doPost("/api/asset", asset, Asset.class));
}
List<Asset> loadedAssetsType1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
loadedAssetsType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsType1, idComparator);
Collections.sort(loadedAssetsType1, idComparator);
Assert.assertEquals(assetsType1, loadedAssetsType1);
List<Asset> loadedAssetsType2 = new ArrayList<>();
pageLink = new TextPageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
loadedAssetsType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsType2, idComparator);
Collections.sort(loadedAssetsType2, idComparator);
Assert.assertEquals(assetsType2, loadedAssetsType2);
for (Asset asset : loadedAssetsType1) {
doDelete("/api/asset/"+asset.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Asset asset : loadedAssetsType2) {
doDelete("/api/asset/"+asset.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/assets?type={type}&",
new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindCustomerAssets() throws Exception {
Customer customer = new Customer();
customer.setTitle("Test customer");
customer = doPost("/api/customer", customer, Customer.class);
CustomerId customerId = customer.getId();
List<Asset> assets = new ArrayList<>();
for (int i=0;i<128;i++) {
Asset asset = new Asset();
asset.setName("Asset"+i);
asset.setType("default");
asset = doPost("/api/asset", asset, Asset.class);
assets.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/asset/" + asset.getId().getId().toString(), Asset.class));
}
List<Asset> loadedAssets = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(23);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
new TypeReference<TextPageData<Asset>>(){}, pageLink);
loadedAssets.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assets, idComparator);
Collections.sort(loadedAssets, idComparator);
Assert.assertEquals(assets, loadedAssets);
}
@Test
public void testFindCustomerAssetsByName() throws Exception {
Customer customer = new Customer();
customer.setTitle("Test customer");
customer = doPost("/api/customer", customer, Customer.class);
CustomerId customerId = customer.getId();
String title1 = "Asset title 1";
List<Asset> assetsTitle1 = new ArrayList<>();
for (int i=0;i<125;i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
asset = doPost("/api/asset", asset, Asset.class);
assetsTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/asset/" + asset.getId().getId().toString(), Asset.class));
}
String title2 = "Asset title 2";
List<Asset> assetsTitle2 = new ArrayList<>();
for (int i=0;i<143;i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
asset = doPost("/api/asset", asset, Asset.class);
assetsTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/asset/" + asset.getId().getId().toString(), Asset.class));
}
List<Asset> loadedAssetsTitle1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
new TypeReference<TextPageData<Asset>>(){}, pageLink);
loadedAssetsTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsTitle1, idComparator);
Collections.sort(loadedAssetsTitle1, idComparator);
Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
List<Asset> loadedAssetsTitle2 = new ArrayList<>();
pageLink = new TextPageLink(4, title2);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
new TypeReference<TextPageData<Asset>>(){}, pageLink);
loadedAssetsTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsTitle2, idComparator);
Collections.sort(loadedAssetsTitle2, idComparator);
Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
for (Asset asset : loadedAssetsTitle1) {
doDelete("/api/customer/asset/" + asset.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4, title1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
new TypeReference<TextPageData<Asset>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Asset asset : loadedAssetsTitle2) {
doDelete("/api/customer/asset/" + asset.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4, title2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?",
new TypeReference<TextPageData<Asset>>(){}, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindCustomerAssetsByType() throws Exception {
Customer customer = new Customer();
customer.setTitle("Test customer");
customer = doPost("/api/customer", customer, Customer.class);
CustomerId customerId = customer.getId();
String title1 = "Asset title 1";
String type1 = "typeC";
List<Asset> assetsType1 = new ArrayList<>();
for (int i=0;i<125;i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type1);
asset = doPost("/api/asset", asset, Asset.class);
assetsType1.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/asset/" + asset.getId().getId().toString(), Asset.class));
}
String title2 = "Asset title 2";
String type2 = "typeD";
List<Asset> assetsType2 = new ArrayList<>();
for (int i=0;i<143;i++) {
Asset asset = new Asset();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type2);
asset = doPost("/api/asset", asset, Asset.class);
assetsType2.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/asset/" + asset.getId().getId().toString(), Asset.class));
}
List<Asset> loadedAssetsType1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Asset> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
loadedAssetsType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsType1, idComparator);
Collections.sort(loadedAssetsType1, idComparator);
Assert.assertEquals(assetsType1, loadedAssetsType1);
List<Asset> loadedAssetsType2 = new ArrayList<>();
pageLink = new TextPageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
loadedAssetsType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsType2, idComparator);
Collections.sort(loadedAssetsType2, idComparator);
Assert.assertEquals(assetsType2, loadedAssetsType2);
for (Asset asset : loadedAssetsType1) {
doDelete("/api/customer/asset/" + asset.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
new TypeReference<TextPageData<Asset>>(){}, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Asset asset : loadedAssetsType2) {
doDelete("/api/customer/asset/" + asset.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/assets?type={type}&",
new TypeReference<TextPageData<Asset>>(){}, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
}

244
application/src/test/java/org/thingsboard/server/controller/DeviceControllerTest.java

@ -24,10 +24,7 @@ import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.RandomStringUtils;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceCredentialsId;
import org.thingsboard.server.common.data.id.DeviceId;
@ -83,6 +80,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
public void testSaveDevice() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
Assert.assertNotNull(savedDevice);
@ -114,16 +112,49 @@ public class DeviceControllerTest extends AbstractControllerTest {
public void testFindDeviceById() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
Assert.assertNotNull(foundDevice);
Assert.assertEquals(savedDevice, foundDevice);
}
@Test
public void testFindDeviceTypesByTenantId() throws Exception {
List<Device> devices = new ArrayList<>();
for (int i=0;i<3;i++) {
Device device = new Device();
device.setName("My device B"+i);
device.setType("typeB");
devices.add(doPost("/api/device", device, Device.class));
}
for (int i=0;i<7;i++) {
Device device = new Device();
device.setName("My device C"+i);
device.setType("typeC");
devices.add(doPost("/api/device", device, Device.class));
}
for (int i=0;i<9;i++) {
Device device = new Device();
device.setName("My device A"+i);
device.setType("typeA");
devices.add(doPost("/api/device", device, Device.class));
}
List<TenantDeviceType> deviceTypes = doGetTyped("/api/device/types",
new TypeReference<List<TenantDeviceType>>(){});
Assert.assertNotNull(deviceTypes);
Assert.assertEquals(3, deviceTypes.size());
Assert.assertEquals("typeA", deviceTypes.get(0).getType());
Assert.assertEquals("typeB", deviceTypes.get(1).getType());
Assert.assertEquals("typeC", deviceTypes.get(2).getType());
}
@Test
public void testDeleteDevice() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doDelete("/api/device/"+savedDevice.getId().getId().toString())
@ -132,10 +163,20 @@ public class DeviceControllerTest extends AbstractControllerTest {
doGet("/api/device/"+savedDevice.getId().getId().toString())
.andExpect(status().isNotFound());
}
@Test
public void testSaveDeviceWithEmptyType() throws Exception {
Device device = new Device();
device.setName("My device");
doPost("/api/device", device)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device type should be specified")));
}
@Test
public void testSaveDeviceWithEmptyName() throws Exception {
Device device = new Device();
device.setType("default");
doPost("/api/device", device)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Device name should be specified")));
@ -145,6 +186,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
public void testAssignUnassignDeviceToCustomer() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
Customer customer = new Customer();
@ -170,6 +212,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
public void testAssignDeviceToNonExistentCustomer() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/customer/" + UUIDs.timeBased().toString()
@ -203,6 +246,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/customer/" + savedCustomer.getId().getId().toString()
@ -219,6 +263,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
public void testFindDeviceCredentialsByDeviceId() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@ -229,6 +274,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
public void testSaveDeviceCredentials() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@ -255,6 +301,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
public void testSaveDeviceCredentialsWithEmptyCredentialsType() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@ -268,6 +315,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
public void testSaveDeviceCredentialsWithEmptyCredentialsId() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@ -281,6 +329,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
public void testSaveNonExistentDeviceCredentials() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@ -298,6 +347,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
public void testSaveDeviceCredentialsWithNonExistentDevice() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@ -307,9 +357,10 @@ public class DeviceControllerTest extends AbstractControllerTest {
}
@Test
public void testSaveDeviceCredentialsWithInvalidCredemtialsIdLength() throws Exception {
public void testSaveDeviceCredentialsWithInvalidCredentialsIdLength() throws Exception {
Device device = new Device();
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
@ -325,6 +376,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
for (int i=0;i<178;i++) {
Device device = new Device();
device.setName("Device"+i);
device.setType("default");
devices.add(doPost("/api/device", device, Device.class));
}
List<Device> loadedDevices = new ArrayList<>();
@ -355,6 +407,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
devicesTitle1.add(doPost("/api/device", device, Device.class));
}
String title2 = "Device title 2";
@ -365,6 +418,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
devicesTitle2.add(doPost("/api/device", device, Device.class));
}
@ -423,6 +477,89 @@ public class DeviceControllerTest extends AbstractControllerTest {
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindTenantDevicesByType() throws Exception {
String title1 = "Device title 1";
String type1 = "typeA";
List<Device> devicesType1 = new ArrayList<>();
for (int i=0;i<143;i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type1);
devicesType1.add(doPost("/api/device", device, Device.class));
}
String title2 = "Device title 2";
String type2 = "typeB";
List<Device> devicesType2 = new ArrayList<>();
for (int i=0;i<75;i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type2);
devicesType2.add(doPost("/api/device", device, Device.class));
}
List<Device> loadedDevicesType1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
loadedDevicesType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType1, idComparator);
Collections.sort(loadedDevicesType1, idComparator);
Assert.assertEquals(devicesType1, loadedDevicesType1);
List<Device> loadedDevicesType2 = new ArrayList<>();
pageLink = new TextPageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
loadedDevicesType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType2, idComparator);
Collections.sort(loadedDevicesType2, idComparator);
Assert.assertEquals(devicesType2, loadedDevicesType2);
for (Device device : loadedDevicesType1) {
doDelete("/api/device/"+device.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesType2) {
doDelete("/api/device/"+device.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindCustomerDevices() throws Exception {
@ -435,6 +572,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
for (int i=0;i<128;i++) {
Device device = new Device();
device.setName("Device"+i);
device.setType("default");
device = doPost("/api/device", device, Device.class);
devices.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
@ -473,6 +611,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
device = doPost("/api/device", device, Device.class);
devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
@ -485,6 +624,7 @@ public class DeviceControllerTest extends AbstractControllerTest {
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
device = doPost("/api/device", device, Device.class);
devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
@ -546,4 +686,96 @@ public class DeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindCustomerDevicesByType() throws Exception {
Customer customer = new Customer();
customer.setTitle("Test customer");
customer = doPost("/api/customer", customer, Customer.class);
CustomerId customerId = customer.getId();
String title1 = "Device title 1";
String type1 = "typeC";
List<Device> devicesType1 = new ArrayList<>();
for (int i=0;i<125;i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type1);
device = doPost("/api/device", device, Device.class);
devicesType1.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
}
String title2 = "Device title 2";
String type2 = "typeD";
List<Device> devicesType2 = new ArrayList<>();
for (int i=0;i<143;i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type2);
device = doPost("/api/device", device, Device.class);
devicesType2.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
}
List<Device> loadedDevicesType1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
loadedDevicesType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType1, idComparator);
Collections.sort(loadedDevicesType1, idComparator);
Assert.assertEquals(devicesType1, loadedDevicesType1);
List<Device> loadedDevicesType2 = new ArrayList<>();
pageLink = new TextPageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
loadedDevicesType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType2, idComparator);
Collections.sort(loadedDevicesType2, idComparator);
Assert.assertEquals(devicesType2, loadedDevicesType2);
for (Device device : loadedDevicesType1) {
doDelete("/api/customer/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<TextPageData<Device>>(){}, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesType2) {
doDelete("/api/customer/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new TextPageLink(4);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<TextPageData<Device>>(){}, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
}

2
application/src/test/java/org/thingsboard/server/controller/TenantControllerTest.java

@ -130,7 +130,7 @@ public class TenantControllerTest extends AbstractControllerTest {
Assert.assertEquals(tenants, loadedTenants);
for (Tenant tenant : loadedTenants) {
if (!tenant.getTitle().equals("Tenant")) {
if (!tenant.getTitle().equals(TEST_TENANT_NAME)) {
doDelete("/api/tenant/"+tenant.getId().getId().toString())
.andExpect(status().isOk());
}

2
application/src/test/java/org/thingsboard/server/controller/UserControllerTest.java

@ -182,7 +182,7 @@ public class UserControllerTest extends AbstractControllerTest {
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
String email = "tenant@thingsboard.org";
String email = TENANT_ADMIN_EMAIL;
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedTenant.getId());

1
application/src/test/java/org/thingsboard/server/system/HttpDeviceApiTest.java

@ -47,6 +47,7 @@ public class HttpDeviceApiTest extends AbstractControllerTest {
loginTenantAdmin();
device = new Device();
device.setName("My device");
device.setType("default");
device = doPost("/api/device", device, Device.class);
deviceCredentials =

7
common/data/src/main/java/org/thingsboard/server/common/data/Customer.java

@ -20,7 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import com.fasterxml.jackson.databind.JsonNode;
public class Customer extends ContactBased<CustomerId>{
public class Customer extends ContactBased<CustomerId> implements HasName {
private static final long serialVersionUID = -1599722990298929275L;
@ -59,6 +59,11 @@ public class Customer extends ContactBased<CustomerId>{
this.title = title;
}
@Override
public String getName() {
return title;
}
public JsonNode getAdditionalInfo() {
return additionalInfo;
}

7
common/data/src/main/java/org/thingsboard/server/common/data/DashboardInfo.java

@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId;
public class DashboardInfo extends SearchTextBased<DashboardId> {
public class DashboardInfo extends SearchTextBased<DashboardId> implements HasName {
private TenantId tenantId;
private CustomerId customerId;
@ -64,6 +64,11 @@ public class DashboardInfo extends SearchTextBased<DashboardId> {
this.title = title;
}
@Override
public String getName() {
return title;
}
@Override
public String getSearchText() {
return title;

3
common/data/src/main/java/org/thingsboard/server/common/data/Device.java

@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import com.fasterxml.jackson.databind.JsonNode;
public class Device extends SearchTextBased<DeviceId> {
public class Device extends SearchTextBased<DeviceId> implements HasName {
private static final long serialVersionUID = 2807343040519543363L;
@ -64,6 +64,7 @@ public class Device extends SearchTextBased<DeviceId> {
this.customerId = customerId;
}
@Override
public String getName() {
return name;
}

13
ui/src/app/dashboard/device-aliases.scss → common/data/src/main/java/org/thingsboard/server/common/data/HasName.java

@ -13,13 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data;
public interface HasName {
String getName();
.tb-aliases-dialog {
.md-dialog-content {
padding-bottom: 0px;
}
.tb-alias {
padding: 10px 0 0 10px;
margin: 5px;
}
}

7
common/data/src/main/java/org/thingsboard/server/common/data/Tenant.java

@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import com.fasterxml.jackson.databind.JsonNode;
public class Tenant extends ContactBased<TenantId>{
public class Tenant extends ContactBased<TenantId> implements HasName {
private static final long serialVersionUID = 8057243243859922101L;
@ -50,6 +50,11 @@ public class Tenant extends ContactBased<TenantId>{
this.title = title;
}
@Override
public String getName() {
return title;
}
public String getRegion() {
return region;
}

79
common/data/src/main/java/org/thingsboard/server/common/data/TenantDeviceType.java

@ -0,0 +1,79 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data;
import org.thingsboard.server.common.data.id.TenantId;
public class TenantDeviceType {
private static final long serialVersionUID = 8057240243859922101L;
private String type;
private TenantId tenantId;
public TenantDeviceType() {
super();
}
public TenantDeviceType(String type, TenantId tenantId) {
this.type = type;
this.tenantId = tenantId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public TenantId getTenantId() {
return tenantId;
}
public void setTenantId(TenantId tenantId) {
this.tenantId = tenantId;
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TenantDeviceType that = (TenantDeviceType) o;
if (type != null ? !type.equals(that.type) : that.type != null) return false;
return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("TenantDeviceType{");
sb.append("type='").append(type).append('\'');
sb.append(", tenantId=").append(tenantId);
sb.append('}');
return sb.toString();
}
}

7
common/data/src/main/java/org/thingsboard/server/common/data/User.java

@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.security.Authority;
import com.fasterxml.jackson.databind.JsonNode;
public class User extends SearchTextBased<UserId> {
public class User extends SearchTextBased<UserId> implements HasName {
private static final long serialVersionUID = 8250339805336035966L;
@ -77,6 +77,11 @@ public class User extends SearchTextBased<UserId> {
this.email = email;
}
@Override
public String getName() {
return email;
}
public Authority getAuthority() {
return authority;
}

7
common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java

@ -18,13 +18,14 @@ package org.thingsboard.server.common.data.alarm;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.id.EntityId;
/**
* Created by ashvayka on 11.05.17.
*/
@Data
public class Alarm extends BaseData<AlarmId> {
public class Alarm extends BaseData<AlarmId> implements HasName {
private long startTs;
private long endTs;
@ -37,4 +38,8 @@ public class Alarm extends BaseData<AlarmId> {
private JsonNode details;
private boolean propagate;
@Override
public String getName() {
return type;
}
}

4
common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java

@ -16,12 +16,13 @@
package org.thingsboard.server.common.data.asset;
import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
public class Asset extends SearchTextBased<AssetId> {
public class Asset extends SearchTextBased<AssetId> implements HasName {
private static final long serialVersionUID = 2807343040519543363L;
@ -64,6 +65,7 @@ public class Asset extends SearchTextBased<AssetId> {
this.customerId = customerId;
}
@Override
public String getName() {
return name;
}

79
common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java

@ -0,0 +1,79 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.asset;
import org.thingsboard.server.common.data.id.TenantId;
public class TenantAssetType {
private static final long serialVersionUID = 8057290243855622101L;
private String type;
private TenantId tenantId;
public TenantAssetType() {
super();
}
public TenantAssetType(String type, TenantId tenantId) {
this.type = type;
this.tenantId = tenantId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public TenantId getTenantId() {
return tenantId;
}
public void setTenantId(TenantId tenantId) {
this.tenantId = tenantId;
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TenantAssetType that = (TenantAssetType) o;
if (type != null ? !type.equals(that.type) : that.type != null) return false;
return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("TenantAssetType{");
sb.append("type='").append(type).append('\'');
sb.append(", tenantId=").append(tenantId);
sb.append('}');
return sb.toString();
}
}

4
common/data/src/main/java/org/thingsboard/server/common/data/plugin/PluginMetaData.java

@ -15,13 +15,14 @@
*/
package org.thingsboard.server.common.data.plugin;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.TenantId;
import com.fasterxml.jackson.databind.JsonNode;
public class PluginMetaData extends SearchTextBased<PluginId> {
public class PluginMetaData extends SearchTextBased<PluginId> implements HasName {
private static final long serialVersionUID = 1L;
@ -75,6 +76,7 @@ public class PluginMetaData extends SearchTextBased<PluginId> {
this.tenantId = tenantId;
}
@Override
public String getName() {
return name;
}

10
common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelation.java

@ -47,11 +47,11 @@ public class EntityRelation {
this.additionalInfo = additionalInfo;
}
public EntityRelation(EntityRelation device) {
this.from = device.getFrom();
this.to = device.getTo();
this.type = device.getType();
this.additionalInfo = device.getAdditionalInfo();
public EntityRelation(EntityRelation entityRelation) {
this.from = entityRelation.getFrom();
this.to = entityRelation.getTo();
this.type = entityRelation.getType();
this.additionalInfo = entityRelation.getAdditionalInfo();
}
public EntityId getFrom() {

59
common/data/src/main/java/org/thingsboard/server/common/data/relation/EntityRelationInfo.java

@ -0,0 +1,59 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.relation;
public class EntityRelationInfo extends EntityRelation {
private static final long serialVersionUID = 2807343097519543363L;
private String toName;
public EntityRelationInfo() {
super();
}
public EntityRelationInfo(EntityRelation entityRelation) {
super(entityRelation);
}
public String getToName() {
return toName;
}
public void setToName(String toName) {
this.toName = toName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
EntityRelationInfo that = (EntityRelationInfo) o;
return toName != null ? toName.equals(that.toName) : that.toName == null;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (toName != null ? toName.hashCode() : 0);
return result;
}
}

8
common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleMetaData.java

@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.rule;
import lombok.Data;
import lombok.ToString;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.RuleId;
@ -26,7 +27,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
@Data
public class RuleMetaData extends SearchTextBased<RuleId> {
public class RuleMetaData extends SearchTextBased<RuleId> implements HasName {
private static final long serialVersionUID = -5656679015122935465L;
@ -66,4 +67,9 @@ public class RuleMetaData extends SearchTextBased<RuleId> {
return name;
}
@Override
public String getName() {
return name;
}
}

4
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java

@ -28,6 +28,10 @@ import java.util.Optional;
*/
public interface AlarmService {
Alarm findAlarmById(AlarmId alarmId);
ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId);
Optional<Alarm> saveIfNotExists(Alarm alarm);
ListenableFuture<Boolean> updateAlarm(Alarm alarm);

66
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -0,0 +1,66 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.alarm;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.page.TimePageData;
import java.util.Optional;
@Service
@Slf4j
public class BaseAlarmService implements AlarmService {
@Override
public Alarm findAlarmById(AlarmId alarmId) {
return null;
}
@Override
public ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId) {
return null;
}
@Override
public Optional<Alarm> saveIfNotExists(Alarm alarm) {
return null;
}
@Override
public ListenableFuture<Boolean> updateAlarm(Alarm alarm) {
return null;
}
@Override
public ListenableFuture<Boolean> ackAlarm(Alarm alarm) {
return null;
}
@Override
public ListenableFuture<Boolean> clearAlarm(AlarmId alarmId) {
return null;
}
@Override
public ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query) {
return null;
}
}

32
dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java

@ -16,12 +16,11 @@
package org.thingsboard.server.dao.asset;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.model.AssetEntity;
import org.thingsboard.server.dao.model.DeviceEntity;
import org.thingsboard.server.dao.model.TenantAssetTypeEntity;
import java.util.List;
import java.util.Optional;
@ -50,6 +49,16 @@ public interface AssetDao extends Dao<AssetEntity> {
*/
List<AssetEntity> findAssetsByTenantId(UUID tenantId, TextPageLink pageLink);
/**
* Find assets by tenantId, type and page link.
*
* @param tenantId the tenantId
* @param type the type
* @param pageLink the page link
* @return the list of asset objects
*/
List<AssetEntity> findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink);
/**
* Find assets by tenantId and assets Ids.
*
@ -69,6 +78,17 @@ public interface AssetDao extends Dao<AssetEntity> {
*/
List<AssetEntity> findAssetsByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
/**
* Find assets by tenantId, customerId, type and page link.
*
* @param tenantId the tenantId
* @param customerId the customerId
* @param type the type
* @param pageLink the page link
* @return the list of asset objects
*/
List<AssetEntity> findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink);
/**
* Find assets by tenantId, customerId and assets Ids.
*
@ -87,4 +107,12 @@ public interface AssetDao extends Dao<AssetEntity> {
* @return the optional asset object
*/
Optional<AssetEntity> findAssetsByTenantIdAndName(UUID tenantId, String name);
/**
* Find tenants asset types.
*
* @return the list of tenant asset type objects
*/
ListenableFuture<List<TenantAssetTypeEntity>> findTenantAssetTypesAsync();
}

50
dao/src/main/java/org/thingsboard/server/dao/asset/AssetDaoImpl.java

@ -15,7 +15,12 @@
*/
package org.thingsboard.server.dao.asset;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.mapping.Result;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@ -23,7 +28,9 @@ import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.AbstractSearchTextDao;
import org.thingsboard.server.dao.model.AssetEntity;
import org.thingsboard.server.dao.model.TenantAssetTypeEntity;
import javax.annotation.Nullable;
import java.util.*;
import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
@ -59,6 +66,16 @@ public class AssetDaoImpl extends AbstractSearchTextDao<AssetEntity> implements
return assetEntities;
}
@Override
public List<AssetEntity> findAssetsByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) {
log.debug("Try to find assets by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ASSET_TYPE_PROPERTY, type),
eq(ASSET_TENANT_ID_PROPERTY, tenantId)), pageLink);
log.trace("Found assets [{}] by tenantId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, type, pageLink);
return assetEntities;
}
@Override
public ListenableFuture<List<AssetEntity>> findAssetsByTenantIdAndIdsAsync(UUID tenantId, List<UUID> assetIds) {
log.debug("Try to find assets by tenantId [{}] and asset Ids [{}]", tenantId, assetIds);
@ -81,6 +98,19 @@ public class AssetDaoImpl extends AbstractSearchTextDao<AssetEntity> implements
return assetEntities;
}
@Override
public List<AssetEntity> findAssetsByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) {
log.debug("Try to find assets by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(ASSET_TYPE_PROPERTY, type),
eq(ASSET_CUSTOMER_ID_PROPERTY, customerId),
eq(ASSET_TENANT_ID_PROPERTY, tenantId)),
pageLink);
log.trace("Found assets [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", assetEntities, tenantId, customerId, type, pageLink);
return assetEntities;
}
@Override
public ListenableFuture<List<AssetEntity>> findAssetsByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List<UUID> assetIds) {
log.debug("Try to find assets by tenantId [{}], customerId [{}] and asset Ids [{}]", tenantId, customerId, assetIds);
@ -101,4 +131,24 @@ public class AssetDaoImpl extends AbstractSearchTextDao<AssetEntity> implements
return Optional.ofNullable(findOneByStatement(query));
}
@Override
public ListenableFuture<List<TenantAssetTypeEntity>> findTenantAssetTypesAsync() {
Select statement = select().distinct().column(ASSET_TYPE_PROPERTY).column(ASSET_TENANT_ID_PROPERTY).from(ASSET_TYPES_BY_TENANT_VIEW_NAME);
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = getSession().executeAsync(statement);
ListenableFuture<List<TenantAssetTypeEntity>> result = Futures.transform(resultSetFuture, new Function<ResultSet, List<TenantAssetTypeEntity>>() {
@Nullable
@Override
public List<TenantAssetTypeEntity> apply(@Nullable ResultSet resultSet) {
Result<TenantAssetTypeEntity> result = cluster.getMapper(TenantAssetTypeEntity.class).map(resultSet);
if (result != null) {
return result.all();
} else {
return Collections.emptyList();
}
}
});
return result;
}
}

8
dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.asset;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.TenantAssetType;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
@ -34,7 +35,7 @@ public interface AssetService {
Optional<Asset> findAssetByTenantIdAndName(TenantId tenantId, String name);
Asset saveAsset(Asset device);
Asset saveAsset(Asset asset);
Asset assignAssetToCustomer(AssetId assetId, CustomerId customerId);
@ -44,16 +45,21 @@ public interface AssetService {
TextPageData<Asset> findAssetsByTenantId(TenantId tenantId, TextPageLink pageLink);
TextPageData<Asset> findAssetsByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink);
ListenableFuture<List<Asset>> findAssetsByTenantIdAndIdsAsync(TenantId tenantId, List<AssetId> assetIds);
void deleteAssetsByTenantId(TenantId tenantId);
TextPageData<Asset> findAssetsByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
TextPageData<Asset> findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink);
ListenableFuture<List<Asset>> findAssetsByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<AssetId> assetIds);
void unassignCustomerAssets(TenantId tenantId, CustomerId customerId);
ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query);
ListenableFuture<List<TenantAssetType>> findAssetTypesByTenantId(TenantId tenantId);
}

59
dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java

@ -26,6 +26,7 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.TenantAssetType;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
@ -34,12 +35,9 @@ import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.AssetEntity;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.thingsboard.server.dao.model.TenantEntity;
import org.thingsboard.server.dao.relation.EntityRelationsQuery;
import org.thingsboard.server.dao.model.*;
import org.thingsboard.server.dao.relation.EntitySearchDirection;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
@ -57,7 +55,7 @@ import static org.thingsboard.server.dao.service.Validator.*;
@Service
@Slf4j
public class BaseAssetService extends BaseEntityService implements AssetService {
public class BaseAssetService extends AbstractEntityService implements AssetService {
@Autowired
private AssetDao assetDao;
@ -132,7 +130,18 @@ public class BaseAssetService extends BaseEntityService implements AssetService
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<AssetEntity> assetEntities = assetDao.findAssetsByTenantId(tenantId.getId(), pageLink);
List<Asset> assets = convertDataList(assetEntities);
return new TextPageData<Asset>(assets, pageLink);
return new TextPageData<>(assets, pageLink);
}
@Override
public TextPageData<Asset> findAssetsByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, "Incorrect tenantId " + tenantId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<AssetEntity> assetEntities = assetDao.findAssetsByTenantIdAndType(tenantId.getId(), type, pageLink);
List<Asset> assets = convertDataList(assetEntities);
return new TextPageData<>(assets, pageLink);
}
@Override
@ -159,7 +168,19 @@ public class BaseAssetService extends BaseEntityService implements AssetService
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<AssetEntity> assetEntities = assetDao.findAssetsByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
List<Asset> assets = convertDataList(assetEntities);
return new TextPageData<Asset>(assets, pageLink);
return new TextPageData<>(assets, pageLink);
}
@Override
public TextPageData<Asset> findAssetsByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink) {
log.trace("Executing findAssetsByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
validateId(tenantId, "Incorrect tenantId " + tenantId);
validateId(customerId, "Incorrect customerId " + customerId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<AssetEntity> assetEntities = assetDao.findAssetsByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
List<Asset> assets = convertDataList(assetEntities);
return new TextPageData<>(assets, pageLink);
}
@Override
@ -207,6 +228,25 @@ public class BaseAssetService extends BaseEntityService implements AssetService
return assets;
}
@Override
public ListenableFuture<List<TenantAssetType>> findAssetTypesByTenantId(TenantId tenantId) {
log.trace("Executing findAssetTypesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, "Incorrect tenantId " + tenantId);
ListenableFuture<List<TenantAssetTypeEntity>> tenantAssetTypeEntities = assetDao.findTenantAssetTypesAsync();
ListenableFuture<List<TenantAssetType>> tenantAssetTypes = Futures.transform(tenantAssetTypeEntities,
(Function<List<TenantAssetTypeEntity>, List<TenantAssetType>>) assetTypeEntities -> {
List<TenantAssetType> assetTypes = new ArrayList<>();
for (TenantAssetTypeEntity assetTypeEntity : assetTypeEntities) {
if (assetTypeEntity.getTenantId().equals(tenantId.getId())) {
assetTypes.add(assetTypeEntity.toTenantAssetType());
}
}
assetTypes.sort((TenantAssetType o1, TenantAssetType o2) -> o1.getType().compareTo(o2.getType()));
return assetTypes;
});
return tenantAssetTypes;
}
private DataValidator<Asset> assetValidator =
new DataValidator<Asset>() {
@ -232,6 +272,9 @@ public class BaseAssetService extends BaseEntityService implements AssetService
@Override
protected void validateDataImpl(Asset asset) {
if (StringUtils.isEmpty(asset.getType())) {
throw new DataValidationException("Asset type should be specified!");
}
if (StringUtils.isEmpty(asset.getName())) {
throw new DataValidationException("Asset name should be specified!");
}

1
dao/src/main/java/org/thingsboard/server/dao/cache/ServiceCacheConfiguration.java

@ -45,7 +45,6 @@ public class ServiceCacheConfiguration {
@Value("${cache.device_credentials.time_to_live}")
private Integer cacheDeviceCredentialsTTL;
@Value("${zk.enabled}")
private boolean zkEnabled;
@Value("${zk.url}")

6
dao/src/main/java/org/thingsboard/server/dao/customer/CustomerServiceImpl.java

@ -31,17 +31,15 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.AssetEntity;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.thingsboard.server.dao.model.TenantEntity;
import org.thingsboard.server.dao.service.DataValidator;
@ -53,7 +51,7 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.service.Validator;
@Service
@Slf4j
public class CustomerServiceImpl extends BaseEntityService implements CustomerService {
public class CustomerServiceImpl extends AbstractEntityService implements CustomerService {
private static final String PUBLIC_CUSTOMER_TITLE = "Public";

5
dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardService.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.dao.dashboard;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.id.CustomerId;
@ -27,8 +28,12 @@ public interface DashboardService {
public Dashboard findDashboardById(DashboardId dashboardId);
public ListenableFuture<Dashboard> findDashboardByIdAsync(DashboardId dashboardId);
public DashboardInfo findDashboardInfoById(DashboardId dashboardId);
public ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(DashboardId dashboardId);
public Dashboard saveDashboard(Dashboard dashboard);
public Dashboard assignDashboardToCustomer(DashboardId dashboardId, CustomerId customerId);

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

@ -17,9 +17,13 @@ package org.thingsboard.server.dao.dashboard;
import static org.thingsboard.server.dao.DaoUtil.convertDataList;
import static org.thingsboard.server.dao.DaoUtil.getData;
import static org.thingsboard.server.dao.service.Validator.validateId;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.Dashboard;
@ -30,7 +34,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.*;
import org.thingsboard.server.dao.service.DataValidator;
@ -42,7 +46,7 @@ import org.thingsboard.server.dao.service.Validator;
@Service
@Slf4j
public class DashboardServiceImpl extends BaseEntityService implements DashboardService {
public class DashboardServiceImpl extends AbstractEntityService implements DashboardService {
@Autowired
private DashboardDao dashboardDao;
@ -64,6 +68,14 @@ public class DashboardServiceImpl extends BaseEntityService implements Dashboard
return getData(dashboardEntity);
}
@Override
public ListenableFuture<Dashboard> findDashboardByIdAsync(DashboardId dashboardId) {
log.trace("Executing findDashboardByIdAsync [{}]", dashboardId);
validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
ListenableFuture<DashboardEntity> dashboardEntity = dashboardDao.findByIdAsync(dashboardId.getId());
return Futures.transform(dashboardEntity, (Function<? super DashboardEntity, ? extends Dashboard>) input -> getData(input));
}
@Override
public DashboardInfo findDashboardInfoById(DashboardId dashboardId) {
log.trace("Executing findDashboardInfoById [{}]", dashboardId);
@ -72,6 +84,14 @@ public class DashboardServiceImpl extends BaseEntityService implements Dashboard
return getData(dashboardInfoEntity);
}
@Override
public ListenableFuture<DashboardInfo> findDashboardInfoByIdAsync(DashboardId dashboardId) {
log.trace("Executing findDashboardInfoByIdAsync [{}]", dashboardId);
validateId(dashboardId, "Incorrect dashboardId " + dashboardId);
ListenableFuture<DashboardInfoEntity> dashboardInfoEntity = dashboardInfoDao.findByIdAsync(dashboardId.getId());
return Futures.transform(dashboardInfoEntity, (Function<? super DashboardInfoEntity, ? extends DashboardInfo>) input -> getData(input));
}
@Override
public Dashboard saveDashboard(Dashboard dashboard) {
log.trace("Executing saveDashboard [{}]", dashboard);

30
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java

@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.model.DeviceEntity;
import org.thingsboard.server.dao.model.TenantDeviceTypeEntity;
/**
* The Interface DeviceDao.
@ -48,6 +49,16 @@ public interface DeviceDao extends Dao<DeviceEntity> {
*/
List<DeviceEntity> findDevicesByTenantId(UUID tenantId, TextPageLink pageLink);
/**
* Find devices by tenantId, type and page link.
*
* @param tenantId the tenantId
* @param type the type
* @param pageLink the page link
* @return the list of device objects
*/
List<DeviceEntity> findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink);
/**
* Find devices by tenantId and devices Ids.
*
@ -67,6 +78,18 @@ public interface DeviceDao extends Dao<DeviceEntity> {
*/
List<DeviceEntity> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink);
/**
* Find devices by tenantId, customerId, type and page link.
*
* @param tenantId the tenantId
* @param customerId the customerId
* @param type the type
* @param pageLink the page link
* @return the list of device objects
*/
List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink);
/**
* Find devices by tenantId, customerId and devices Ids.
*
@ -85,4 +108,11 @@ public interface DeviceDao extends Dao<DeviceEntity> {
* @return the optional device object
*/
Optional<DeviceEntity> findDevicesByTenantIdAndName(UUID tenantId, String name);
/**
* Find tenants device types.
*
* @return the list of tenant device type objects
*/
ListenableFuture<List<TenantDeviceTypeEntity>> findTenantDeviceTypesAsync();
}

53
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDaoImpl.java

@ -22,7 +22,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.*;
import java.util.*;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.mapping.Result;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@ -32,6 +37,9 @@ import org.thingsboard.server.dao.AbstractSearchTextDao;
import org.thingsboard.server.dao.model.DeviceEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.thingsboard.server.dao.model.TenantDeviceTypeEntity;
import javax.annotation.Nullable;
@Component
@Slf4j
@ -63,6 +71,16 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
return deviceEntities;
}
@Override
public List<DeviceEntity> findDevicesByTenantIdAndType(UUID tenantId, String type, TextPageLink pageLink) {
log.debug("Try to find devices by tenantId [{}], type [{}] and pageLink [{}]", tenantId, type, pageLink);
List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type),
eq(DEVICE_TENANT_ID_PROPERTY, tenantId)), pageLink);
log.trace("Found devices [{}] by tenantId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, type, pageLink);
return deviceEntities;
}
@Override
public ListenableFuture<List<DeviceEntity>> findDevicesByTenantIdAndIdsAsync(UUID tenantId, List<UUID> deviceIds) {
log.debug("Try to find devices by tenantId [{}] and device Ids [{}]", tenantId, deviceIds);
@ -75,7 +93,7 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
@Override
public List<DeviceEntity> findDevicesByTenantIdAndCustomerId(UUID tenantId, UUID customerId, TextPageLink pageLink) {
log.debug("Try to find devices by tenantId [{}], customerId[{}] and pageLink [{}]", tenantId, customerId, pageLink);
log.debug("Try to find devices by tenantId [{}], customerId [{}] and pageLink [{}]", tenantId, customerId, pageLink);
List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
@ -85,6 +103,19 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
return deviceEntities;
}
@Override
public List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndType(UUID tenantId, UUID customerId, String type, TextPageLink pageLink) {
log.debug("Try to find devices by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", tenantId, customerId, type, pageLink);
List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
Arrays.asList(eq(DEVICE_TYPE_PROPERTY, type),
eq(DEVICE_CUSTOMER_ID_PROPERTY, customerId),
eq(DEVICE_TENANT_ID_PROPERTY, tenantId)),
pageLink);
log.trace("Found devices [{}] by tenantId [{}], customerId [{}], type [{}] and pageLink [{}]", deviceEntities, tenantId, customerId, type, pageLink);
return deviceEntities;
}
@Override
public ListenableFuture<List<DeviceEntity>> findDevicesByTenantIdCustomerIdAndIdsAsync(UUID tenantId, UUID customerId, List<UUID> deviceIds) {
log.debug("Try to find devices by tenantId [{}], customerId [{}] and device Ids [{}]", tenantId, customerId, deviceIds);
@ -105,4 +136,24 @@ public class DeviceDaoImpl extends AbstractSearchTextDao<DeviceEntity> implement
return Optional.ofNullable(findOneByStatement(query));
}
@Override
public ListenableFuture<List<TenantDeviceTypeEntity>> findTenantDeviceTypesAsync() {
Select statement = select().distinct().column(DEVICE_TYPE_PROPERTY).column(DEVICE_TENANT_ID_PROPERTY).from(DEVICE_TYPES_BY_TENANT_VIEW_NAME);
statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
ResultSetFuture resultSetFuture = getSession().executeAsync(statement);
ListenableFuture<List<TenantDeviceTypeEntity>> result = Futures.transform(resultSetFuture, new Function<ResultSet, List<TenantDeviceTypeEntity>>() {
@Nullable
@Override
public List<TenantDeviceTypeEntity> apply(@Nullable ResultSet resultSet) {
Result<TenantDeviceTypeEntity> result = cluster.getMapper(TenantDeviceTypeEntity.class).map(resultSet);
if (result != null) {
return result.all();
} else {
return Collections.emptyList();
}
}
});
return result;
}
}

7
dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.device;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.TenantDeviceType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
@ -44,16 +45,22 @@ public interface DeviceService {
TextPageData<Device> findDevicesByTenantId(TenantId tenantId, TextPageLink pageLink);
TextPageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink);
ListenableFuture<List<Device>> findDevicesByTenantIdAndIdsAsync(TenantId tenantId, List<DeviceId> deviceIds);
void deleteDevicesByTenantId(TenantId tenantId);
TextPageData<Device> findDevicesByTenantIdAndCustomerId(TenantId tenantId, CustomerId customerId, TextPageLink pageLink);
TextPageData<Device> findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink);
ListenableFuture<List<Device>> findDevicesByTenantIdCustomerIdAndIdsAsync(TenantId tenantId, CustomerId customerId, List<DeviceId> deviceIds);
void unassignCustomerDevices(TenantId tenantId, CustomerId customerId);
ListenableFuture<List<Device>> findDevicesByQuery(DeviceSearchQuery query);
ListenableFuture<List<TenantDeviceType>> findDeviceTypesByTenantId(TenantId tenantId);
}

59
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -26,6 +26,7 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.TenantDeviceType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
@ -36,10 +37,11 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.thingsboard.server.dao.model.DeviceEntity;
import org.thingsboard.server.dao.model.TenantDeviceTypeEntity;
import org.thingsboard.server.dao.model.TenantEntity;
import org.thingsboard.server.dao.relation.EntitySearchDirection;
import org.thingsboard.server.dao.service.DataValidator;
@ -47,9 +49,7 @@ import org.thingsboard.server.dao.service.PaginatedRemover;
import org.thingsboard.server.dao.tenant.TenantDao;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;
import static org.thingsboard.server.dao.DaoUtil.*;
@ -58,7 +58,7 @@ import static org.thingsboard.server.dao.service.Validator.*;
@Service
@Slf4j
public class DeviceServiceImpl extends BaseEntityService implements DeviceService {
public class DeviceServiceImpl extends AbstractEntityService implements DeviceService {
@Autowired
private DeviceDao deviceDao;
@ -148,7 +148,18 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantId(tenantId.getId(), pageLink);
List<Device> devices = convertDataList(deviceEntities);
return new TextPageData<Device>(devices, pageLink);
return new TextPageData<>(devices, pageLink);
}
@Override
public TextPageData<Device> findDevicesByTenantIdAndType(TenantId tenantId, String type, TextPageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink);
validateId(tenantId, "Incorrect tenantId " + tenantId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndType(tenantId.getId(), type, pageLink);
List<Device> devices = convertDataList(deviceEntities);
return new TextPageData<>(devices, pageLink);
}
@Override
@ -176,7 +187,19 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndCustomerId(tenantId.getId(), customerId.getId(), pageLink);
List<Device> devices = convertDataList(deviceEntities);
return new TextPageData<Device>(devices, pageLink);
return new TextPageData<>(devices, pageLink);
}
@Override
public TextPageData<Device> findDevicesByTenantIdAndCustomerIdAndType(TenantId tenantId, CustomerId customerId, String type, TextPageLink pageLink) {
log.trace("Executing findDevicesByTenantIdAndCustomerIdAndType, tenantId [{}], customerId [{}], type [{}], pageLink [{}]", tenantId, customerId, type, pageLink);
validateId(tenantId, "Incorrect tenantId " + tenantId);
validateId(customerId, "Incorrect customerId " + customerId);
validateString(type, "Incorrect type " + type);
validatePageLink(pageLink, "Incorrect page link " + pageLink);
List<DeviceEntity> deviceEntities = deviceDao.findDevicesByTenantIdAndCustomerIdAndType(tenantId.getId(), customerId.getId(), type, pageLink);
List<Device> devices = convertDataList(deviceEntities);
return new TextPageData<>(devices, pageLink);
}
@Override
@ -224,6 +247,25 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
return devices;
}
@Override
public ListenableFuture<List<TenantDeviceType>> findDeviceTypesByTenantId(TenantId tenantId) {
log.trace("Executing findDeviceTypesByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, "Incorrect tenantId " + tenantId);
ListenableFuture<List<TenantDeviceTypeEntity>> tenantDeviceTypeEntities = deviceDao.findTenantDeviceTypesAsync();
ListenableFuture<List<TenantDeviceType>> tenantDeviceTypes = Futures.transform(tenantDeviceTypeEntities,
(Function<List<TenantDeviceTypeEntity>, List<TenantDeviceType>>) deviceTypeEntities -> {
List<TenantDeviceType> deviceTypes = new ArrayList<>();
for (TenantDeviceTypeEntity deviceTypeEntity : deviceTypeEntities) {
if (deviceTypeEntity.getTenantId().equals(tenantId.getId())) {
deviceTypes.add(deviceTypeEntity.toTenantDeviceType());
}
}
deviceTypes.sort((TenantDeviceType o1, TenantDeviceType o2) -> o1.getType().compareTo(o2.getType()));
return deviceTypes;
});
return tenantDeviceTypes;
}
private DataValidator<Device> deviceValidator =
new DataValidator<Device>() {
@ -249,6 +291,9 @@ public class DeviceServiceImpl extends BaseEntityService implements DeviceServic
@Override
protected void validateDataImpl(Device device) {
if (StringUtils.isEmpty(device.getType())) {
throw new DataValidationException("Device type should be specified!");
}
if (StringUtils.isEmpty(device.getName())) {
throw new DataValidationException("Device name should be specified!");
}

44
ui/src/app/dashboard/aliases-device-select.scss → dao/src/main/java/org/thingsboard/server/dao/entity/AbstractEntityService.java

@ -14,33 +14,23 @@
* limitations under the License.
*/
.md-panel {
&.tb-aliases-device-select-panel {
position: absolute;
}
}
package org.thingsboard.server.dao.entity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.dao.relation.RelationService;
@Slf4j
public abstract class AbstractEntityService {
@Autowired
protected RelationService relationService;
protected void deleteEntityRelations(EntityId entityId) {
log.trace("Executing deleteEntityRelations [{}]", entityId);
relationService.deleteEntityRelations(entityId);
}
.tb-aliases-device-select-panel {
max-height: 150px;
@media (min-height: 350px) {
max-height: 250px;
}
min-width: 300px;
background: white;
border-radius: 4px;
box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2),
0 13px 19px 2px rgba(0, 0, 0, 0.14),
0 5px 24px 4px rgba(0, 0, 0, 0.12);
overflow-x: hidden;
overflow-y: auto;
md-content {
background-color: #fff;
}
}
.tb-aliases-device-select {
span {
pointer-events: all;
cursor: pointer;
}
}

93
dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java

@ -15,23 +15,102 @@
*/
package org.thingsboard.server.dao.entity;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.dao.relation.RelationService;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.alarm.AlarmId;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.plugin.PluginService;
import org.thingsboard.server.dao.rule.RuleService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.user.UserService;
/**
* Created by ashvayka on 04.05.17.
*/
@Service
@Slf4j
public class BaseEntityService {
public class BaseEntityService extends AbstractEntityService implements EntityService {
@Autowired
protected RelationService relationService;
private AssetService assetService;
protected void deleteEntityRelations(EntityId entityId) {
log.trace("Executing deleteEntityRelations [{}]", entityId);
relationService.deleteEntityRelations(entityId);
@Autowired
private DeviceService deviceService;
@Autowired
private RuleService ruleService;
@Autowired
private PluginService pluginService;
@Autowired
private TenantService tenantService;
@Autowired
private CustomerService customerService;
@Autowired
private UserService userService;
@Autowired
private DashboardService dashboardService;
@Autowired
private AlarmService alarmService;
@Override
public void deleteEntityRelations(EntityId entityId) {
super.deleteEntityRelations(entityId);
}
@Override
public ListenableFuture<String> fetchEntityNameAsync(EntityId entityId) {
log.trace("Executing fetchEntityNameAsync [{}]", entityId);
ListenableFuture<String> entityName;
ListenableFuture<? extends HasName> hasName;
switch (entityId.getEntityType()) {
case ASSET:
hasName = assetService.findAssetByIdAsync(new AssetId(entityId.getId()));
break;
case DEVICE:
hasName = deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
break;
case RULE:
hasName = ruleService.findRuleByIdAsync(new RuleId(entityId.getId()));
break;
case PLUGIN:
hasName = pluginService.findPluginByIdAsync(new PluginId(entityId.getId()));
break;
case TENANT:
hasName = tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
break;
case CUSTOMER:
hasName = customerService.findCustomerByIdAsync(new CustomerId(entityId.getId()));
break;
case USER:
hasName = userService.findUserByIdAsync(new UserId(entityId.getId()));
break;
case DASHBOARD:
hasName = dashboardService.findDashboardInfoByIdAsync(new DashboardId(entityId.getId()));
break;
case ALARM:
hasName = alarmService.findAlarmByIdAsync(new AlarmId(entityId.getId()));
break;
default:
throw new IllegalStateException("Not Implemented!");
}
entityName = Futures.transform(hasName, (Function<HasName, String>) hasName1 -> hasName1.getName() );
return entityName;
}
}

43
ui/src/app/components/device-filter.scss → dao/src/main/java/org/thingsboard/server/dao/entity/EntityService.java

@ -13,33 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.tb-device-filter {
#device_list_chips {
.md-chips {
padding-bottom: 1px;
}
}
.device-name-filter-input {
margin-top: 10px;
margin-bottom: 0px;
.md-errors-spacer {
min-height: 0px;
}
}
.tb-filter-switch {
padding-left: 10px;
.filter-switch {
margin: 0;
}
.filter-label {
margin: 5px 0;
}
}
.tb-error-messages {
margin-top: -11px;
height: 35px;
.tb-error-message {
padding-left: 1px;
}
}
}
package org.thingsboard.server.dao.entity;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EntityId;
public interface EntityService {
ListenableFuture<String> fetchEntityNameAsync(EntityId entityId);
void deleteEntityRelations(EntityId entityId);
}

7
dao/src/main/java/org/thingsboard/server/dao/model/AssetEntity.java

@ -49,12 +49,13 @@ public final class AssetEntity implements SearchTextEntity<Asset> {
@Column(name = ASSET_CUSTOMER_ID_PROPERTY)
private UUID customerId;
@Column(name = ASSET_NAME_PROPERTY)
private String name;
@PartitionKey(value = 3)
@Column(name = ASSET_TYPE_PROPERTY)
private String type;
@Column(name = ASSET_NAME_PROPERTY)
private String name;
@Column(name = SEARCH_TEXT_PROPERTY)
private String searchText;

7
dao/src/main/java/org/thingsboard/server/dao/model/DeviceEntity.java

@ -49,12 +49,13 @@ public final class DeviceEntity implements SearchTextEntity<Device> {
@Column(name = DEVICE_CUSTOMER_ID_PROPERTY)
private UUID customerId;
@Column(name = DEVICE_NAME_PROPERTY)
private String name;
@PartitionKey(value = 3)
@Column(name = DEVICE_TYPE_PROPERTY)
private String type;
@Column(name = DEVICE_NAME_PROPERTY)
private String name;
@Column(name = SEARCH_TEXT_PROPERTY)
private String searchText;

6
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -124,8 +124,11 @@ public class ModelConstants {
public static final String DEVICE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text";
public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text";
public static final String DEVICE_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_and_search_text";
public static final String DEVICE_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_customer_by_type_and_search_text";
public static final String DEVICE_BY_TENANT_AND_NAME_VIEW_NAME = "device_by_tenant_and_name";
public static final String DEVICE_TYPES_BY_TENANT_VIEW_NAME = "device_types_by_tenant";
/**
* Cassandra asset constants.
@ -138,8 +141,11 @@ public class ModelConstants {
public static final String ASSET_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_and_search_text";
public static final String ASSET_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_tenant_by_type_and_search_text";
public static final String ASSET_BY_CUSTOMER_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_and_search_text";
public static final String ASSET_BY_CUSTOMER_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "asset_by_customer_by_type_and_search_text";
public static final String ASSET_BY_TENANT_AND_NAME_VIEW_NAME = "asset_by_tenant_and_name";
public static final String ASSET_TYPES_BY_TENANT_VIEW_NAME = "asset_types_by_tenant";
/**
* Cassandra entity relation constants.

107
dao/src/main/java/org/thingsboard/server/dao/model/TenantAssetTypeEntity.java

@ -0,0 +1,107 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.model;
import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table;
import com.datastax.driver.mapping.annotations.Transient;
import org.thingsboard.server.common.data.asset.TenantAssetType;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.*;
@Table(name = ASSET_TYPES_BY_TENANT_VIEW_NAME)
public class TenantAssetTypeEntity {
@Transient
private static final long serialVersionUID = -1268181161886910152L;
@PartitionKey(value = 0)
@Column(name = ASSET_TYPE_PROPERTY)
private String type;
@PartitionKey(value = 1)
@Column(name = ASSET_TENANT_ID_PROPERTY)
private UUID tenantId;
public TenantAssetTypeEntity() {
super();
}
public TenantAssetTypeEntity(TenantAssetType tenantAssetType) {
this.type = tenantAssetType.getType();
if (tenantAssetType.getTenantId() != null) {
this.tenantId = tenantAssetType.getTenantId().getId();
}
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TenantAssetTypeEntity that = (TenantAssetTypeEntity) o;
if (type != null ? !type.equals(that.type) : that.type != null) return false;
return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("TenantAssetTypeEntity{");
sb.append("type='").append(type).append('\'');
sb.append(", tenantId=").append(tenantId);
sb.append('}');
return sb.toString();
}
public TenantAssetType toTenantAssetType() {
TenantAssetType tenantAssetType = new TenantAssetType();
tenantAssetType.setType(type);
if (tenantId != null) {
tenantAssetType.setTenantId(new TenantId(tenantId));
}
return tenantAssetType;
}
}

107
dao/src/main/java/org/thingsboard/server/dao/model/TenantDeviceTypeEntity.java

@ -0,0 +1,107 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.model;
import com.datastax.driver.mapping.annotations.Column;
import com.datastax.driver.mapping.annotations.PartitionKey;
import com.datastax.driver.mapping.annotations.Table;
import com.datastax.driver.mapping.annotations.Transient;
import org.thingsboard.server.common.data.TenantDeviceType;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.*;
@Table(name = DEVICE_TYPES_BY_TENANT_VIEW_NAME)
public class TenantDeviceTypeEntity {
@Transient
private static final long serialVersionUID = -1268181166886910152L;
@PartitionKey(value = 0)
@Column(name = DEVICE_TYPE_PROPERTY)
private String type;
@PartitionKey(value = 1)
@Column(name = DEVICE_TENANT_ID_PROPERTY)
private UUID tenantId;
public TenantDeviceTypeEntity() {
super();
}
public TenantDeviceTypeEntity(TenantDeviceType tenantDeviceType) {
this.type = tenantDeviceType.getType();
if (tenantDeviceType.getTenantId() != null) {
this.tenantId = tenantDeviceType.getTenantId().getId();
}
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public UUID getTenantId() {
return tenantId;
}
public void setTenantId(UUID tenantId) {
this.tenantId = tenantId;
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0);
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TenantDeviceTypeEntity that = (TenantDeviceTypeEntity) o;
if (type != null ? !type.equals(that.type) : that.type != null) return false;
return tenantId != null ? tenantId.equals(that.tenantId) : that.tenantId == null;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("TenantDeviceTypeEntity{");
sb.append("type='").append(type).append('\'');
sb.append(", tenantId=").append(tenantId);
sb.append('}');
return sb.toString();
}
public TenantDeviceType toTenantDeviceType() {
TenantDeviceType tenantDeviceType = new TenantDeviceType();
tenantDeviceType.setType(type);
if (tenantId != null) {
tenantDeviceType.setTenantId(new TenantId(tenantId));
}
return tenantDeviceType;
}
}

6
dao/src/main/java/org/thingsboard/server/dao/plugin/BasePluginService.java

@ -22,7 +22,6 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.PluginId;
import org.thingsboard.server.common.data.id.RuleId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
@ -30,9 +29,8 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.common.data.rule.RuleMetaData;
import org.thingsboard.server.dao.component.ComponentDescriptorService;
import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DatabaseException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
@ -55,7 +53,7 @@ import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@Slf4j
public class BasePluginService extends BaseEntityService implements PluginService {
public class BasePluginService extends AbstractEntityService implements PluginService {
//TODO: move to a better place.
public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);

43
dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java

@ -23,9 +23,24 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.plugin.PluginService;
import org.thingsboard.server.dao.rule.RuleService;
import org.thingsboard.server.dao.tenant.TenantService;
import javax.annotation.Nullable;
import java.util.*;
@ -41,6 +56,9 @@ public class BaseRelationService implements RelationService {
@Autowired
private RelationDao relationDao;
@Autowired
private EntityService entityService;
@Override
public ListenableFuture<Boolean> checkRelation(EntityId from, EntityId to, String relationType) {
log.trace("Executing checkRelation [{}][{}][{}]", from, to, relationType);
@ -99,6 +117,31 @@ public class BaseRelationService implements RelationService {
return relationDao.findAllByFrom(from);
}
@Override
public ListenableFuture<List<EntityRelationInfo>> findInfoByFrom(EntityId from) {
log.trace("Executing findInfoByFrom [{}]", from);
validate(from);
ListenableFuture<List<EntityRelation>> relations = relationDao.findAllByFrom(from);
ListenableFuture<List<EntityRelationInfo>> relationsInfo = Futures.transform(relations,
(AsyncFunction<List<EntityRelation>, List<EntityRelationInfo>>) relations1 -> {
List<ListenableFuture<EntityRelationInfo>> futures = new ArrayList<>();
relations1.stream().forEach(relation -> futures.add(fetchRelationInfoAsync(relation)));
return Futures.successfulAsList(futures);
});
return relationsInfo;
}
private ListenableFuture<EntityRelationInfo> fetchRelationInfoAsync(EntityRelation relation) {
ListenableFuture<String> entityName = entityService.fetchEntityNameAsync(relation.getTo());
ListenableFuture<EntityRelationInfo> entityRelationInfo =
Futures.transform(entityName, (Function<String, EntityRelationInfo>) entityName1 -> {
EntityRelationInfo entityRelationInfo1 = new EntityRelationInfo(relation);
entityRelationInfo1.setToName(entityName1);
return entityRelationInfo1;
});
return entityRelationInfo;
}
@Override
public ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType) {
log.trace("Executing findByFromAndType [{}][{}]", from, relationType);

3
dao/src/main/java/org/thingsboard/server/dao/relation/RelationService.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.relation;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import java.util.List;
@ -38,6 +39,8 @@ public interface RelationService {
ListenableFuture<List<EntityRelation>> findByFrom(EntityId from);
ListenableFuture<List<EntityRelationInfo>> findInfoByFrom(EntityId from);
ListenableFuture<List<EntityRelation>> findByFromAndType(EntityId from, String relationType);
ListenableFuture<List<EntityRelation>> findByTo(EntityId to);

6
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleService.java

@ -23,7 +23,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.id.RuleId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
@ -34,11 +33,10 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.common.data.rule.RuleMetaData;
import org.thingsboard.server.dao.component.ComponentDescriptorService;
import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.DatabaseException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.AssetEntity;
import org.thingsboard.server.dao.model.RuleMetaDataEntity;
import org.thingsboard.server.dao.plugin.PluginService;
import org.thingsboard.server.dao.service.DataValidator;
@ -58,7 +56,7 @@ import static org.thingsboard.server.dao.service.Validator.validatePageLink;
@Service
@Slf4j
public class BaseRuleService extends BaseEntityService implements RuleService {
public class BaseRuleService extends AbstractEntityService implements RuleService {
private final TenantId systemTenantId = new TenantId(NULL_UUID);

6
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java

@ -26,7 +26,6 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
@ -34,9 +33,8 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.CustomerEntity;
import org.thingsboard.server.dao.model.TenantEntity;
import org.thingsboard.server.dao.plugin.PluginService;
import org.thingsboard.server.dao.rule.RuleService;
@ -50,7 +48,7 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService;
@Service
@Slf4j
public class TenantServiceImpl extends BaseEntityService implements TenantService {
public class TenantServiceImpl extends AbstractEntityService implements TenantService {
private static final String DEFAULT_TENANT_REGION = "Global";

3
dao/src/main/java/org/thingsboard/server/dao/user/UserService.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.dao.user;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
@ -27,6 +28,8 @@ public interface UserService {
public User findUserById(UserId userId);
public ListenableFuture<User> findUserByIdAsync(UserId userId);
public User findUserByEmail(String email);
public User saveUser(User user);

15
dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

@ -23,6 +23,9 @@ import static org.thingsboard.server.dao.service.Validator.validateString;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
@ -35,7 +38,7 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.entity.BaseEntityService;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.*;
@ -47,7 +50,7 @@ import org.springframework.stereotype.Service;
@Service
@Slf4j
public class UserServiceImpl extends BaseEntityService implements UserService {
public class UserServiceImpl extends AbstractEntityService implements UserService {
@Autowired
private UserDao userDao;
@ -77,6 +80,14 @@ public class UserServiceImpl extends BaseEntityService implements UserService {
return getData(userEntity);
}
@Override
public ListenableFuture<User> findUserByIdAsync(UserId userId) {
log.trace("Executing findUserByIdAsync [{}]", userId);
validateId(userId, "Incorrect userId " + userId);
ListenableFuture<UserEntity> userEntity = userDao.findByIdAsync(userId.getId());
return Futures.transform(userEntity, (Function<? super UserEntity, ? extends User>) input -> getData(input));
}
@Override
public User saveUser(User user) {
log.trace("Executing saveUser [{}]", user);

21
dao/src/main/resources/demo-data.cql

@ -149,66 +149,73 @@ VALUES (
/** Demo device **/
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
VALUES (
minTimeuuid ( '2016-11-01 01:02:05+0000' ),
minTimeuuid ( '2016-11-01 01:02:01+0000' ),
minTimeuuid ( '2016-11-01 01:02:03+0000' ),
'default',
'Test Device A1',
'test device a1'
);
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
VALUES (
minTimeuuid ( '2016-11-01 01:02:05+0001' ),
minTimeuuid ( '2016-11-01 01:02:01+0000' ),
minTimeuuid ( '2016-11-01 01:02:03+0000' ),
'default',
'Test Device A2',
'test device a2'
);
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
VALUES (
minTimeuuid ( '2016-11-01 01:02:05+0002' ),
minTimeuuid ( '2016-11-01 01:02:01+0000' ),
minTimeuuid ( '2016-11-01 01:02:03+0000' ),
'default',
'Test Device A3',
'test device a3'
);
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
VALUES (
minTimeuuid ( '2016-11-01 01:02:05+0003' ),
minTimeuuid ( '2016-11-01 01:02:01+0000' ),
minTimeuuid ( '2016-11-01 01:02:03+0001' ),
'default',
'Test Device B1',
'test device b1'
);
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text)
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text)
VALUES (
minTimeuuid ( '2016-11-01 01:02:05+0004' ),
minTimeuuid ( '2016-11-01 01:02:01+0000' ),
minTimeuuid ( '2016-11-01 01:02:03+0002' ),
'default',
'Test Device C1',
'test device c1'
);
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text, additional_info)
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text, additional_info)
VALUES (
c8f1a6f0-b993-11e6-8a04-9ff4e1b7933c,
minTimeuuid ( '2016-11-01 01:02:01+0000' ),
minTimeuuid ( 0 ),
'default',
'DHT11 Demo Device',
'dht11 demo device',
'{"description":"Demo device that is used in sample applications that upload data from DHT11 temperature and humidity sensor"}'
);
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, name, search_text, additional_info)
INSERT INTO thingsboard.device ( id, tenant_id, customer_id, type, name, search_text, additional_info)
VALUES (
c8f1a6f0-b993-11e6-8a04-9ff4e1b7933d,
minTimeuuid ( '2016-11-01 01:02:01+0000' ),
minTimeuuid ( 0 ),
'default',
'Raspberry Pi Demo Device',
'raspberry pi demo device',
'{"description":"Demo device that is used in Raspberry Pi GPIO control sample application"}'

145
dao/src/main/resources/schema.cql

@ -152,36 +152,57 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.customer_by_tenant_and_search
WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
CREATE TABLE IF NOT EXISTS thingsboard.device (
id timeuuid,
tenant_id timeuuid,
customer_id timeuuid,
name text,
type text,
search_text text,
additional_info text,
PRIMARY KEY (id, tenant_id, customer_id)
id timeuuid,
tenant_id timeuuid,
customer_id timeuuid,
name text,
type text,
search_text text,
additional_info text,
PRIMARY KEY (id, tenant_id, customer_id, type)
);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_name AS
SELECT *
from thingsboard.device
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, name, id, customer_id)
WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
SELECT *
from thingsboard.device
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, name, id, customer_id, type)
WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_and_search_text AS
SELECT *
from thingsboard.device
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, search_text, id, customer_id)
WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
SELECT *
from thingsboard.device
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, search_text, id, customer_id, type)
WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_tenant_by_type_and_search_text AS
SELECT *
from thingsboard.device
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, type, search_text, id, customer_id)
WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_and_search_text AS
SELECT *
from thingsboard.device
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( customer_id, tenant_id, search_text, id )
WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
SELECT *
from thingsboard.device
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( customer_id, tenant_id, search_text, id, type )
WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_by_type_and_search_text AS
SELECT *
from thingsboard.device
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_types_by_tenant AS
SELECT *
from thingsboard.device
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( (type, tenant_id), id, customer_id)
WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC);
CREATE TABLE IF NOT EXISTS thingsboard.device_credentials (
id timeuuid PRIMARY KEY,
@ -203,38 +224,58 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_credentials_by_credent
WHERE credentials_id IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( credentials_id, id );
CREATE TABLE IF NOT EXISTS thingsboard.asset (
id timeuuid,
tenant_id timeuuid,
customer_id timeuuid,
name text,
type text,
search_text text,
additional_info text,
PRIMARY KEY (id, tenant_id, customer_id)
id timeuuid,
tenant_id timeuuid,
customer_id timeuuid,
name text,
type text,
search_text text,
additional_info text,
PRIMARY KEY (id, tenant_id, customer_id, type)
);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_name AS
SELECT *
from thingsboard.asset
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, name, id, customer_id)
WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
SELECT *
from thingsboard.asset
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND name IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, name, id, customer_id, type)
WITH CLUSTERING ORDER BY ( name ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_and_search_text AS
SELECT *
from thingsboard.asset
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, search_text, id, customer_id)
WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
SELECT *
from thingsboard.asset
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, search_text, id, customer_id, type)
WITH CLUSTERING ORDER BY ( search_text ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_tenant_by_type_and_search_text AS
SELECT *
from thingsboard.asset
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( tenant_id, type, search_text, id, customer_id)
WITH CLUSTERING ORDER BY ( type ASC, search_text ASC, id DESC, customer_id DESC);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_and_search_text AS
SELECT *
from thingsboard.asset
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( customer_id, tenant_id, search_text, id )
WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
SELECT *
from thingsboard.asset
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( customer_id, tenant_id, search_text, id, type )
WITH CLUSTERING ORDER BY ( tenant_id DESC, search_text ASC, id DESC );
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_by_type_and_search_text AS
SELECT *
from thingsboard.asset
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_types_by_tenant AS
SELECT *
from thingsboard.asset
WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL
PRIMARY KEY ( (type, tenant_id), id, customer_id)
WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC);
CREATE TABLE IF NOT EXISTS thingsboard.relation (
from_id timeuuid,
@ -247,11 +288,11 @@ CREATE TABLE IF NOT EXISTS thingsboard.relation (
);
CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.reverse_relation AS
SELECT *
from thingsboard.relation
WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL
PRIMARY KEY ((to_id, to_type), relation_type, from_id, from_type)
WITH CLUSTERING ORDER BY ( relation_type ASC, from_id ASC, from_type ASC);
SELECT *
from thingsboard.relation
WHERE from_id IS NOT NULL AND from_type IS NOT NULL AND relation_type IS NOT NULL AND to_id IS NOT NULL AND to_type IS NOT NULL
PRIMARY KEY ((to_id, to_type), relation_type, from_id, from_type)
WITH CLUSTERING ORDER BY ( relation_type ASC, from_id ASC, from_type ASC);
CREATE TABLE IF NOT EXISTS thingsboard.widgets_bundle (
id timeuuid,

4
dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java

@ -40,6 +40,7 @@ import org.thingsboard.server.common.data.plugin.ComponentScope;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.plugin.PluginMetaData;
import org.thingsboard.server.common.data.rule.RuleMetaData;
import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.dao.component.ComponentDescriptorService;
import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.dashboard.DashboardService;
@ -87,6 +88,9 @@ public abstract class AbstractServiceTest {
@Autowired
protected DeviceService deviceService;
@Autowired
protected AssetService assetService;
@Autowired
protected DeviceCredentialsService deviceCredentialsService;

634
dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java

@ -0,0 +1,634 @@
/**
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.service;
import com.datastax.driver.core.utils.UUIDs;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.TenantAssetType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
public class BaseAssetServiceTest extends AbstractServiceTest {
private IdComparator<Asset> idComparator = new IdComparator<>();
private TenantId tenantId;
@Before
public void before() {
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = tenantService.saveTenant(tenant);
Assert.assertNotNull(savedTenant);
tenantId = savedTenant.getId();
}
@After
public void after() {
tenantService.deleteTenant(tenantId);
}
@Test
public void testSaveAsset() {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = assetService.saveAsset(asset);
Assert.assertNotNull(savedAsset);
Assert.assertNotNull(savedAsset.getId());
Assert.assertTrue(savedAsset.getCreatedTime() > 0);
Assert.assertEquals(asset.getTenantId(), savedAsset.getTenantId());
Assert.assertNotNull(savedAsset.getCustomerId());
Assert.assertEquals(NULL_UUID, savedAsset.getCustomerId().getId());
Assert.assertEquals(asset.getName(), savedAsset.getName());
savedAsset.setName("My new asset");
assetService.saveAsset(savedAsset);
Asset foundAsset = assetService.findAssetById(savedAsset.getId());
Assert.assertEquals(foundAsset.getName(), savedAsset.getName());
assetService.deleteAsset(savedAsset.getId());
}
@Test(expected = DataValidationException.class)
public void testSaveAssetWithEmptyName() {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setType("default");
assetService.saveAsset(asset);
}
@Test(expected = DataValidationException.class)
public void testSaveAssetWithEmptyTenant() {
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
assetService.saveAsset(asset);
}
@Test(expected = DataValidationException.class)
public void testSaveAssetWithInvalidTenant() {
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
asset.setTenantId(new TenantId(UUIDs.timeBased()));
assetService.saveAsset(asset);
}
@Test(expected = DataValidationException.class)
public void testAssignAssetToNonExistentCustomer() {
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
asset.setTenantId(tenantId);
asset = assetService.saveAsset(asset);
try {
assetService.assignAssetToCustomer(asset.getId(), new CustomerId(UUIDs.timeBased()));
} finally {
assetService.deleteAsset(asset.getId());
}
}
@Test(expected = DataValidationException.class)
public void testAssignAssetToCustomerFromDifferentTenant() {
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
asset.setTenantId(tenantId);
asset = assetService.saveAsset(asset);
Tenant tenant = new Tenant();
tenant.setTitle("Test different tenant");
tenant = tenantService.saveTenant(tenant);
Customer customer = new Customer();
customer.setTenantId(tenant.getId());
customer.setTitle("Test different customer");
customer = customerService.saveCustomer(customer);
try {
assetService.assignAssetToCustomer(asset.getId(), customer.getId());
} finally {
assetService.deleteAsset(asset.getId());
tenantService.deleteTenant(tenant.getId());
}
}
@Test
public void testFindAssetById() {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = assetService.saveAsset(asset);
Asset foundAsset = assetService.findAssetById(savedAsset.getId());
Assert.assertNotNull(foundAsset);
Assert.assertEquals(savedAsset, foundAsset);
assetService.deleteAsset(savedAsset.getId());
}
@Test
public void testFindAssetTypesByTenantId() throws Exception {
List<Asset> assets = new ArrayList<>();
try {
for (int i=0;i<3;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("My asset B"+i);
asset.setType("typeB");
assets.add(assetService.saveAsset(asset));
}
for (int i=0;i<7;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("My asset C"+i);
asset.setType("typeC");
assets.add(assetService.saveAsset(asset));
}
for (int i=0;i<9;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("My asset A"+i);
asset.setType("typeA");
assets.add(assetService.saveAsset(asset));
}
List<TenantAssetType> assetTypes = assetService.findAssetTypesByTenantId(tenantId).get();
Assert.assertNotNull(assetTypes);
Assert.assertEquals(3, assetTypes.size());
Assert.assertEquals("typeA", assetTypes.get(0).getType());
Assert.assertEquals("typeB", assetTypes.get(1).getType());
Assert.assertEquals("typeC", assetTypes.get(2).getType());
} finally {
assets.forEach((asset) -> { assetService.deleteAsset(asset.getId()); });
}
}
@Test
public void testDeleteAsset() {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("My asset");
asset.setType("default");
Asset savedAsset = assetService.saveAsset(asset);
Asset foundAsset = assetService.findAssetById(savedAsset.getId());
Assert.assertNotNull(foundAsset);
assetService.deleteAsset(savedAsset.getId());
foundAsset = assetService.findAssetById(savedAsset.getId());
Assert.assertNull(foundAsset);
}
@Test
public void testFindAssetsByTenantId() {
Tenant tenant = new Tenant();
tenant.setTitle("Test tenant");
tenant = tenantService.saveTenant(tenant);
TenantId tenantId = tenant.getId();
List<Asset> assets = new ArrayList<>();
for (int i=0;i<178;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("Asset"+i);
asset.setType("default");
assets.add(assetService.saveAsset(asset));
}
List<Asset> loadedAssets = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(23);
TextPageData<Asset> pageData = null;
do {
pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
loadedAssets.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assets, idComparator);
Collections.sort(loadedAssets, idComparator);
Assert.assertEquals(assets, loadedAssets);
assetService.deleteAssetsByTenantId(tenantId);
pageLink = new TextPageLink(33);
pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertTrue(pageData.getData().isEmpty());
tenantService.deleteTenant(tenantId);
}
@Test
public void testFindAssetsByTenantIdAndName() {
String title1 = "Asset title 1";
List<Asset> assetsTitle1 = new ArrayList<>();
for (int i=0;i<143;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
assetsTitle1.add(assetService.saveAsset(asset));
}
String title2 = "Asset title 2";
List<Asset> assetsTitle2 = new ArrayList<>();
for (int i=0;i<175;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
assetsTitle2.add(assetService.saveAsset(asset));
}
List<Asset> loadedAssetsTitle1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<Asset> pageData = null;
do {
pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
loadedAssetsTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsTitle1, idComparator);
Collections.sort(loadedAssetsTitle1, idComparator);
Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
List<Asset> loadedAssetsTitle2 = new ArrayList<>();
pageLink = new TextPageLink(4, title2);
do {
pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
loadedAssetsTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsTitle2, idComparator);
Collections.sort(loadedAssetsTitle2, idComparator);
Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
for (Asset asset : loadedAssetsTitle1) {
assetService.deleteAsset(asset.getId());
}
pageLink = new TextPageLink(4, title1);
pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Asset asset : loadedAssetsTitle2) {
assetService.deleteAsset(asset.getId());
}
pageLink = new TextPageLink(4, title2);
pageData = assetService.findAssetsByTenantId(tenantId, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindAssetsByTenantIdAndType() {
String title1 = "Asset title 1";
String type1 = "typeA";
List<Asset> assetsType1 = new ArrayList<>();
for (int i=0;i<143;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type1);
assetsType1.add(assetService.saveAsset(asset));
}
String title2 = "Asset title 2";
String type2 = "typeB";
List<Asset> assetsType2 = new ArrayList<>();
for (int i=0;i<175;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type2);
assetsType2.add(assetService.saveAsset(asset));
}
List<Asset> loadedAssetsType1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Asset> pageData = null;
do {
pageData = assetService.findAssetsByTenantIdAndType(tenantId, type1, pageLink);
loadedAssetsType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsType1, idComparator);
Collections.sort(loadedAssetsType1, idComparator);
Assert.assertEquals(assetsType1, loadedAssetsType1);
List<Asset> loadedAssetsType2 = new ArrayList<>();
pageLink = new TextPageLink(4);
do {
pageData = assetService.findAssetsByTenantIdAndType(tenantId, type2, pageLink);
loadedAssetsType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsType2, idComparator);
Collections.sort(loadedAssetsType2, idComparator);
Assert.assertEquals(assetsType2, loadedAssetsType2);
for (Asset asset : loadedAssetsType1) {
assetService.deleteAsset(asset.getId());
}
pageLink = new TextPageLink(4);
pageData = assetService.findAssetsByTenantIdAndType(tenantId, type1, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Asset asset : loadedAssetsType2) {
assetService.deleteAsset(asset.getId());
}
pageLink = new TextPageLink(4);
pageData = assetService.findAssetsByTenantIdAndType(tenantId, type2, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindAssetsByTenantIdAndCustomerId() {
Tenant tenant = new Tenant();
tenant.setTitle("Test tenant");
tenant = tenantService.saveTenant(tenant);
TenantId tenantId = tenant.getId();
Customer customer = new Customer();
customer.setTitle("Test customer");
customer.setTenantId(tenantId);
customer = customerService.saveCustomer(customer);
CustomerId customerId = customer.getId();
List<Asset> assets = new ArrayList<>();
for (int i=0;i<278;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
asset.setName("Asset"+i);
asset.setType("default");
asset = assetService.saveAsset(asset);
assets.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
}
List<Asset> loadedAssets = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(23);
TextPageData<Asset> pageData = null;
do {
pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
loadedAssets.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assets, idComparator);
Collections.sort(loadedAssets, idComparator);
Assert.assertEquals(assets, loadedAssets);
assetService.unassignCustomerAssets(tenantId, customerId);
pageLink = new TextPageLink(33);
pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertTrue(pageData.getData().isEmpty());
tenantService.deleteTenant(tenantId);
}
@Test
public void testFindAssetsByTenantIdCustomerIdAndName() {
Customer customer = new Customer();
customer.setTitle("Test customer");
customer.setTenantId(tenantId);
customer = customerService.saveCustomer(customer);
CustomerId customerId = customer.getId();
String title1 = "Asset title 1";
List<Asset> assetsTitle1 = new ArrayList<>();
for (int i=0;i<175;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
asset = assetService.saveAsset(asset);
assetsTitle1.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
}
String title2 = "Asset title 2";
List<Asset> assetsTitle2 = new ArrayList<>();
for (int i=0;i<143;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType("default");
asset = assetService.saveAsset(asset);
assetsTitle2.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
}
List<Asset> loadedAssetsTitle1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15, title1);
TextPageData<Asset> pageData = null;
do {
pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
loadedAssetsTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsTitle1, idComparator);
Collections.sort(loadedAssetsTitle1, idComparator);
Assert.assertEquals(assetsTitle1, loadedAssetsTitle1);
List<Asset> loadedAssetsTitle2 = new ArrayList<>();
pageLink = new TextPageLink(4, title2);
do {
pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
loadedAssetsTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsTitle2, idComparator);
Collections.sort(loadedAssetsTitle2, idComparator);
Assert.assertEquals(assetsTitle2, loadedAssetsTitle2);
for (Asset asset : loadedAssetsTitle1) {
assetService.deleteAsset(asset.getId());
}
pageLink = new TextPageLink(4, title1);
pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Asset asset : loadedAssetsTitle2) {
assetService.deleteAsset(asset.getId());
}
pageLink = new TextPageLink(4, title2);
pageData = assetService.findAssetsByTenantIdAndCustomerId(tenantId, customerId, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
customerService.deleteCustomer(customerId);
}
@Test
public void testFindAssetsByTenantIdCustomerIdAndType() {
Customer customer = new Customer();
customer.setTitle("Test customer");
customer.setTenantId(tenantId);
customer = customerService.saveCustomer(customer);
CustomerId customerId = customer.getId();
String title1 = "Asset title 1";
String type1 = "typeC";
List<Asset> assetsType1 = new ArrayList<>();
for (int i=0;i<175;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type1);
asset = assetService.saveAsset(asset);
assetsType1.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
}
String title2 = "Asset title 2";
String type2 = "typeD";
List<Asset> assetsType2 = new ArrayList<>();
for (int i=0;i<143;i++) {
Asset asset = new Asset();
asset.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
asset.setName(name);
asset.setType(type2);
asset = assetService.saveAsset(asset);
assetsType2.add(assetService.assignAssetToCustomer(asset.getId(), customerId));
}
List<Asset> loadedAssetsType1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Asset> pageData = null;
do {
pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
loadedAssetsType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsType1, idComparator);
Collections.sort(loadedAssetsType1, idComparator);
Assert.assertEquals(assetsType1, loadedAssetsType1);
List<Asset> loadedAssetsType2 = new ArrayList<>();
pageLink = new TextPageLink(4);
do {
pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
loadedAssetsType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(assetsType2, idComparator);
Collections.sort(loadedAssetsType2, idComparator);
Assert.assertEquals(assetsType2, loadedAssetsType2);
for (Asset asset : loadedAssetsType1) {
assetService.deleteAsset(asset.getId());
}
pageLink = new TextPageLink(4);
pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Asset asset : loadedAssetsType2) {
assetService.deleteAsset(asset.getId());
}
pageLink = new TextPageLink(4);
pageData = assetService.findAssetsByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
customerService.deleteCustomer(customerId);
}
}

9
dao/src/test/java/org/thingsboard/server/dao/service/DeviceCredentialsServiceImplTest.java

@ -58,6 +58,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
public void testSaveDeviceCredentialsWithEmptyDevice() {
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setTenantId(tenantId);
device = deviceService.saveDevice(device);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@ -73,6 +74,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
public void testSaveDeviceCredentialsWithEmptyCredentialsType() {
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setTenantId(tenantId);
device = deviceService.saveDevice(device);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@ -88,6 +90,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
public void testSaveDeviceCredentialsWithEmptyCredentialsId() {
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setTenantId(tenantId);
device = deviceService.saveDevice(device);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@ -103,6 +106,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
public void testSaveNonExistentDeviceCredentials() {
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setTenantId(tenantId);
device = deviceService.saveDevice(device);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@ -122,6 +126,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
public void testSaveDeviceCredentialsWithNonExistentDevice() {
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setTenantId(tenantId);
device = deviceService.saveDevice(device);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@ -137,6 +142,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
public void testSaveDeviceCredentialsWithInvalidCredemtialsIdLength() {
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setTenantId(tenantId);
device = deviceService.saveDevice(device);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getId());
@ -153,6 +159,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device");
device.setType("default");
Device savedDevice = deviceService.saveDevice(device);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId());
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
@ -166,6 +173,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device");
device.setType("default");
Device savedDevice = deviceService.saveDevice(device);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId());
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
@ -181,6 +189,7 @@ public class DeviceCredentialsServiceImplTest extends AbstractServiceTest {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device");
device.setType("default");
Device savedDevice = deviceService.saveDevice(device);
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(savedDevice.getId());
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());

223
dao/src/test/java/org/thingsboard/server/dao/service/DeviceServiceImplTest.java

@ -24,6 +24,7 @@ import org.junit.Test;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantDeviceType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceCredentialsId;
import org.thingsboard.server.common.data.id.DeviceId;
@ -37,6 +38,7 @@ import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@ -65,6 +67,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device");
device.setType("default");
Device savedDevice = deviceService.saveDevice(device);
Assert.assertNotNull(savedDevice);
@ -95,6 +98,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
@Test(expected = DataValidationException.class)
public void testSaveDeviceWithEmptyName() {
Device device = new Device();
device.setType("default");
device.setTenantId(tenantId);
deviceService.saveDevice(device);
}
@ -103,6 +107,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
public void testSaveDeviceWithEmptyTenant() {
Device device = new Device();
device.setName("My device");
device.setType("default");
deviceService.saveDevice(device);
}
@ -110,6 +115,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
public void testSaveDeviceWithInvalidTenant() {
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setTenantId(new TenantId(UUIDs.timeBased()));
deviceService.saveDevice(device);
}
@ -118,6 +124,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
public void testAssignDeviceToNonExistentCustomer() {
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setTenantId(tenantId);
device = deviceService.saveDevice(device);
try {
@ -131,6 +138,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
public void testAssignDeviceToCustomerFromDifferentTenant() {
Device device = new Device();
device.setName("My device");
device.setType("default");
device.setTenantId(tenantId);
device = deviceService.saveDevice(device);
Tenant tenant = new Tenant();
@ -153,18 +161,56 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device");
device.setType("default");
Device savedDevice = deviceService.saveDevice(device);
Device foundDevice = deviceService.findDeviceById(savedDevice.getId());
Assert.assertNotNull(foundDevice);
Assert.assertEquals(savedDevice, foundDevice);
deviceService.deleteDevice(savedDevice.getId());
}
@Test
public void testFindDeviceTypesByTenantId() throws Exception {
List<Device> devices = new ArrayList<>();
try {
for (int i=0;i<3;i++) {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device B"+i);
device.setType("typeB");
devices.add(deviceService.saveDevice(device));
}
for (int i=0;i<7;i++) {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device C"+i);
device.setType("typeC");
devices.add(deviceService.saveDevice(device));
}
for (int i=0;i<9;i++) {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device A"+i);
device.setType("typeA");
devices.add(deviceService.saveDevice(device));
}
List<TenantDeviceType> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId).get();
Assert.assertNotNull(deviceTypes);
Assert.assertEquals(3, deviceTypes.size());
Assert.assertEquals("typeA", deviceTypes.get(0).getType());
Assert.assertEquals("typeB", deviceTypes.get(1).getType());
Assert.assertEquals("typeC", deviceTypes.get(2).getType());
} finally {
devices.forEach((device) -> { deviceService.deleteDevice(device.getId()); });
}
}
@Test
public void testDeleteDevice() {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("My device");
device.setType("default");
Device savedDevice = deviceService.saveDevice(device);
Device foundDevice = deviceService.findDeviceById(savedDevice.getId());
Assert.assertNotNull(foundDevice);
@ -188,6 +234,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("Device"+i);
device.setType("default");
devices.add(deviceService.saveDevice(device));
}
@ -216,7 +263,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
tenantService.deleteTenant(tenantId);
}
@Test
public void testFindDevicesByTenantIdAndName() {
String title1 = "Device title 1";
@ -228,6 +275,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
devicesTitle1.add(deviceService.saveDevice(device));
}
String title2 = "Device title 2";
@ -239,6 +287,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
devicesTitle2.add(deviceService.saveDevice(device));
}
@ -291,6 +340,85 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindDevicesByTenantIdAndType() {
String title1 = "Device title 1";
String type1 = "typeA";
List<Device> devicesType1 = new ArrayList<>();
for (int i=0;i<143;i++) {
Device device = new Device();
device.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type1);
devicesType1.add(deviceService.saveDevice(device));
}
String title2 = "Device title 2";
String type2 = "typeB";
List<Device> devicesType2 = new ArrayList<>();
for (int i=0;i<175;i++) {
Device device = new Device();
device.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type2);
devicesType2.add(deviceService.saveDevice(device));
}
List<Device> loadedDevicesType1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Device> pageData = null;
do {
pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type1, pageLink);
loadedDevicesType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType1, idComparator);
Collections.sort(loadedDevicesType1, idComparator);
Assert.assertEquals(devicesType1, loadedDevicesType1);
List<Device> loadedDevicesType2 = new ArrayList<>();
pageLink = new TextPageLink(4);
do {
pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type2, pageLink);
loadedDevicesType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType2, idComparator);
Collections.sort(loadedDevicesType2, idComparator);
Assert.assertEquals(devicesType2, loadedDevicesType2);
for (Device device : loadedDevicesType1) {
deviceService.deleteDevice(device.getId());
}
pageLink = new TextPageLink(4);
pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type1, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesType2) {
deviceService.deleteDevice(device.getId());
}
pageLink = new TextPageLink(4);
pageData = deviceService.findDevicesByTenantIdAndType(tenantId, type2, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@Test
public void testFindDevicesByTenantIdAndCustomerId() {
@ -311,6 +439,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
Device device = new Device();
device.setTenantId(tenantId);
device.setName("Device"+i);
device.setType("default");
device = deviceService.saveDevice(device);
devices.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
}
@ -359,6 +488,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
device = deviceService.saveDevice(device);
devicesTitle1.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
}
@ -371,6 +501,7 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
device = deviceService.saveDevice(device);
devicesTitle2.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
}
@ -425,4 +556,94 @@ public class DeviceServiceImplTest extends AbstractServiceTest {
Assert.assertEquals(0, pageData.getData().size());
customerService.deleteCustomer(customerId);
}
@Test
public void testFindDevicesByTenantIdCustomerIdAndType() {
Customer customer = new Customer();
customer.setTitle("Test customer");
customer.setTenantId(tenantId);
customer = customerService.saveCustomer(customer);
CustomerId customerId = customer.getId();
String title1 = "Device title 1";
String type1 = "typeC";
List<Device> devicesType1 = new ArrayList<>();
for (int i=0;i<175;i++) {
Device device = new Device();
device.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title1+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type1);
device = deviceService.saveDevice(device);
devicesType1.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
}
String title2 = "Device title 2";
String type2 = "typeD";
List<Device> devicesType2 = new ArrayList<>();
for (int i=0;i<143;i++) {
Device device = new Device();
device.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric(15);
String name = title2+suffix;
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type2);
device = deviceService.saveDevice(device);
devicesType2.add(deviceService.assignDeviceToCustomer(device.getId(), customerId));
}
List<Device> loadedDevicesType1 = new ArrayList<>();
TextPageLink pageLink = new TextPageLink(15);
TextPageData<Device> pageData = null;
do {
pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
loadedDevicesType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType1, idComparator);
Collections.sort(loadedDevicesType1, idComparator);
Assert.assertEquals(devicesType1, loadedDevicesType1);
List<Device> loadedDevicesType2 = new ArrayList<>();
pageLink = new TextPageLink(4);
do {
pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
loadedDevicesType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageData.getNextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType2, idComparator);
Collections.sort(loadedDevicesType2, idComparator);
Assert.assertEquals(devicesType2, loadedDevicesType2);
for (Device device : loadedDevicesType1) {
deviceService.deleteDevice(device.getId());
}
pageLink = new TextPageLink(4);
pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type1, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesType2) {
deviceService.deleteDevice(device.getId());
}
pageLink = new TextPageLink(4);
pageData = deviceService.findDevicesByTenantIdAndCustomerIdAndType(tenantId, customerId, type2, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
customerService.deleteCustomer(customerId);
}
}

6
docker/.env

@ -1 +1,7 @@
CASSANDRA_DATA_DIR=/home/docker/cassandra_volume
# cassandra schema container environment variables
CREATE_SCHEMA=true
ADD_SYSTEM_DATA=false
ADD_DEMO_DATA=false
CASSANDRA_URL=cassandra

132
docker/cassandra/cassandra.yaml

@ -0,0 +1,132 @@
#
# Copyright © 2016-2017 The Thingsboard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
apiVersion: v1
kind: Service
metadata:
name: cassandra-headless
labels:
app: cassandra-headless
spec:
ports:
- port: 9042
name: cql
clusterIP: None
selector:
app: cassandra
---
apiVersion: "apps/v1beta1"
kind: StatefulSet
metadata:
name: cassandra
spec:
serviceName: cassandra-headless
replicas: 2
template:
metadata:
labels:
app: cassandra
spec:
nodeSelector:
machinetype: other
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- cassandra-headless
topologyKey: "kubernetes.io/hostname"
containers:
- name: cassandra
image: cassandra:3.9
imagePullPolicy: Always
ports:
- containerPort: 7000
name: intra-node
- containerPort: 7001
name: tls-intra-node
- containerPort: 7199
name: jmx
- containerPort: 9042
name: cql
- containerPort: 9160
name: thrift
securityContext:
capabilities:
add:
- IPC_LOCK
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "PID=$(pidof java) && kill $PID && while ps -p $PID > /dev/null; do sleep 1; done"]
env:
- name: MAX_HEAP_SIZE
value: 2048M
- name: HEAP_NEWSIZE
value: 100M
- name: CASSANDRA_SEEDS
value: "cassandra-0.cassandra-headless.default.svc.cluster.local"
- name: CASSANDRA_CLUSTER_NAME
value: "Thingsboard-Cluster"
- name: CASSANDRA_DC
value: "DC1-Thingsboard-Cluster"
- name: CASSANDRA_RACK
value: "Rack-Thingsboard-Cluster"
- name: CASSANDRA_AUTO_BOOTSTRAP
value: "false"
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
readinessProbe:
exec:
command:
- /bin/bash
- -c
- /ready-probe.sh
initialDelaySeconds: 15
timeoutSeconds: 5
volumeMounts:
- name: cassandra-data
mountPath: /var/lib/cassandra/data
- name: cassandra-commitlog
mountPath: /var/lib/cassandra/commitlog
volumeClaimTemplates:
- metadata:
name: cassandra-data
annotations:
volume.beta.kubernetes.io/storage-class: fast
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 3Gi
- metadata:
name: cassandra-commitlog
annotations:
volume.beta.kubernetes.io/storage-class: fast
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 2Gi

32
docker/deploy.sh → docker/common/common.yaml

@ -1,4 +1,3 @@
#!/bin/bash
#
# Copyright © 2016-2017 The Thingsboard Authors
#
@ -15,17 +14,20 @@
# limitations under the License.
#
command='docker-compose -f docker-compose.yml -f docker-compose.random.yml'
echo "stopping images.."
$command stop
echo "removing stopped images.."
$command rm -f
echo "building images.."
$command build
echo "starting images..."
$command up -d
---
apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
---
apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
name: fast
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
---

2
docker/docker-compose.static.yml

@ -17,7 +17,7 @@
version: '2'
services:
db:
cassandra:
ports:
- "9042:9042"
- "9160:9160"

28
docker/docker-compose.yml

@ -17,24 +17,32 @@
version: '2'
services:
thingsboard:
image: "thingsboard/application:1.2.3"
tb:
image: "thingsboard/application:1.2.4"
ports:
- "8080:8080"
- "1883:1883"
- "5683:5683/udp"
env_file:
- thingsboard.env
entrypoint: ./run_thingsboard.sh
thingsboard-db-schema:
image: "thingsboard/thingsboard-db-schema:1.2.3"
env_file:
- thingsboard-db-schema.env
entrypoint: ./install_schema.sh
db:
- tb.env
entrypoint: ./run-application.sh
tb-cassandra-schema:
image: "thingsboard/tb-cassandra-schema:1.2.4"
environment:
- CREATE_SCHEMA=${CREATE_SCHEMA}
- ADD_SYSTEM_DATA=${ADD_SYSTEM_DATA}
- ADD_DEMO_DATA=${ADD_DEMO_DATA}
- CASSANDRA_URL=${CASSANDRA_URL}
entrypoint: ./install-schema.sh
cassandra:
image: "cassandra:3.9"
ports:
- "9042"
- "9160"
volumes:
- "${CASSANDRA_DATA_DIR}:/var/lib/cassandra"
zk:
image: "zookeeper:3.4.9"
ports:
- "2181"
restart: always

4
docker/thingsboard-db-schema/Dockerfile → docker/tb-cassandra-schema/Dockerfile

@ -16,12 +16,12 @@
FROM cassandra:3.9
ADD install_schema.sh /root/install_schema.sh
ADD install-schema.sh /root/install-schema.sh
RUN apt-get update \
&& apt-get install -y nmap
RUN chmod +x /root/install_schema.sh
RUN chmod +x /root/install-schema.sh
ADD schema.cql /root/schema.cql
ADD demo-data.cql /root/demo-data.cql

13
docker/tb-cassandra-schema/Makefile

@ -0,0 +1,13 @@
VERSION=1.2.4
PROJECT=thingsboard
APP=tb-cassandra-schema
build:
cp ../../dao/src/main/resources/schema.cql .
cp ../../dao/src/main/resources/demo-data.cql .
cp ../../dao/src/main/resources/system-data.cql .
docker build --pull -t ${PROJECT}/${APP}:${VERSION} .
rm schema.cql demo-data.cql system-data.cql
push: build
docker push ${PROJECT}/${APP}:${VERSION}

16
docker/thingsboard-db-schema/install_schema.sh → docker/tb-cassandra-schema/install-schema.sh

@ -16,15 +16,15 @@
#
until nmap db -p 9042 | grep "9042/tcp open"
until nmap $CASSANDRA_URL -p 9042 | grep "9042/tcp open"
do
echo "Wait for Cassandra..."
echo "Wait for $CASSANDRA_URL..."
sleep 10
done
if [ "$SKIP_SCHEMA_CREATION" == "false" ]; then
if [ "$CREATE_SCHEMA" == "true" ]; then
echo "Creating 'Thingsboard' keyspace..."
cqlsh db -f /root/schema.cql
cqlsh $CASSANDRA_URL -f /root/schema.cql
if [ "$?" -eq 0 ]; then
echo "'Thingsboard' keyspace was successfully created!"
else
@ -32,9 +32,9 @@ if [ "$SKIP_SCHEMA_CREATION" == "false" ]; then
fi
fi
if [ "$SKIP_SYSTEM_DATA" == "false" ]; then
if [ "$ADD_SYSTEM_DATA" == "true" ]; then
echo "Adding system data..."
cqlsh db -f /root/system-data.cql
cqlsh $CASSANDRA_URL -f /root/system-data.cql
if [ "$?" -eq 0 ]; then
echo "System data was successfully added!"
else
@ -42,9 +42,9 @@ if [ "$SKIP_SYSTEM_DATA" == "false" ]; then
fi
fi
if [ "$SKIP_DEMO_DATA" == "false" ]; then
if [ "$ADD_DEMO_DATA" == "true" ]; then
echo "Adding demo data..."
cqlsh db -f /root/demo-data.cql
cqlsh $CASSANDRA_URL -f /root/demo-data.cql
if [ "$?" -eq 0 ]; then
echo "Demo data was successfully added!"
else

33
docker/docker-compose.random.yml → docker/tb-cassandra-schema/tb-cassandra-schema.yaml

@ -14,13 +14,26 @@
# limitations under the License.
#
version: '2'
services:
db:
ports:
- "9042"
- "9160"
zk:
ports:
- "2181"
apiVersion: v1
kind: Pod
metadata:
name: tb-cassandra-schema
spec:
containers:
- name: tb-cassandra-schema
imagePullPolicy: Always
image: thingsboard/tb-cassandra-schema:1.2.4
env:
- name: CREATE_SCHEMA
value: "false"
- name: ADD_SYSTEM_DATA
value: "false"
- name : ADD_DEMO_DATA
value: "false"
- name : CASSANDRA_URL
value: "cassandra-headless"
command:
- sh
- -c
- ./install-schema.sh
restartPolicy: Never

3
docker/thingsboard.env → docker/tb.env

@ -1,6 +1,7 @@
#Thingsboard server configuration
CASSANDRA_URL=db:9042
TB_CASSANDRA_SCHEMA_URL=tb-cassandra-schema
CASSANDRA_URL=cassandra:9042
ZOOKEEPER_URL=zk:2181
MQTT_BIND_ADDRESS=0.0.0.0
MQTT_BIND_PORT=1883

4
docker/thingsboard/Dockerfile → docker/tb/Dockerfile

@ -16,9 +16,9 @@
FROM openjdk:8-jre
ADD run_thingsboard.sh /root/run_thingsboard.sh
ADD run-application.sh /root/run-application.sh
ADD thingsboard.deb /root/thingsboard.deb
RUN chmod +x /root/run_thingsboard.sh
RUN chmod +x /root/run-application.sh
WORKDIR /root

11
docker/tb/Makefile

@ -0,0 +1,11 @@
VERSION=1.2.4
PROJECT=thingsboard
APP=application
build:
cp ../../application/target/thingsboard.deb .
docker build --pull -t ${PROJECT}/${APP}:${VERSION} .
rm thingsboard.deb
push: build
docker push ${PROJECT}/${APP}:${VERSION}

6
docker/thingsboard/run_thingsboard.sh → docker/tb/run-application.sh

@ -21,12 +21,12 @@ dpkg -i /root/thingsboard.deb
reachable=0
while [ $reachable -eq 0 ];
do
echo "thingsboard-db-schema container is still in progress. waiting until it completed..."
echo "$TB_CASSANDRA_SCHEMA_URL container is still in progress. waiting until it completed..."
sleep 3
ping -q -c 1 thingsboard-db-schema > /dev/null 2>&1
ping -q -c 1 $TB_CASSANDRA_SCHEMA_URL > /dev/null 2>&1
if [ "$?" -ne 0 ];
then
echo "thingsboard-db-schema container completed!"
echo "$TB_CASSANDRA_SCHEMA_URL container completed!"
reachable=1
fi
done

121
docker/tb/tb.yaml

@ -0,0 +1,121 @@
#
# Copyright © 2016-2017 The Thingsboard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
---
apiVersion: v1
kind: Service
metadata:
name: tb-service
labels:
app: tb-service
spec:
ports:
- port: 8080
name: ui
- port: 1883
name: mqtt
- port: 5683
name: coap
selector:
app: tb
type: LoadBalancer
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: tb-budget
spec:
selector:
matchLabels:
app: tb
minAvailable: 3
---
apiVersion: v1
kind: ConfigMap
metadata:
name: tb-config
data:
zookeeper.enabled: "true"
zookeeper.url: "zk-headless"
cassandra.url: "cassandra-headless:9042"
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: tb
spec:
serviceName: "tb-service"
replicas: 3
template:
metadata:
labels:
app: tb
spec:
nodeSelector:
machinetype: tb
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- tb-service
topologyKey: "kubernetes.io/hostname"
containers:
- name: tb
imagePullPolicy: Always
image: thingsboard/application:1.2.4
ports:
- containerPort: 8080
name: ui
- containerPort: 1883
name: mqtt
- containerPort: 5683
name: coap
- containerPort: 9001
name: rpc
env:
- name: ZOOKEEPER_ENABLED
valueFrom:
configMapKeyRef:
name: tb-config
key: zookeeper.enabled
- name: ZOOKEEPER_URL
valueFrom:
configMapKeyRef:
name: tb-config
key: zookeeper.url
- name : CASSANDRA_URL
valueFrom:
configMapKeyRef:
name: tb-config
key: cassandra.url
- name : RPC_HOST
valueFrom:
fieldRef:
fieldPath: status.podIP
command:
- sh
- -c
- ./run-application.sh
livenessProbe:
httpGet:
path: /login
port: ui-port
initialDelaySeconds: 120
timeoutSeconds: 10

5
docker/thingsboard-db-schema.env

@ -1,5 +0,0 @@
#Db schema configuration
SKIP_SCHEMA_CREATION=false
SKIP_SYSTEM_DATA=false
SKIP_DEMO_DATA=false

28
docker/thingsboard-db-schema/build_and_deploy.sh

@ -1,28 +0,0 @@
#!/bin/bash
#
# Copyright © 2016-2017 The Thingsboard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
cp ../../dao/src/main/resources/schema.cql schema.cql
cp ../../dao/src/main/resources/demo-data.cql demo-data.cql
cp ../../dao/src/main/resources/system-data.cql system-data.cql
docker build -t thingsboard/thingsboard-db-schema:1.2.3 -t thingsboard/thingsboard-db-schema:latest .
docker login
docker push thingsboard/thingsboard-db-schema:1.2.3
docker push thingsboard/thingsboard-db-schema:latest

26
docker/thingsboard/build_and_deploy.sh

@ -1,26 +0,0 @@
#!/bin/bash
#
# Copyright © 2016-2017 The Thingsboard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
cp ../../application/target/thingsboard.deb thingsboard.deb
docker build -t thingsboard/application:1.2.3 -t thingsboard/application:latest .
docker login
docker push thingsboard/application:1.2.3
docker push thingsboard/application:latest

71
docker/zookeeper/Dockerfile

@ -0,0 +1,71 @@
#
# Copyright © 2016-2017 The Thingsboard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
FROM ubuntu:16.04
ENV ZK_USER=zookeeper \
ZK_DATA_DIR=/var/lib/zookeeper/data \
ZK_DATA_LOG_DIR=/var/lib/zookeeper/log \
ZK_LOG_DIR=/var/log/zookeeper \
JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
ARG GPG_KEY=C823E3E5B12AF29C67F81976F5CECB3CB5E9BD2D
ARG ZK_DIST=zookeeper-3.4.9
RUN set -x \
&& apt-get update \
&& apt-get install -y openjdk-8-jre-headless wget netcat-openbsd \
&& wget -q "http://www.apache.org/dist/zookeeper/$ZK_DIST/$ZK_DIST.tar.gz" \
&& wget -q "http://www.apache.org/dist/zookeeper/$ZK_DIST/$ZK_DIST.tar.gz.asc" \
&& export GNUPGHOME="$(mktemp -d)" \
&& gpg --keyserver ha.pool.sks-keyservers.net --recv-key "$GPG_KEY" \
&& gpg --batch --verify "$ZK_DIST.tar.gz.asc" "$ZK_DIST.tar.gz" \
&& tar -xzf "$ZK_DIST.tar.gz" -C /opt \
&& rm -r "$GNUPGHOME" "$ZK_DIST.tar.gz" "$ZK_DIST.tar.gz.asc" \
&& ln -s /opt/$ZK_DIST /opt/zookeeper \
&& rm -rf /opt/zookeeper/CHANGES.txt \
/opt/zookeeper/README.txt \
/opt/zookeeper/NOTICE.txt \
/opt/zookeeper/CHANGES.txt \
/opt/zookeeper/README_packaging.txt \
/opt/zookeeper/build.xml \
/opt/zookeeper/config \
/opt/zookeeper/contrib \
/opt/zookeeper/dist-maven \
/opt/zookeeper/docs \
/opt/zookeeper/ivy.xml \
/opt/zookeeper/ivysettings.xml \
/opt/zookeeper/recipes \
/opt/zookeeper/src \
/opt/zookeeper/$ZK_DIST.jar.asc \
/opt/zookeeper/$ZK_DIST.jar.md5 \
/opt/zookeeper/$ZK_DIST.jar.sha1 \
&& apt-get autoremove -y wget \
&& rm -rf /var/lib/apt/lists/*
#Copy configuration generator script to bin
COPY zk-gen-config.sh zk-ok.sh /opt/zookeeper/bin/
# Create a user for the zookeeper process and configure file system ownership
# for nessecary directories and symlink the distribution as a user executable
RUN set -x \
&& useradd $ZK_USER \
&& [ `id -u $ZK_USER` -eq 1000 ] \
&& [ `id -g $ZK_USER` -eq 1000 ] \
&& mkdir -p $ZK_DATA_DIR $ZK_DATA_LOG_DIR $ZK_LOG_DIR /usr/share/zookeeper /tmp/zookeeper /usr/etc/ \
&& chown -R "$ZK_USER:$ZK_USER" /opt/$ZK_DIST $ZK_DATA_DIR $ZK_LOG_DIR $ZK_DATA_LOG_DIR /tmp/zookeeper \
&& ln -s /opt/zookeeper/conf/ /usr/etc/zookeeper \
&& ln -s /opt/zookeeper/bin/* /usr/bin \
&& ln -s /opt/zookeeper/$ZK_DIST.jar /usr/share/zookeeper/ \
&& ln -s /opt/zookeeper/lib/* /usr/share/zookeeper

9
docker/zookeeper/Makefile

@ -0,0 +1,9 @@
VERSION=1.2.4
PROJECT=thingsboard
APP=zk
build:
docker build --pull -t ${PROJECT}/${APP}:${VERSION} .
push: build
docker push ${PROJECT}/${APP}:${VERSION}

153
docker/zookeeper/zk-gen-config.sh

@ -0,0 +1,153 @@
#!/usr/bin/env bash
#
# Copyright © 2016-2017 The Thingsboard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
ZK_USER=${ZK_USER:-"zookeeper"}
ZK_LOG_LEVEL=${ZK_LOG_LEVEL:-"INFO"}
ZK_DATA_DIR=${ZK_DATA_DIR:-"/var/lib/zookeeper/data"}
ZK_DATA_LOG_DIR=${ZK_DATA_LOG_DIR:-"/var/lib/zookeeper/log"}
ZK_LOG_DIR=${ZK_LOG_DIR:-"var/log/zookeeper"}
ZK_CONF_DIR=${ZK_CONF_DIR:-"/opt/zookeeper/conf"}
ZK_CLIENT_PORT=${ZK_CLIENT_PORT:-2181}
ZK_SERVER_PORT=${ZK_SERVER_PORT:-2888}
ZK_ELECTION_PORT=${ZK_ELECTION_PORT:-3888}
ZK_TICK_TIME=${ZK_TICK_TIME:-2000}
ZK_INIT_LIMIT=${ZK_INIT_LIMIT:-10}
ZK_SYNC_LIMIT=${ZK_SYNC_LIMIT:-5}
ZK_HEAP_SIZE=${ZK_HEAP_SIZE:-2G}
ZK_MAX_CLIENT_CNXNS=${ZK_MAX_CLIENT_CNXNS:-60}
ZK_MIN_SESSION_TIMEOUT=${ZK_MIN_SESSION_TIMEOUT:- $((ZK_TICK_TIME*2))}
ZK_MAX_SESSION_TIMEOUT=${ZK_MAX_SESSION_TIMEOUT:- $((ZK_TICK_TIME*20))}
ZK_SNAP_RETAIN_COUNT=${ZK_SNAP_RETAIN_COUNT:-3}
ZK_PURGE_INTERVAL=${ZK_PURGE_INTERVAL:-0}
ID_FILE="$ZK_DATA_DIR/myid"
ZK_CONFIG_FILE="$ZK_CONF_DIR/zoo.cfg"
LOGGER_PROPS_FILE="$ZK_CONF_DIR/log4j.properties"
JAVA_ENV_FILE="$ZK_CONF_DIR/java.env"
HOST=`hostname -s`
DOMAIN=`hostname -d`
function print_servers() {
for (( i=1; i<=$ZK_REPLICAS; i++ ))
do
echo "server.$i=$NAME-$((i-1)).$DOMAIN:$ZK_SERVER_PORT:$ZK_ELECTION_PORT"
done
}
function validate_env() {
echo "Validating environment"
if [ -z $ZK_REPLICAS ]; then
echo "ZK_REPLICAS is a mandatory environment variable"
exit 1
fi
if [[ $HOST =~ (.*)-([0-9]+)$ ]]; then
NAME=${BASH_REMATCH[1]}
ORD=${BASH_REMATCH[2]}
else
echo "Failed to extract ordinal from hostname $HOST"
exit 1
fi
MY_ID=$((ORD+1))
echo "ZK_REPLICAS=$ZK_REPLICAS"
echo "MY_ID=$MY_ID"
echo "ZK_LOG_LEVEL=$ZK_LOG_LEVEL"
echo "ZK_DATA_DIR=$ZK_DATA_DIR"
echo "ZK_DATA_LOG_DIR=$ZK_DATA_LOG_DIR"
echo "ZK_LOG_DIR=$ZK_LOG_DIR"
echo "ZK_CLIENT_PORT=$ZK_CLIENT_PORT"
echo "ZK_SERVER_PORT=$ZK_SERVER_PORT"
echo "ZK_ELECTION_PORT=$ZK_ELECTION_PORT"
echo "ZK_TICK_TIME=$ZK_TICK_TIME"
echo "ZK_INIT_LIMIT=$ZK_INIT_LIMIT"
echo "ZK_SYNC_LIMIT=$ZK_SYNC_LIMIT"
echo "ZK_MAX_CLIENT_CNXNS=$ZK_MAX_CLIENT_CNXNS"
echo "ZK_MIN_SESSION_TIMEOUT=$ZK_MIN_SESSION_TIMEOUT"
echo "ZK_MAX_SESSION_TIMEOUT=$ZK_MAX_SESSION_TIMEOUT"
echo "ZK_HEAP_SIZE=$ZK_HEAP_SIZE"
echo "ZK_SNAP_RETAIN_COUNT=$ZK_SNAP_RETAIN_COUNT"
echo "ZK_PURGE_INTERVAL=$ZK_PURGE_INTERVAL"
echo "ENSEMBLE"
print_servers
echo "Environment validation successful"
}
function create_config() {
rm -f $ZK_CONFIG_FILE
echo "Creating ZooKeeper configuration"
echo "#This file was autogenerated by zk DO NOT EDIT" >> $ZK_CONFIG_FILE
echo "clientPort=$ZK_CLIENT_PORT" >> $ZK_CONFIG_FILE
echo "dataDir=$ZK_DATA_DIR" >> $ZK_CONFIG_FILE
echo "dataLogDir=$ZK_DATA_LOG_DIR" >> $ZK_CONFIG_FILE
echo "tickTime=$ZK_TICK_TIME" >> $ZK_CONFIG_FILE
echo "initLimit=$ZK_INIT_LIMIT" >> $ZK_CONFIG_FILE
echo "syncLimit=$ZK_SYNC_LIMIT" >> $ZK_CONFIG_FILE
echo "maxClientCnxns=$ZK_MAX_CLIENT_CNXNS" >> $ZK_CONFIG_FILE
echo "minSessionTimeout=$ZK_MIN_SESSION_TIMEOUT" >> $ZK_CONFIG_FILE
echo "maxSessionTimeout=$ZK_MAX_SESSION_TIMEOUT" >> $ZK_CONFIG_FILE
echo "autopurge.snapRetainCount=$ZK_SNAP_RETAIN_COUNT" >> $ZK_CONFIG_FILE
echo "autopurge.purgeInteval=$ZK_PURGE_INTERVAL" >> $ZK_CONFIG_FILE
if [ $ZK_REPLICAS -gt 1 ]; then
print_servers >> $ZK_CONFIG_FILE
fi
echo "Wrote ZooKeeper configuration file to $ZK_CONFIG_FILE"
}
function create_data_dirs() {
echo "Creating ZooKeeper data directories and setting permissions"
if [ ! -d $ZK_DATA_DIR ]; then
mkdir -p $ZK_DATA_DIR
chown -R $ZK_USER:$ZK_USER $ZK_DATA_DIR
fi
if [ ! -d $ZK_DATA_LOG_DIR ]; then
mkdir -p $ZK_DATA_LOG_DIR
chown -R $ZK_USER:$ZK_USER $ZK_DATA_LOG_DIR
fi
if [ ! -d $ZK_LOG_DIR ]; then
mkdir -p $ZK_LOG_DIR
chown -R $ZK_USER:$ZK_USER $ZK_LOG_DIR
fi
if [ ! -f $ID_FILE ]; then
echo $MY_ID >> $ID_FILE
fi
echo "Created ZooKeeper data directories and set permissions in $ZK_DATA_DIR"
}
function create_log_props () {
rm -f $LOGGER_PROPS_FILE
echo "Creating ZooKeeper log4j configuration"
echo "zookeeper.root.logger=CONSOLE" >> $LOGGER_PROPS_FILE
echo "zookeeper.console.threshold="$ZK_LOG_LEVEL >> $LOGGER_PROPS_FILE
echo "log4j.rootLogger=\${zookeeper.root.logger}" >> $LOGGER_PROPS_FILE
echo "log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender" >> $LOGGER_PROPS_FILE
echo "log4j.appender.CONSOLE.Threshold=\${zookeeper.console.threshold}" >> $LOGGER_PROPS_FILE
echo "log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout" >> $LOGGER_PROPS_FILE
echo "log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n" >> $LOGGER_PROPS_FILE
echo "Wrote log4j configuration to $LOGGER_PROPS_FILE"
}
function create_java_env() {
rm -f $JAVA_ENV_FILE
echo "Creating JVM configuration file"
echo "ZOO_LOG_DIR=$ZK_LOG_DIR" >> $JAVA_ENV_FILE
echo "JVMFLAGS=\"-Xmx$ZK_HEAP_SIZE -Xms$ZK_HEAP_SIZE\"" >> $JAVA_ENV_FILE
echo "Wrote JVM configuration to $JAVA_ENV_FILE"
}
validate_env && create_config && create_log_props && create_data_dirs && create_java_env

25
docker/deploy_cassandra_zookeeper.sh → docker/zookeeper/zk-ok.sh

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
#
# Copyright © 2016-2017 The Thingsboard Authors
#
@ -15,17 +15,14 @@
# limitations under the License.
#
# zk-ok.sh uses the ruok ZooKeeper four letter work to determine if the instance
# is health. The $? variable will be set to 0 if server responds that it is
# healthy, or 1 if the server fails to respond.
command='docker-compose -f docker-compose.yml -f docker-compose.static.yml'
echo "stopping images.."
$command stop
echo "removing stopped images.."
$command rm -f
echo "building images.."
$command build
echo "starting cassandra, zookeeper, thingsboard-db-schema images..."
$command up -d db zk thingsboard-db-schema
ZK_CLIENT_PORT=${ZK_CLIENT_PORT:-2181}
OK=$(echo ruok | nc 127.0.0.1 $ZK_CLIENT_PORT)
if [ "$OK" == "imok" ]; then
exit 0
else
exit 1
fi

190
docker/zookeeper/zookeeper.yaml

@ -0,0 +1,190 @@
#
# Copyright © 2016-2017 The Thingsboard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
apiVersion: v1
kind: Service
metadata:
name: zk-headless
labels:
app: zk-headless
spec:
ports:
- port: 2888
name: server
- port: 3888
name: leader-election
clusterIP: None
selector:
app: zk
---
apiVersion: v1
kind: ConfigMap
metadata:
name: zk-config
data:
ensemble: "zk-0;zk-1;zk-2"
replicas: "3"
jvm.heap: "500m"
tick: "2000"
init: "10"
sync: "5"
client.cnxns: "60"
snap.retain: "3"
purge.interval: "1"
client.port: "2181"
server.port: "2888"
election.port: "3888"
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: zk-budget
spec:
selector:
matchLabels:
app: zk
minAvailable: 3
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: zk
spec:
serviceName: zk-headless
replicas: 3
template:
metadata:
labels:
app: zk
annotations:
pod.alpha.kubernetes.io/initialized: "true"
spec:
nodeSelector:
machinetype: other
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- zk-headless
topologyKey: "kubernetes.io/hostname"
containers:
- name: zk
imagePullPolicy: Always
image: thingsboard/zk:1.2.4
ports:
- containerPort: 2181
name: client
- containerPort: 2888
name: server
- containerPort: 3888
name: leader-election
env:
- name : ZK_ENSEMBLE
valueFrom:
configMapKeyRef:
name: zk-config
key: ensemble
- name : ZK_REPLICAS
valueFrom:
configMapKeyRef:
name: zk-config
key: replicas
- name : ZK_HEAP_SIZE
valueFrom:
configMapKeyRef:
name: zk-config
key: jvm.heap
- name : ZK_TICK_TIME
valueFrom:
configMapKeyRef:
name: zk-config
key: tick
- name : ZK_INIT_LIMIT
valueFrom:
configMapKeyRef:
name: zk-config
key: init
- name : ZK_SYNC_LIMIT
valueFrom:
configMapKeyRef:
name: zk-config
key: tick
- name : ZK_MAX_CLIENT_CNXNS
valueFrom:
configMapKeyRef:
name: zk-config
key: client.cnxns
- name: ZK_SNAP_RETAIN_COUNT
valueFrom:
configMapKeyRef:
name: zk-config
key: snap.retain
- name: ZK_PURGE_INTERVAL
valueFrom:
configMapKeyRef:
name: zk-config
key: purge.interval
- name: ZK_CLIENT_PORT
valueFrom:
configMapKeyRef:
name: zk-config
key: client.port
- name: ZK_SERVER_PORT
valueFrom:
configMapKeyRef:
name: zk-config
key: server.port
- name: ZK_ELECTION_PORT
valueFrom:
configMapKeyRef:
name: zk-config
key: election.port
command:
- sh
- -c
- zk-gen-config.sh && zkServer.sh start-foreground
readinessProbe:
exec:
command:
- "zk-ok.sh"
initialDelaySeconds: 15
timeoutSeconds: 5
livenessProbe:
exec:
command:
- "zk-ok.sh"
initialDelaySeconds: 15
timeoutSeconds: 5
volumeMounts:
- name: zkdatadir
mountPath: /var/lib/zookeeper
securityContext:
runAsUser: 1000
fsGroup: 1000
volumeClaimTemplates:
- metadata:
name: zkdatadir
annotations:
volume.beta.kubernetes.io/storage-class: slow
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi

2
transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttSslHandlerProvider.java

@ -41,7 +41,7 @@ import java.security.cert.X509Certificate;
*/
@Slf4j
@Component("MqttSslHandlerProvider")
@ConditionalOnProperty(prefix = "mqtt.ssl", value = "key-store", havingValue = "", matchIfMissing = false)
@ConditionalOnProperty(prefix = "mqtt.ssl", value = "enabled", havingValue = "true", matchIfMissing = false)
public class MqttSslHandlerProvider {
public static final String TLS = "TLS";

24
ui/src/app/api/asset.service.js

@ -31,7 +31,8 @@ function AssetService($http, $q, customerService, userService) {
getTenantAssets: getTenantAssets,
getCustomerAssets: getCustomerAssets,
findByQuery: findByQuery,
fetchAssetsByNameFilter: fetchAssetsByNameFilter
fetchAssetsByNameFilter: fetchAssetsByNameFilter,
getAssetTypes: getAssetTypes
}
return service;
@ -152,7 +153,7 @@ function AssetService($http, $q, customerService, userService) {
return deferred.promise;
}
function getTenantAssets(pageLink, applyCustomersInfo, config) {
function getTenantAssets(pageLink, applyCustomersInfo, config, type) {
var deferred = $q.defer();
var url = '/api/tenant/assets?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
@ -164,6 +165,9 @@ function AssetService($http, $q, customerService, userService) {
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
}
if (angular.isDefined(type) && type.length) {
url += '&type=' + type;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
customerService.applyAssignedCustomersInfo(response.data.data).then(
@ -184,7 +188,7 @@ function AssetService($http, $q, customerService, userService) {
return deferred.promise;
}
function getCustomerAssets(customerId, pageLink, applyCustomersInfo, config) {
function getCustomerAssets(customerId, pageLink, applyCustomersInfo, config, type) {
var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/assets?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
@ -196,6 +200,9 @@ function AssetService($http, $q, customerService, userService) {
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
}
if (angular.isDefined(type) && type.length) {
url += '&type=' + type;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
@ -258,4 +265,15 @@ function AssetService($http, $q, customerService, userService) {
return deferred.promise;
}
function getAssetTypes() {
var deferred = $q.defer();
var url = '/api/asset/types';
$http.get(url).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
}

24
ui/src/app/api/device.service.js

@ -41,12 +41,13 @@ function DeviceService($http, $q, attributeService, customerService, types) {
deleteDeviceAttributes: deleteDeviceAttributes,
sendOneWayRpcCommand: sendOneWayRpcCommand,
sendTwoWayRpcCommand: sendTwoWayRpcCommand,
findByQuery: findByQuery
findByQuery: findByQuery,
getDeviceTypes: getDeviceTypes
}
return service;
function getTenantDevices(pageLink, applyCustomersInfo, config) {
function getTenantDevices(pageLink, applyCustomersInfo, config, type) {
var deferred = $q.defer();
var url = '/api/tenant/devices?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
@ -58,6 +59,9 @@ function DeviceService($http, $q, attributeService, customerService, types) {
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
}
if (angular.isDefined(type) && type.length) {
url += '&type=' + type;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
customerService.applyAssignedCustomersInfo(response.data.data).then(
@ -78,7 +82,7 @@ function DeviceService($http, $q, attributeService, customerService, types) {
return deferred.promise;
}
function getCustomerDevices(customerId, pageLink, applyCustomersInfo, config) {
function getCustomerDevices(customerId, pageLink, applyCustomersInfo, config, type) {
var deferred = $q.defer();
var url = '/api/customer/' + customerId + '/devices?limit=' + pageLink.limit;
if (angular.isDefined(pageLink.textSearch)) {
@ -90,6 +94,9 @@ function DeviceService($http, $q, attributeService, customerService, types) {
if (angular.isDefined(pageLink.textOffset)) {
url += '&textOffset=' + pageLink.textOffset;
}
if (angular.isDefined(type) && type.length) {
url += '&type=' + type;
}
$http.get(url, config).then(function success(response) {
if (applyCustomersInfo) {
customerService.applyAssignedCustomerInfo(response.data.data, customerId).then(
@ -286,4 +293,15 @@ function DeviceService($http, $q, attributeService, customerService, types) {
return deferred.promise;
}
function getDeviceTypes() {
var deferred = $q.defer();
var url = '/api/device/types';
$http.get(url).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
}

13
ui/src/app/api/entity-relation.service.js

@ -25,6 +25,7 @@ function EntityRelationService($http, $q) {
deleteRelation: deleteRelation,
deleteRelations: deleteRelations,
findByFrom: findByFrom,
findInfoByFrom: findInfoByFrom,
findByFromAndType: findByFromAndType,
findByTo: findByTo,
findByToAndType: findByToAndType,
@ -84,6 +85,18 @@ function EntityRelationService($http, $q) {
return deferred.promise;
}
function findInfoByFrom(fromId, fromType) {
var deferred = $q.defer();
var url = '/api/relations/info?fromId=' + fromId;
url += '&fromType=' + fromType;
$http.get(url, null).then(function success(response) {
deferred.resolve(response.data);
}, function fail() {
deferred.reject();
});
return deferred.promise;
}
function findByFromAndType(fromId, fromType, relationType) {
var deferred = $q.defer();
var url = '/api/relations?fromId=' + fromId;

142
ui/src/app/api/entity.service.js

@ -20,14 +20,13 @@ export default angular.module('thingsboard.api.entity', [thingsboardTypes])
.name;
/*@ngInject*/
function EntityService($http, $q, $filter, $translate, userService, deviceService,
function EntityService($http, $q, $filter, $translate, $log, userService, deviceService,
assetService, tenantService, customerService,
ruleService, pluginService, entityRelationService, attributeService, types, utils) {
ruleService, pluginService, dashboardService, entityRelationService, attributeService, types, utils) {
var service = {
getEntity: getEntity,
getEntities: getEntities,
getEntitiesByNameFilter: getEntitiesByNameFilter,
entityName: entityName,
processEntityAliases: processEntityAliases,
getEntityKeys: getEntityKeys,
checkEntityAlias: checkEntityAlias,
@ -36,7 +35,8 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
saveRelatedEntity: saveRelatedEntity,
getRelatedEntity: getRelatedEntity,
deleteRelatedEntity: deleteRelatedEntity,
moveEntity: moveEntity
moveEntity: moveEntity,
copyEntity: copyEntity
};
return service;
@ -62,6 +62,15 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
case types.entityType.plugin:
promise = pluginService.getPlugin(entityId);
break;
case types.entityType.dashboard:
promise = dashboardService.getDashboardInfo(entityId);
break;
case types.entityType.user:
promise = userService.getUser(entityId);
break;
case types.entityType.alarm:
$log.error('Get Alarm Entity is not implemented!');
break;
}
return promise;
}
@ -133,6 +142,15 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
case types.entityType.plugin:
promise = getEntitiesByIdsPromise(pluginService.getPlugin, entityIds);
break;
case types.entityType.dashboard:
promise = getEntitiesByIdsPromise(dashboardService.getDashboardInfo, entityIds);
break;
case types.entityType.user:
promise = getEntitiesByIdsPromise(userService.getUser, entityIds);
break;
case types.entityType.alarm:
$log.error('Get Alarm Entity is not implemented!');
break;
}
return promise;
}
@ -140,34 +158,38 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
function getEntities(entityType, entityIds, config) {
var deferred = $q.defer();
var promise = getEntitiesPromise(entityType, entityIds, config);
promise.then(
function success(result) {
deferred.resolve(result);
},
function fail() {
deferred.reject();
}
);
if (promise) {
promise.then(
function success(result) {
deferred.resolve(result);
},
function fail() {
deferred.reject();
}
);
} else {
deferred.reject();
}
return deferred.promise;
}
function getEntitiesByPageLinkPromise(entityType, pageLink, config) {
function getEntitiesByPageLinkPromise(entityType, pageLink, config, subType) {
var promise;
var user = userService.getCurrentUser();
var customerId = user.customerId;
switch (entityType) {
case types.entityType.device:
if (user.authority === 'CUSTOMER_USER') {
promise = deviceService.getCustomerDevices(customerId, pageLink, false, config);
promise = deviceService.getCustomerDevices(customerId, pageLink, false, config, subType);
} else {
promise = deviceService.getTenantDevices(pageLink, false, config);
promise = deviceService.getTenantDevices(pageLink, false, config, subType);
}
break;
case types.entityType.asset:
if (user.authority === 'CUSTOMER_USER') {
promise = assetService.getCustomerAssets(customerId, pageLink, false, config);
promise = assetService.getCustomerAssets(customerId, pageLink, false, config, subType);
} else {
promise = assetService.getTenantAssets(pageLink, false, config);
promise = assetService.getTenantAssets(pageLink, false, config, subType);
}
break;
case types.entityType.tenant:
@ -182,48 +204,48 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
case types.entityType.plugin:
promise = pluginService.getAllPlugins(pageLink);
break;
case types.entityType.dashboard:
if (user.authority === 'CUSTOMER_USER') {
promise = dashboardService.getCustomerDashboards(customerId, pageLink);
} else {
promise = dashboardService.getTenantDashboards(pageLink);
}
break;
case types.entityType.user:
$log.error('Get User Entities is not implemented!');
break;
case types.entityType.alarm:
$log.error('Get Alarm Entities is not implemented!');
break;
}
return promise;
}
function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config) {
function getEntitiesByNameFilter(entityType, entityNameFilter, limit, config, subType) {
var deferred = $q.defer();
var pageLink = {limit: limit, textSearch: entityNameFilter};
var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config);
promise.then(
function success(result) {
if (result.data && result.data.length > 0) {
deferred.resolve(result.data);
} else {
var promise = getEntitiesByPageLinkPromise(entityType, pageLink, config, subType);
if (promise) {
promise.then(
function success(result) {
if (result.data && result.data.length > 0) {
deferred.resolve(result.data);
} else {
deferred.resolve(null);
}
},
function fail() {
deferred.resolve(null);
}
},
function fail() {
deferred.resolve(null);
}
);
return deferred.promise;
}
function entityName(entityType, entity) {
var name = '';
switch (entityType) {
case types.entityType.device:
case types.entityType.asset:
case types.entityType.rule:
case types.entityType.plugin:
name = entity.name;
break;
case types.entityType.tenant:
case types.entityType.customer:
name = entity.title;
break;
);
} else {
deferred.resolve(null);
}
return name;
return deferred.promise;
}
function entityToEntityInfo(entityType, entity) {
return { name: entityName(entityType, entity), entityType: entityType, id: entity.id.id };
return { name: entity.name, entityType: entityType, id: entity.id.id };
}
function entitiesToEntitiesInfo(entityType, entities) {
@ -626,6 +648,32 @@ function EntityService($http, $q, $filter, $translate, userService, deviceServic
return deferred.promise;
}
function copyEntity(entity, targetParentId, keys) {
var deferred = $q.defer();
if (!entity.id && !entity.id.id) {
deferred.reject();
} else {
getRelatedEntity(entity.id, keys).then(
function success(relatedEntity) {
delete relatedEntity.id.id;
relatedEntity.name = entity.name;
saveRelatedEntity(relatedEntity, targetParentId, keys).then(
function success(savedEntity) {
deferred.resolve(savedEntity);
},
function fail() {
deferred.reject();
}
);
},
function fail() {
deferred.reject();
}
);
}
return deferred.promise;
}
function saveEntityPromise(entity) {
var entityType = entity.id.entityType;
if (!entity.id.id) {

4
ui/src/app/app.config.js

@ -160,10 +160,6 @@ export default function AppConfig($provide,
indigoTheme();
}
$mdThemingProvider.theme('tb-search-input', 'default')
.primaryPalette('tb-primary')
.backgroundPalette('tb-primary');
$mdThemingProvider.setDefaultTheme('default');
//$mdThemingProvider.alwaysWatchTheme(true);
}

7
ui/src/app/asset/asset-card.tpl.html

@ -15,5 +15,8 @@
limitations under the License.
-->
<div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'asset.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div>
<div class="tb-small" ng-show="vm.isPublic()">{{'asset.public' | translate}}</div>
<div flex layout="column" style="margin-top: -10px;">
<div flex style="text-transform: uppercase; padding-bottom: 10px;">{{vm.item.type}}</div>
<div class="tb-small" ng-show="vm.isAssignedToCustomer()">{{'asset.assignedToCustomer' | translate}} '{{vm.item.assignedCustomer.title}}'</div>
<div class="tb-small" ng-show="vm.isPublic()">{{'asset.public' | translate}}</div>
</div>

14
ui/src/app/asset/asset-fieldset.tpl.html

@ -56,13 +56,13 @@
<div translate ng-message="required">asset.name-required</div>
</div>
</md-input-container>
<md-input-container class="md-block">
<label translate>asset.type</label>
<input required name="type" ng-model="asset.type">
<div ng-messages="theForm.name.$error">
<div translate ng-message="required">asset.type-required</div>
</div>
</md-input-container>
<tb-entity-subtype-autocomplete
ng-disabled="loading || !isEdit"
tb-required="true"
the-form="theForm"
ng-model="asset.type"
entity-type="types.entityType.asset">
</tb-entity-subtype-autocomplete>
<md-input-container class="md-block">
<label translate>asset.description</label>
<textarea ng-model="asset.additionalInfo.description" rows="2"></textarea>

12
ui/src/app/asset/asset.controller.js

@ -47,7 +47,8 @@ export function AssetCardController(types) {
/*@ngInject*/
export function AssetController(userService, assetService, customerService, $state, $stateParams, $document, $mdDialog, $q, $translate, types) {
export function AssetController($rootScope, userService, assetService, customerService, $state, $stateParams,
$document, $mdDialog, $q, $translate, types) {
var customerId = $stateParams.customerId;
@ -129,8 +130,8 @@ export function AssetController(userService, assetService, customerService, $sta
}
if (vm.assetsScope === 'tenant') {
fetchAssetsFunction = function (pageLink) {
return assetService.getTenantAssets(pageLink, true);
fetchAssetsFunction = function (pageLink, assetType) {
return assetService.getTenantAssets(pageLink, true, null, assetType);
};
deleteAssetFunction = function (assetId) {
return assetService.deleteAsset(assetId);
@ -229,8 +230,8 @@ export function AssetController(userService, assetService, customerService, $sta
} else if (vm.assetsScope === 'customer' || vm.assetsScope === 'customer_user') {
fetchAssetsFunction = function (pageLink) {
return assetService.getCustomerAssets(customerId, pageLink, true);
fetchAssetsFunction = function (pageLink, assetType) {
return assetService.getCustomerAssets(customerId, pageLink, true, null, assetType);
};
deleteAssetFunction = function (assetId) {
return assetService.unassignAssetFromCustomer(assetId);
@ -333,6 +334,7 @@ export function AssetController(userService, assetService, customerService, $sta
var deferred = $q.defer();
assetService.saveAsset(asset).then(
function success(savedAsset) {
$rootScope.$broadcast('assetSaved');
var assets = [ savedAsset ];
customerService.applyAssignedCustomersInfo(assets).then(
function success(items) {

1
ui/src/app/asset/asset.directive.js

@ -25,6 +25,7 @@ export default function AssetDirective($compile, $templateCache, toast, $transla
var template = $templateCache.get(assetFieldsetTemplate);
element.html(template);
scope.types = types;
scope.isAssignedToCustomer = false;
scope.isPublic = false;
scope.assignedCustomer = null;

6
ui/src/app/asset/asset.routes.js

@ -20,7 +20,7 @@ import assetsTemplate from './assets.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/*@ngInject*/
export default function AssetRoutes($stateProvider) {
export default function AssetRoutes($stateProvider, types) {
$stateProvider
.state('home.assets', {
url: '/assets',
@ -37,6 +37,8 @@ export default function AssetRoutes($stateProvider) {
data: {
assetsType: 'tenant',
searchEnabled: true,
searchByEntitySubtype: true,
searchEntityType: types.entityType.asset,
pageTitle: 'asset.assets'
},
ncyBreadcrumb: {
@ -58,6 +60,8 @@ export default function AssetRoutes($stateProvider) {
data: {
assetsType: 'customer',
searchEnabled: true,
searchByEntitySubtype: true,
searchEntityType: types.entityType.asset,
pageTitle: 'customer.assets'
},
ncyBreadcrumb: {

6
ui/src/app/asset/assets.tpl.html

@ -55,4 +55,10 @@
default-event-type="{{vm.types.eventType.alarm.value}}">
</tb-event-table>
</md-tab>
<md-tab ng-if="!vm.grid.detailsConfig.isDetailsEditMode" label="{{ 'relation.relations' | translate }}">
<tb-relation-table flex
entity-id="vm.grid.operatingItem().id.id"
entity-type="{{vm.types.entityType.asset}}">
</tb-relation-table>
</md-tab>
</tb-grid>

1
ui/src/app/common/dashboard-utils.service.js

@ -179,6 +179,7 @@ function DashboardUtils(types, utils, timeService) {
dashboard.configuration.settings.showEntitiesSelect = true;
dashboard.configuration.settings.showDashboardTimewindow = true;
dashboard.configuration.settings.showDashboardExport = true;
dashboard.configuration.settings.toolbarAlwaysOpen = false;
} else {
if (angular.isUndefined(dashboard.configuration.settings.stateControllerId)) {
dashboard.configuration.settings.stateControllerId = 'default';

5
ui/src/app/common/types.constant.js

@ -98,7 +98,10 @@ export default angular.module('thingsboard.types', [])
rule: "RULE",
plugin: "PLUGIN",
tenant: "TENANT",
customer: "CUSTOMER"
customer: "CUSTOMER",
user: "USER",
dashboard: "DASHBOARD",
alarm: "ALARM"
},
entitySearchDirection: {
from: "FROM",

26
ui/src/app/common/utils.service.js

@ -108,7 +108,8 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
guid: guid,
isLocalUrl: isLocalUrl,
validateDatasources: validateDatasources,
createKey: createKey
createKey: createKey,
entityTypeName: entityTypeName
}
return service;
@ -346,4 +347,27 @@ function Utils($mdColorPalette, $rootScope, $window, types) {
return dataKey;
}
function entityTypeName (type) {
switch (type) {
case types.entityType.device:
return 'entity.type-device';
case types.entityType.asset:
return 'entity.type-asset';
case types.entityType.rule:
return 'entity.type-rule';
case types.entityType.plugin:
return 'entity.type-plugin';
case types.entityType.tenant:
return 'entity.type-tenant';
case types.entityType.customer:
return 'entity.type-customer';
case types.entityType.user:
return 'entity.type-user';
case types.entityType.dashboard:
return 'entity.type-dashboard';
case types.entityType.alarm:
return 'entity.type-alarm';
}
}
}

2
ui/src/app/components/dashboard-autocomplete.tpl.html

@ -34,7 +34,7 @@
</md-item-template>
<md-not-found>
<div class="tb-not-found">
<span translate translate-values='{ dashboard: dashboardSearchText }'>dashboard.no-dashboards-matching</span>
<span translate translate-values='{ entity: dashboardSearchText }'>dashboard.no-dashboards-matching</span>
</div>
</md-not-found>
<div ng-messages="theForm.dashboard.$error">

248
ui/src/app/components/datasource-device.directive.js

@ -1,248 +0,0 @@
/*
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import './datasource-device.scss';
import 'md-color-picker';
import tinycolor from 'tinycolor2';
import $ from 'jquery';
import thingsboardTypes from '../common/types.constant';
import thingsboardDatakeyConfigDialog from './datakey-config-dialog.controller';
import thingsboardTruncate from './truncate.filter';
/* eslint-disable import/no-unresolved, import/default */
import datasourceDeviceTemplate from './datasource-device.tpl.html';
import datakeyConfigDialogTemplate from './datakey-config-dialog.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/* eslint-disable angular/angularelement */
export default angular.module('thingsboard.directives.datasourceDevice', [thingsboardTruncate, thingsboardTypes, thingsboardDatakeyConfigDialog])
.directive('tbDatasourceDevice', DatasourceDevice)
.name;
/*@ngInject*/
function DatasourceDevice($compile, $templateCache, $q, $mdDialog, $window, $document, $mdColorPicker, $mdConstant, types) {
var linker = function (scope, element, attrs, ngModelCtrl) {
var template = $templateCache.get(datasourceDeviceTemplate);
element.html(template);
scope.ngModelCtrl = ngModelCtrl;
scope.types = types;
scope.selectedTimeseriesDataKey = null;
scope.timeseriesDataKeySearchText = null;
scope.selectedAttributeDataKey = null;
scope.attributeDataKeySearchText = null;
scope.updateValidity = function () {
if (ngModelCtrl.$viewValue) {
var value = ngModelCtrl.$viewValue;
var dataValid = angular.isDefined(value) && value != null;
ngModelCtrl.$setValidity('deviceData', dataValid);
if (dataValid) {
ngModelCtrl.$setValidity('deviceAlias',
angular.isDefined(value.deviceAliasId) &&
value.deviceAliasId != null);
ngModelCtrl.$setValidity('deviceKeys',
angular.isDefined(value.dataKeys) &&
value.dataKeys != null &&
value.dataKeys.length > 0);
}
}
};
scope.$watch('deviceAlias', function () {
if (ngModelCtrl.$viewValue) {
if (scope.deviceAlias) {
ngModelCtrl.$viewValue.deviceAliasId = scope.deviceAlias.id;
} else {
ngModelCtrl.$viewValue.deviceAliasId = null;
}
scope.updateValidity();
scope.selectedDeviceAliasChange();
}
});
scope.$watch('timeseriesDataKeys', function () {
if (ngModelCtrl.$viewValue) {
var dataKeys = [];
dataKeys = dataKeys.concat(scope.timeseriesDataKeys);
dataKeys = dataKeys.concat(scope.attributeDataKeys);
ngModelCtrl.$viewValue.dataKeys = dataKeys;
scope.updateValidity();
}
}, true);
scope.$watch('attributeDataKeys', function () {
if (ngModelCtrl.$viewValue) {
var dataKeys = [];
dataKeys = dataKeys.concat(scope.timeseriesDataKeys);
dataKeys = dataKeys.concat(scope.attributeDataKeys);
ngModelCtrl.$viewValue.dataKeys = dataKeys;
scope.updateValidity();
}
}, true);
ngModelCtrl.$render = function () {
if (ngModelCtrl.$viewValue) {
var deviceAliasId = ngModelCtrl.$viewValue.deviceAliasId;
if (scope.deviceAliases[deviceAliasId]) {
scope.deviceAlias = {id: deviceAliasId, alias: scope.deviceAliases[deviceAliasId].alias,
deviceId: scope.deviceAliases[deviceAliasId].deviceId};
} else {
scope.deviceAlias = null;
}
var timeseriesDataKeys = [];
var attributeDataKeys = [];
for (var d in ngModelCtrl.$viewValue.dataKeys) {
var dataKey = ngModelCtrl.$viewValue.dataKeys[d];
if (dataKey.type === types.dataKeyType.timeseries) {
timeseriesDataKeys.push(dataKey);
} else if (dataKey.type === types.dataKeyType.attribute) {
attributeDataKeys.push(dataKey);
}
}
scope.timeseriesDataKeys = timeseriesDataKeys;
scope.attributeDataKeys = attributeDataKeys;
}
};
scope.textIsNotEmpty = function(text) {
return (text && text != null && text.length > 0) ? true : false;
}
scope.selectedDeviceAliasChange = function () {
if (!scope.timeseriesDataKeySearchText || scope.timeseriesDataKeySearchText === '') {
scope.timeseriesDataKeySearchText = scope.timeseriesDataKeySearchText === '' ? null : '';
}
if (!scope.attributeDataKeySearchText || scope.attributeDataKeySearchText === '') {
scope.attributeDataKeySearchText = scope.attributeDataKeySearchText === '' ? null : '';
}
};
scope.transformTimeseriesDataKeyChip = function (chip) {
return scope.generateDataKey({chip: chip, type: types.dataKeyType.timeseries});
};
scope.transformAttributeDataKeyChip = function (chip) {
return scope.generateDataKey({chip: chip, type: types.dataKeyType.attribute});
};
scope.showColorPicker = function (event, dataKey) {
$mdColorPicker.show({
value: dataKey.color,
defaultValue: '#fff',
random: tinycolor.random(),
clickOutsideToClose: false,
hasBackdrop: false,
skipHide: true,
preserveScope: false,
mdColorAlphaChannel: true,
mdColorSpectrum: true,
mdColorSliders: true,
mdColorGenericPalette: false,
mdColorMaterialPalette: true,
mdColorHistory: false,
mdColorDefaultTab: 2,
$event: event
}).then(function (color) {
dataKey.color = color;
ngModelCtrl.$setDirty();
});
}
scope.editDataKey = function (event, dataKey, index) {
$mdDialog.show({
controller: 'DatakeyConfigDialogController',
controllerAs: 'vm',
templateUrl: datakeyConfigDialogTemplate,
locals: {
dataKey: angular.copy(dataKey),
dataKeySettingsSchema: scope.datakeySettingsSchema,
deviceAlias: scope.deviceAlias,
deviceAliases: scope.deviceAliases
},
parent: angular.element($document[0].body),
fullscreen: true,
targetEvent: event,
skipHide: true,
onComplete: function () {
var w = angular.element($window);
w.triggerHandler('resize');
}
}).then(function (dataKey) {
if (dataKey.type === types.dataKeyType.timeseries) {
scope.timeseriesDataKeys[index] = dataKey;
} else if (dataKey.type === types.dataKeyType.attribute) {
scope.attributeDataKeys[index] = dataKey;
}
ngModelCtrl.$setDirty();
}, function () {
});
};
scope.dataKeysSearch = function (searchText, type) {
if (scope.deviceAlias) {
var deferred = $q.defer();
scope.fetchDeviceKeys({deviceAliasId: scope.deviceAlias.id, query: searchText, type: type})
.then(function (dataKeys) {
deferred.resolve(dataKeys);
}, function (e) {
deferred.reject(e);
});
return deferred.promise;
} else {
return $q.when([]);
}
};
scope.createKey = function (event, chipsId) {
var chipsChild = $(chipsId, element)[0].firstElementChild;
var el = angular.element(chipsChild);
var chipBuffer = el.scope().$mdChipsCtrl.getChipBuffer();
event.preventDefault();
event.stopPropagation();
el.scope().$mdChipsCtrl.appendChip(chipBuffer.trim());
el.scope().$mdChipsCtrl.resetChipBuffer();
}
$compile(element.contents())(scope);
}
return {
restrict: "E",
require: "^ngModel",
scope: {
widgetType: '=',
deviceAliases: '=',
datakeySettingsSchema: '=',
generateDataKey: '&',
fetchDeviceKeys: '&',
onCreateDeviceAlias: '&'
},
link: linker
};
}
/* eslint-enable angular/angularelement */

137
ui/src/app/components/datasource-device.tpl.html

@ -1,137 +0,0 @@
<!--
Copyright © 2016-2017 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<section flex layout='column' layout-align="center" layout-gt-sm='row' layout-align-gt-sm="start center">
<tb-device-alias-select
tb-required="true"
device-aliases="deviceAliases"
ng-model="deviceAlias"
on-create-device-alias="onCreateDeviceAlias({event: event, alias: alias})">
</tb-device-alias-select>
<section flex layout='column'>
<section flex layout='column' layout-align="center" style="padding-left: 4px;">
<md-chips flex
id="timeseries_datakey_chips"
ng-required="true"
ng-model="timeseriesDataKeys" md-autocomplete-snap
md-transform-chip="transformTimeseriesDataKeyChip($chip)"
md-require-match="false">
<md-autocomplete
md-no-cache="true"
id="timeseries_datakey"
md-selected-item="selectedTimeseriesDataKey"
md-search-text="timeseriesDataKeySearchText"
md-items="item in dataKeysSearch(timeseriesDataKeySearchText, types.dataKeyType.timeseries)"
md-item-text="item.name"
md-min-length="0"
placeholder="{{'datakey.timeseries' | translate }}"
md-menu-class="tb-timeseries-datakey-autocomplete">
<span md-highlight-text="timeseriesDataKeySearchText" md-highlight-flags="^i">{{item}}</span>
<md-not-found>
<div class="tb-not-found">
<div class="tb-no-entries" ng-if="!textIsNotEmpty(timeseriesDataKeySearchText)">
<span translate>device.no-keys-found</span>
</div>
<div ng-if="textIsNotEmpty(timeseriesDataKeySearchText)">
<span translate translate-values='{ key: "{{timeseriesDataKeySearchText | truncate:true:6:&apos;...&apos;}}" }'>device.no-key-matching</span>
<span>
<a translate ng-click="createKey($event, '#timeseries_datakey_chips')">device.create-new-key</a>
</span>
</div>
</div>
</md-not-found>
</md-autocomplete>
<md-chip-template>
<div layout="row" layout-align="start center" class="tb-attribute-chip">
<div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
<div class="tb-color-result" ng-style="{background: $chip.color}"></div>
</div>
<div layout="row" flex>
<div class="tb-chip-label">
{{$chip.label}}
</div>
<div class="tb-chip-separator">: </div>
<div class="tb-chip-label">
<strong ng-if="!$chip.postFuncBody">{{$chip.name}}</strong>
<strong ng-if="$chip.postFuncBody">f({{$chip.name}})</strong>
</div>
</div>
<md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32">
<md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon>
</md-button>
</div>
</md-chip-template>
</md-chips>
<md-chips flex ng-if="widgetType === types.widgetType.latest.value"
id="attribute_datakey_chips"
ng-required="true"
ng-model="attributeDataKeys" md-autocomplete-snap
md-transform-chip="transformAttributeDataKeyChip($chip)"
md-require-match="false">
<md-autocomplete
md-no-cache="true"
id="attribute_datakey"
md-selected-item="selectedAttributeDataKey"
md-search-text="attributeDataKeySearchText"
md-items="item in dataKeysSearch(attributeDataKeySearchText, types.dataKeyType.attribute)"
md-item-text="item.name"
md-min-length="0"
placeholder="{{'datakey.attributes' | translate }}"
md-menu-class="tb-attribute-datakey-autocomplete">
<span md-highlight-text="attributeDataKeySearchText" md-highlight-flags="^i">{{item}}</span>
<md-not-found>
<div class="tb-not-found">
<div class="tb-no-entries" ng-if="!textIsNotEmpty(attributeDataKeySearchText)">
<span translate>device.no-keys-found</span>
</div>
<div ng-if="textIsNotEmpty(attributeDataKeySearchText)">
<span translate translate-values='{ key: "{{attributeDataKeySearchText | truncate:true:6:&apos;...&apos;}}" }'>device.no-key-matching</span>
<span>
<a translate ng-click="createKey($event, '#attribute_datakey_chips')">device.create-new-key</a>
</span>
</div>
</div>
</md-not-found>
</md-autocomplete>
<md-chip-template>
<div layout="row" layout-align="start center" class="tb-attribute-chip">
<div class="tb-color-preview" ng-click="showColorPicker($event, $chip, $index)" style="margin-right: 5px;">
<div class="tb-color-result" ng-style="{background: $chip.color}"></div>
</div>
<div layout="row" flex>
<div class="tb-chip-label">
{{$chip.label}}
</div>
<div class="tb-chip-separator">: </div>
<div class="tb-chip-label">
<strong ng-if="!$chip.postFuncBody">{{$chip.name}}</strong>
<strong ng-if="$chip.postFuncBody">f({{$chip.name}})</strong>
</div>
</div>
<md-button ng-click="editDataKey($event, $chip, $index)" class="md-icon-button tb-md-32">
<md-icon aria-label="edit" class="material-icons tb-md-20">edit</md-icon>
</md-button>
</div>
</md-chip-template>
</md-chips>
</section>
<div class="tb-error-messages" ng-messages="ngModelCtrl.$error" role="alert">
<div translate ng-message="deviceKeys" ng-if="widgetType === types.widgetType.timeseries.value" class="tb-error-message">datakey.timeseries-required</div>
<div translate ng-message="deviceKeys" ng-if="widgetType === types.widgetType.latest.value" class="tb-error-message">datakey.timeseries-or-attributes-required</div>
</div>
</section>
</section>

144
ui/src/app/components/device-alias-select.directive.js

@ -1,144 +0,0 @@
/*
* Copyright © 2016-2017 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import $ from 'jquery';
import './device-alias-select.scss';
/* eslint-disable import/no-unresolved, import/default */
import deviceAliasSelectTemplate from './device-alias-select.tpl.html';
/* eslint-enable import/no-unresolved, import/default */
/* eslint-disable angular/angularelement */
export default angular.module('thingsboard.directives.deviceAliasSelect', [])
.directive('tbDeviceAliasSelect', DeviceAliasSelect)
.name;
/*@ngInject*/
function DeviceAliasSelect($compile, $templateCache, $mdConstant) {
var linker = function (scope, element, attrs, ngModelCtrl) {
var template = $templateCache.get(deviceAliasSelectTemplate);
element.html(template);
scope.tbRequired = angular.isDefined(scope.tbRequired) ? scope.tbRequired : false;
scope.ngModelCtrl = ngModelCtrl;
scope.deviceAliasList = [];
scope.deviceAlias = null;
scope.updateValidity = function () {
var value = ngModelCtrl.$viewValue;
var valid = angular.isDefined(value) && value != null || !scope.tbRequired;
ngModelCtrl.$setValidity('deviceAlias', valid);
};
scope.$watch('deviceAliases', function () {
scope.deviceAliasList = [];
for (var aliasId in scope.deviceAliases) {
var deviceAlias = {id: aliasId, alias: scope.deviceAliases[aliasId].alias, deviceId: scope.deviceAliases[aliasId].deviceId};
scope.deviceAliasList.push(deviceAlias);
}
}, true);
scope.$watch('deviceAlias', function () {
scope.updateView();
});
scope.deviceAliasSearch = function (deviceAliasSearchText) {
return deviceAliasSearchText ? scope.deviceAliasList.filter(
scope.createFilterForDeviceAlias(deviceAliasSearchText)) : scope.deviceAliasList;
};
scope.createFilterForDeviceAlias = function (query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(deviceAlias) {
return (angular.lowercase(deviceAlias.alias).indexOf(lowercaseQuery) === 0);
};
};
scope.updateView = function () {
ngModelCtrl.$setViewValue(scope.deviceAlias);
scope.updateValidity();
}
ngModelCtrl.$render = function () {
if (ngModelCtrl.$viewValue) {
scope.deviceAlias = ngModelCtrl.$viewValue;
}
}
scope.textIsNotEmpty = function(text) {
return (text && text != null && text.length > 0) ? true : false;
}
scope.deviceAliasEnter = function($event) {
if ($event.keyCode === $mdConstant.KEY_CODE.ENTER) {
$event.preventDefault();
if (!scope.deviceAlias) {
var found = scope.deviceAliasSearch(scope.deviceAliasSearchText);
found = found.length > 0;
if (!found) {
scope.createDeviceAlias($event, scope.deviceAliasSearchText);
}
}
}
}
scope.createDeviceAlias = function (event, alias) {
var autoChild = $('#device-autocomplete', element)[0].firstElementChild;
var el = angular.element(autoChild);
el.scope().$mdAutocompleteCtrl.hidden = true;
el.scope().$mdAutocompleteCtrl.hasNotFound = false;
event.preventDefault();
var promise = scope.onCreateDeviceAlias({event: event, alias: alias});
if (promise) {
promise.then(
function success(newAlias) {
el.scope().$mdAutocompleteCtrl.hasNotFound = true;
if (newAlias) {
scope.deviceAliasList.push(newAlias);
scope.deviceAlias = newAlias;
}
},
function fail() {
el.scope().$mdAutocompleteCtrl.hasNotFound = true;
}
);
} else {
el.scope().$mdAutocompleteCtrl.hasNotFound = true;
}
};
$compile(element.contents())(scope);
}
return {
restrict: "E",
require: "^ngModel",
link: linker,
scope: {
tbRequired: '=?',
deviceAliases: '=',
onCreateDeviceAlias: '&'
}
};
}
/* eslint-enable angular/angularelement */

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save