Commit ed3c57d1eacfb82016f9efef52be49609b0cceb5

Authored by Vladyslav_Prykhodko
2 parents b29a610f 9b76d785

Merge remote-tracking branch 'upstream/master' into feature/rule-chain/open-rule-chain-node

Showing 150 changed files with 2130 additions and 1317 deletions
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
18 import com.fasterxml.jackson.databind.ObjectMapper; 19 import com.fasterxml.jackson.databind.ObjectMapper;
19 import com.fasterxml.jackson.databind.node.ArrayNode; 20 import com.fasterxml.jackson.databind.node.ArrayNode;
20 import com.fasterxml.jackson.databind.node.ObjectNode; 21 import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -26,26 +27,27 @@ import org.springframework.beans.factory.annotation.Value; @@ -26,26 +27,27 @@ import org.springframework.beans.factory.annotation.Value;
26 import org.springframework.security.core.Authentication; 27 import org.springframework.security.core.Authentication;
27 import org.springframework.security.core.context.SecurityContextHolder; 28 import org.springframework.security.core.context.SecurityContextHolder;
28 import org.springframework.web.bind.annotation.ExceptionHandler; 29 import org.springframework.web.bind.annotation.ExceptionHandler;
29 -import org.thingsboard.server.common.data.*;  
30 import org.thingsboard.server.common.data.Customer; 30 import org.thingsboard.server.common.data.Customer;
31 import org.thingsboard.server.common.data.Dashboard; 31 import org.thingsboard.server.common.data.Dashboard;
32 import org.thingsboard.server.common.data.DashboardInfo; 32 import org.thingsboard.server.common.data.DashboardInfo;
33 import org.thingsboard.server.common.data.DataConstants; 33 import org.thingsboard.server.common.data.DataConstants;
34 import org.thingsboard.server.common.data.Device; 34 import org.thingsboard.server.common.data.Device;
  35 +import org.thingsboard.server.common.data.DeviceInfo;
35 import org.thingsboard.server.common.data.EntityType; 36 import org.thingsboard.server.common.data.EntityType;
36 import org.thingsboard.server.common.data.EntityView; 37 import org.thingsboard.server.common.data.EntityView;
  38 +import org.thingsboard.server.common.data.EntityViewInfo;
37 import org.thingsboard.server.common.data.HasName; 39 import org.thingsboard.server.common.data.HasName;
38 import org.thingsboard.server.common.data.HasTenantId; 40 import org.thingsboard.server.common.data.HasTenantId;
39 import org.thingsboard.server.common.data.Tenant; 41 import org.thingsboard.server.common.data.Tenant;
40 import org.thingsboard.server.common.data.User; 42 import org.thingsboard.server.common.data.User;
41 import org.thingsboard.server.common.data.alarm.Alarm; 43 import org.thingsboard.server.common.data.alarm.Alarm;
42 -import org.thingsboard.server.common.data.id.AlarmId;  
43 import org.thingsboard.server.common.data.alarm.AlarmInfo; 44 import org.thingsboard.server.common.data.alarm.AlarmInfo;
44 import org.thingsboard.server.common.data.asset.Asset; 45 import org.thingsboard.server.common.data.asset.Asset;
45 import org.thingsboard.server.common.data.asset.AssetInfo; 46 import org.thingsboard.server.common.data.asset.AssetInfo;
46 import org.thingsboard.server.common.data.audit.ActionType; 47 import org.thingsboard.server.common.data.audit.ActionType;
47 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 48 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
48 import org.thingsboard.server.common.data.exception.ThingsboardException; 49 import org.thingsboard.server.common.data.exception.ThingsboardException;
  50 +import org.thingsboard.server.common.data.id.AlarmId;
49 import org.thingsboard.server.common.data.id.AssetId; 51 import org.thingsboard.server.common.data.id.AssetId;
50 import org.thingsboard.server.common.data.id.CustomerId; 52 import org.thingsboard.server.common.data.id.CustomerId;
51 import org.thingsboard.server.common.data.id.DashboardId; 53 import org.thingsboard.server.common.data.id.DashboardId;
@@ -73,7 +75,6 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle; @@ -73,7 +75,6 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle;
73 import org.thingsboard.server.common.msg.TbMsg; 75 import org.thingsboard.server.common.msg.TbMsg;
74 import org.thingsboard.server.common.msg.TbMsgDataType; 76 import org.thingsboard.server.common.msg.TbMsgDataType;
75 import org.thingsboard.server.common.msg.TbMsgMetaData; 77 import org.thingsboard.server.common.msg.TbMsgMetaData;
76 -import org.thingsboard.server.dao.alarm.AlarmService;  
77 import org.thingsboard.server.dao.asset.AssetService; 78 import org.thingsboard.server.dao.asset.AssetService;
78 import org.thingsboard.server.dao.attributes.AttributesService; 79 import org.thingsboard.server.dao.attributes.AttributesService;
79 import org.thingsboard.server.dao.audit.AuditLogService; 80 import org.thingsboard.server.dao.audit.AuditLogService;
@@ -640,6 +641,12 @@ public abstract class BaseController { @@ -640,6 +641,12 @@ public abstract class BaseController {
640 case ALARM_CLEAR: 641 case ALARM_CLEAR:
641 msgType = DataConstants.ALARM_CLEAR; 642 msgType = DataConstants.ALARM_CLEAR;
642 break; 643 break;
  644 + case ASSIGNED_FROM_TENANT:
  645 + msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT;
  646 + break;
  647 + case ASSIGNED_TO_TENANT:
  648 + msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT;
  649 + break;
643 } 650 }
644 if (!StringUtils.isEmpty(msgType)) { 651 if (!StringUtils.isEmpty(msgType)) {
645 try { 652 try {
@@ -659,6 +666,16 @@ public abstract class BaseController { @@ -659,6 +666,16 @@ public abstract class BaseController {
659 String strCustomerName = extractParameter(String.class, 2, additionalInfo); 666 String strCustomerName = extractParameter(String.class, 2, additionalInfo);
660 metaData.putValue("unassignedCustomerId", strCustomerId); 667 metaData.putValue("unassignedCustomerId", strCustomerId);
661 metaData.putValue("unassignedCustomerName", strCustomerName); 668 metaData.putValue("unassignedCustomerName", strCustomerName);
  669 + } else if (actionType == ActionType.ASSIGNED_FROM_TENANT) {
  670 + String strTenantId = extractParameter(String.class, 0, additionalInfo);
  671 + String strTenantName = extractParameter(String.class, 1, additionalInfo);
  672 + metaData.putValue("assignedFromTenantId", strTenantId);
  673 + metaData.putValue("assignedFromTenantName", strTenantName);
  674 + } else if (actionType == ActionType.ASSIGNED_TO_TENANT) {
  675 + String strTenantId = extractParameter(String.class, 0, additionalInfo);
  676 + String strTenantName = extractParameter(String.class, 1, additionalInfo);
  677 + metaData.putValue("assignedToTenantId", strTenantId);
  678 + metaData.putValue("assignedToTenantName", strTenantName);
662 } 679 }
663 ObjectNode entityNode; 680 ObjectNode entityNode;
664 if (entity != null) { 681 if (entity != null) {
@@ -722,5 +739,13 @@ public abstract class BaseController { @@ -722,5 +739,13 @@ public abstract class BaseController {
722 return result; 739 return result;
723 } 740 }
724 741
  742 + protected <E extends HasName> String entityToStr(E entity) {
  743 + try {
  744 + return json.writeValueAsString(json.valueToTree(entity));
  745 + } catch (JsonProcessingException e) {
  746 + log.warn("[{}] Failed to convert entity to string!", entity, e);
  747 + }
  748 + return null;
  749 + }
725 750
726 } 751 }
@@ -40,8 +40,10 @@ import org.thingsboard.server.common.data.Device; @@ -40,8 +40,10 @@ import org.thingsboard.server.common.data.Device;
40 import org.thingsboard.server.common.data.DeviceInfo; 40 import org.thingsboard.server.common.data.DeviceInfo;
41 import org.thingsboard.server.common.data.EntitySubtype; 41 import org.thingsboard.server.common.data.EntitySubtype;
42 import org.thingsboard.server.common.data.EntityType; 42 import org.thingsboard.server.common.data.EntityType;
  43 +import org.thingsboard.server.common.data.Tenant;
43 import org.thingsboard.server.common.data.audit.ActionType; 44 import org.thingsboard.server.common.data.audit.ActionType;
44 import org.thingsboard.server.common.data.device.DeviceSearchQuery; 45 import org.thingsboard.server.common.data.device.DeviceSearchQuery;
  46 +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
45 import org.thingsboard.server.common.data.exception.ThingsboardException; 47 import org.thingsboard.server.common.data.exception.ThingsboardException;
46 import org.thingsboard.server.common.data.id.CustomerId; 48 import org.thingsboard.server.common.data.id.CustomerId;
47 import org.thingsboard.server.common.data.id.DeviceId; 49 import org.thingsboard.server.common.data.id.DeviceId;
@@ -49,6 +51,9 @@ import org.thingsboard.server.common.data.id.TenantId; @@ -49,6 +51,9 @@ import org.thingsboard.server.common.data.id.TenantId;
49 import org.thingsboard.server.common.data.page.PageData; 51 import org.thingsboard.server.common.data.page.PageData;
50 import org.thingsboard.server.common.data.page.PageLink; 52 import org.thingsboard.server.common.data.page.PageLink;
51 import org.thingsboard.server.common.data.security.DeviceCredentials; 53 import org.thingsboard.server.common.data.security.DeviceCredentials;
  54 +import org.thingsboard.server.common.msg.TbMsg;
  55 +import org.thingsboard.server.common.msg.TbMsgDataType;
  56 +import org.thingsboard.server.common.msg.TbMsgMetaData;
52 import org.thingsboard.server.dao.device.claim.ClaimResponse; 57 import org.thingsboard.server.dao.device.claim.ClaimResponse;
53 import org.thingsboard.server.dao.device.claim.ClaimResult; 58 import org.thingsboard.server.dao.device.claim.ClaimResult;
54 import org.thingsboard.server.dao.exception.IncorrectParameterException; 59 import org.thingsboard.server.dao.exception.IncorrectParameterException;
@@ -71,6 +76,7 @@ public class DeviceController extends BaseController { @@ -71,6 +76,7 @@ public class DeviceController extends BaseController {
71 76
72 private static final String DEVICE_ID = "deviceId"; 77 private static final String DEVICE_ID = "deviceId";
73 private static final String DEVICE_NAME = "deviceName"; 78 private static final String DEVICE_NAME = "deviceName";
  79 + private static final String TENANT_ID = "tenantId";
74 80
75 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") 81 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
76 @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET) 82 @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET)
@@ -547,4 +553,54 @@ public class DeviceController extends BaseController { @@ -547,4 +553,54 @@ public class DeviceController extends BaseController {
547 } 553 }
548 return DataConstants.DEFAULT_SECRET_KEY; 554 return DataConstants.DEFAULT_SECRET_KEY;
549 } 555 }
  556 +
  557 + @PreAuthorize("hasAuthority('TENANT_ADMIN')")
  558 + @RequestMapping(value = "/tenant/{tenantId}/device/{deviceId}", method = RequestMethod.POST)
  559 + @ResponseBody
  560 + public Device assignDeviceToTenant(@PathVariable(TENANT_ID) String strTenantId,
  561 + @PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
  562 + checkParameter(TENANT_ID, strTenantId);
  563 + checkParameter(DEVICE_ID, strDeviceId);
  564 + try {
  565 + DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
  566 + Device device = checkDeviceId(deviceId, Operation.ASSIGN_TO_TENANT);
  567 +
  568 + TenantId newTenantId = new TenantId(toUUID(strTenantId));
  569 + Tenant newTenant = tenantService.findTenantById(newTenantId);
  570 + if (newTenant == null) {
  571 + throw new ThingsboardException("Could not find the specified Tenant!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
  572 + }
  573 +
  574 + Device assignedDevice = deviceService.assignDeviceToTenant(newTenantId, device);
  575 +
  576 + logEntityAction(getCurrentUser(), deviceId, assignedDevice,
  577 + assignedDevice.getCustomerId(),
  578 + ActionType.ASSIGNED_TO_TENANT, null, strTenantId, newTenant.getName());
  579 +
  580 + Tenant currentTenant = tenantService.findTenantById(getTenantId());
  581 + pushAssignedFromNotification(currentTenant, newTenantId, assignedDevice);
  582 +
  583 + return assignedDevice;
  584 + } catch (Exception e) {
  585 + logEntityAction(getCurrentUser(), emptyId(EntityType.DEVICE), null,
  586 + null,
  587 + ActionType.ASSIGNED_TO_TENANT, e, strTenantId);
  588 + throw handleException(e);
  589 + }
  590 + }
  591 +
  592 + private void pushAssignedFromNotification(Tenant currentTenant, TenantId newTenantId, Device assignedDevice) {
  593 + String data = entityToStr(assignedDevice);
  594 + if (data != null) {
  595 + TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_ASSIGNED_FROM_TENANT, assignedDevice.getId(), getMetaDataForAssignedFrom(currentTenant), TbMsgDataType.JSON, data);
  596 + tbClusterService.pushMsgToRuleEngine(newTenantId, assignedDevice.getId(), tbMsg, null);
  597 + }
  598 + }
  599 +
  600 + private TbMsgMetaData getMetaDataForAssignedFrom(Tenant tenant) {
  601 + TbMsgMetaData metaData = new TbMsgMetaData();
  602 + metaData.putValue("assignedFromTenantId", tenant.getId().getId().toString());
  603 + metaData.putValue("assignedFromTenantName", tenant.getName());
  604 + return metaData;
  605 + }
550 } 606 }
@@ -28,7 +28,9 @@ import org.thingsboard.server.service.install.DatabaseTsUpgradeService; @@ -28,7 +28,9 @@ import org.thingsboard.server.service.install.DatabaseTsUpgradeService;
28 import org.thingsboard.server.service.install.EntityDatabaseSchemaService; 28 import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
29 import org.thingsboard.server.service.install.SystemDataLoaderService; 29 import org.thingsboard.server.service.install.SystemDataLoaderService;
30 import org.thingsboard.server.service.install.TsDatabaseSchemaService; 30 import org.thingsboard.server.service.install.TsDatabaseSchemaService;
  31 +import org.thingsboard.server.service.install.TsLatestDatabaseSchemaService;
31 import org.thingsboard.server.service.install.migrate.EntitiesMigrateService; 32 import org.thingsboard.server.service.install.migrate.EntitiesMigrateService;
  33 +import org.thingsboard.server.service.install.migrate.TsLatestMigrateService;
32 import org.thingsboard.server.service.install.update.DataUpdateService; 34 import org.thingsboard.server.service.install.update.DataUpdateService;
33 35
34 @Service 36 @Service
@@ -51,6 +53,9 @@ public class ThingsboardInstallService { @@ -51,6 +53,9 @@ public class ThingsboardInstallService {
51 @Autowired 53 @Autowired
52 private TsDatabaseSchemaService tsDatabaseSchemaService; 54 private TsDatabaseSchemaService tsDatabaseSchemaService;
53 55
  56 + @Autowired(required = false)
  57 + private TsLatestDatabaseSchemaService tsLatestDatabaseSchemaService;
  58 +
54 @Autowired 59 @Autowired
55 private DatabaseEntitiesUpgradeService databaseEntitiesUpgradeService; 60 private DatabaseEntitiesUpgradeService databaseEntitiesUpgradeService;
56 61
@@ -72,6 +77,9 @@ public class ThingsboardInstallService { @@ -72,6 +77,9 @@ public class ThingsboardInstallService {
72 @Autowired(required = false) 77 @Autowired(required = false)
73 private EntitiesMigrateService entitiesMigrateService; 78 private EntitiesMigrateService entitiesMigrateService;
74 79
  80 + @Autowired(required = false)
  81 + private TsLatestMigrateService latestMigrateService;
  82 +
75 public void performInstall() { 83 public void performInstall() {
76 try { 84 try {
77 if (isUpgrade) { 85 if (isUpgrade) {
@@ -82,6 +90,10 @@ public class ThingsboardInstallService { @@ -82,6 +90,10 @@ public class ThingsboardInstallService {
82 entitiesMigrateService.migrate(); 90 entitiesMigrateService.migrate();
83 log.info("Updating system data..."); 91 log.info("Updating system data...");
84 systemDataLoaderService.updateSystemWidgets(); 92 systemDataLoaderService.updateSystemWidgets();
  93 + } else if ("3.0.1-cassandra".equals(upgradeFromVersion)) {
  94 + log.info("Migrating ThingsBoard latest timeseries data from cassandra to SQL database ...");
  95 + latestMigrateService.migrate();
  96 + log.info("Updating system data...");
85 } else { 97 } else {
86 switch (upgradeFromVersion) { 98 switch (upgradeFromVersion) {
87 case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion 99 case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
@@ -181,6 +193,10 @@ public class ThingsboardInstallService { @@ -181,6 +193,10 @@ public class ThingsboardInstallService {
181 193
182 tsDatabaseSchemaService.createDatabaseSchema(); 194 tsDatabaseSchemaService.createDatabaseSchema();
183 195
  196 + if (tsLatestDatabaseSchemaService != null) {
  197 + tsLatestDatabaseSchemaService.createDatabaseSchema();
  198 + }
  199 +
184 log.info("Loading system data..."); 200 log.info("Loading system data...");
185 201
186 componentDiscoveryService.discoverComponents(); 202 componentDiscoveryService.discoverComponents();
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install;
  17 +
  18 +import org.springframework.context.annotation.Profile;
  19 +import org.springframework.stereotype.Service;
  20 +import org.thingsboard.server.dao.util.NoSqlTsLatestDao;
  21 +
  22 +@Service
  23 +@NoSqlTsLatestDao
  24 +@Profile("install")
  25 +public class CassandraTsLatestDatabaseSchemaService extends CassandraAbstractDatabaseSchemaService
  26 + implements TsLatestDatabaseSchemaService {
  27 + public CassandraTsLatestDatabaseSchemaService() {
  28 + super("schema-ts-latest.cql");
  29 + }
  30 +}
@@ -18,11 +18,9 @@ package org.thingsboard.server.service.install; @@ -18,11 +18,9 @@ package org.thingsboard.server.service.install;
18 import org.springframework.context.annotation.Profile; 18 import org.springframework.context.annotation.Profile;
19 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
20 import org.thingsboard.server.dao.util.HsqlDao; 20 import org.thingsboard.server.dao.util.HsqlDao;
21 -import org.thingsboard.server.dao.util.SqlDao;  
22 21
23 @Service 22 @Service
24 @HsqlDao 23 @HsqlDao
25 -@SqlDao  
26 @Profile("install") 24 @Profile("install")
27 public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService 25 public class HsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
28 implements EntityDatabaseSchemaService { 26 implements EntityDatabaseSchemaService {
@@ -18,10 +18,8 @@ package org.thingsboard.server.service.install; @@ -18,10 +18,8 @@ package org.thingsboard.server.service.install;
18 import org.springframework.context.annotation.Profile; 18 import org.springframework.context.annotation.Profile;
19 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
20 import org.thingsboard.server.dao.util.PsqlDao; 20 import org.thingsboard.server.dao.util.PsqlDao;
21 -import org.thingsboard.server.dao.util.SqlDao;  
22 21
23 @Service 22 @Service
24 -@SqlDao  
25 @PsqlDao 23 @PsqlDao
26 @Profile("install") 24 @Profile("install")
27 public class PsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService 25 public class PsqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
@@ -21,7 +21,6 @@ import org.springframework.beans.factory.annotation.Value; @@ -21,7 +21,6 @@ import org.springframework.beans.factory.annotation.Value;
21 import org.springframework.context.annotation.Profile; 21 import org.springframework.context.annotation.Profile;
22 import org.springframework.stereotype.Service; 22 import org.springframework.stereotype.Service;
23 import org.thingsboard.server.dao.dashboard.DashboardService; 23 import org.thingsboard.server.dao.dashboard.DashboardService;
24 -import org.thingsboard.server.dao.util.SqlDao;  
25 import org.thingsboard.server.service.install.sql.SqlDbHelper; 24 import org.thingsboard.server.service.install.sql.SqlDbHelper;
26 25
27 import java.nio.charset.Charset; 26 import java.nio.charset.Charset;
@@ -58,7 +57,6 @@ import static org.thingsboard.server.service.install.DatabaseHelper.TYPE; @@ -58,7 +57,6 @@ import static org.thingsboard.server.service.install.DatabaseHelper.TYPE;
58 @Service 57 @Service
59 @Profile("install") 58 @Profile("install")
60 @Slf4j 59 @Slf4j
61 -@SqlDao  
62 public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService { 60 public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService {
63 61
64 private static final String SCHEMA_UPDATE_SQL = "schema_update.sql"; 62 private static final String SCHEMA_UPDATE_SQL = "schema_update.sql";
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install;
  17 +
  18 +public interface TsLatestDatabaseSchemaService extends DatabaseSchemaService {
  19 +}
@@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.EntityType; @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.EntityType;
24 import org.thingsboard.server.common.data.UUIDConverter; 24 import org.thingsboard.server.common.data.UUIDConverter;
25 import org.thingsboard.server.dao.cassandra.CassandraCluster; 25 import org.thingsboard.server.dao.cassandra.CassandraCluster;
26 import org.thingsboard.server.dao.util.NoSqlAnyDao; 26 import org.thingsboard.server.dao.util.NoSqlAnyDao;
27 -import org.thingsboard.server.dao.util.SqlDao;  
28 import org.thingsboard.server.service.install.EntityDatabaseSchemaService; 27 import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
29 28
30 import java.sql.Connection; 29 import java.sql.Connection;
@@ -42,7 +41,6 @@ import static org.thingsboard.server.service.install.migrate.CassandraToSqlColum @@ -42,7 +41,6 @@ import static org.thingsboard.server.service.install.migrate.CassandraToSqlColum
42 41
43 @Service 42 @Service
44 @Profile("install") 43 @Profile("install")
45 -@SqlDao  
46 @NoSqlAnyDao 44 @NoSqlAnyDao
47 @Slf4j 45 @Slf4j
48 public class CassandraEntitiesToSqlMigrateService implements EntitiesMigrateService { 46 public class CassandraEntitiesToSqlMigrateService implements EntitiesMigrateService {
@@ -128,7 +128,7 @@ public class CassandraToSqlTable { @@ -128,7 +128,7 @@ public class CassandraToSqlTable {
128 return this.validateColumnData(data); 128 return this.validateColumnData(data);
129 } 129 }
130 130
131 - private CassandraToSqlColumnData[] validateColumnData(CassandraToSqlColumnData[] data) { 131 + protected CassandraToSqlColumnData[] validateColumnData(CassandraToSqlColumnData[] data) {
132 for (int i=0;i<data.length;i++) { 132 for (int i=0;i<data.length;i++) {
133 CassandraToSqlColumn column = this.columns.get(i); 133 CassandraToSqlColumn column = this.columns.get(i);
134 if (column.getType() == CassandraToSqlColumnType.STRING) { 134 if (column.getType() == CassandraToSqlColumnType.STRING) {
@@ -148,7 +148,7 @@ public class CassandraToSqlTable { @@ -148,7 +148,7 @@ public class CassandraToSqlTable {
148 return data; 148 return data;
149 } 149 }
150 150
151 - private void batchInsert(List<CassandraToSqlColumnData[]> batchData, Connection conn) throws SQLException { 151 + protected void batchInsert(List<CassandraToSqlColumnData[]> batchData, Connection conn) throws SQLException {
152 boolean retry = false; 152 boolean retry = false;
153 for (CassandraToSqlColumnData[] data : batchData) { 153 for (CassandraToSqlColumnData[] data : batchData) {
154 for (CassandraToSqlColumn column: this.columns) { 154 for (CassandraToSqlColumn column: this.columns) {
@@ -269,7 +269,7 @@ public class CassandraToSqlTable { @@ -269,7 +269,7 @@ public class CassandraToSqlTable {
269 return Optional.empty(); 269 return Optional.empty();
270 } 270 }
271 271
272 - private Statement createCassandraSelectStatement() { 272 + protected Statement createCassandraSelectStatement() {
273 StringBuilder selectStatementBuilder = new StringBuilder(); 273 StringBuilder selectStatementBuilder = new StringBuilder();
274 selectStatementBuilder.append("SELECT "); 274 selectStatementBuilder.append("SELECT ");
275 for (CassandraToSqlColumn column : columns) { 275 for (CassandraToSqlColumn column : columns) {
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install.migrate;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.apache.commons.lang3.StringUtils;
  20 +import org.hibernate.exception.ConstraintViolationException;
  21 +import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.beans.factory.annotation.Value;
  23 +import org.springframework.context.annotation.Profile;
  24 +import org.springframework.stereotype.Service;
  25 +import org.thingsboard.server.common.data.UUIDConverter;
  26 +import org.thingsboard.server.dao.cassandra.CassandraCluster;
  27 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;
  28 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;
  29 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
  30 +import org.thingsboard.server.dao.sqlts.dictionary.TsKvDictionaryRepository;
  31 +import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository;
  32 +import org.thingsboard.server.dao.util.NoSqlAnyDao;
  33 +import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
  34 +
  35 +import java.sql.Connection;
  36 +import java.sql.DriverManager;
  37 +import java.util.Arrays;
  38 +import java.util.List;
  39 +import java.util.Optional;
  40 +import java.util.concurrent.ConcurrentHashMap;
  41 +import java.util.concurrent.ConcurrentMap;
  42 +import java.util.concurrent.locks.ReentrantLock;
  43 +import java.util.stream.Collectors;
  44 +
  45 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.bigintColumn;
  46 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.booleanColumn;
  47 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.doubleColumn;
  48 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.idColumn;
  49 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.jsonColumn;
  50 +import static org.thingsboard.server.service.install.migrate.CassandraToSqlColumn.stringColumn;
  51 +
  52 +@Service
  53 +@Profile("install")
  54 +@NoSqlAnyDao
  55 +@Slf4j
  56 +public class CassandraTsLatestToSqlMigrateService implements TsLatestMigrateService {
  57 +
  58 + @Autowired
  59 + private EntityDatabaseSchemaService entityDatabaseSchemaService;
  60 +
  61 + @Autowired
  62 + private InsertLatestTsRepository insertLatestTsRepository;
  63 +
  64 + @Autowired
  65 + protected CassandraCluster cluster;
  66 +
  67 + @Autowired
  68 + protected TsKvDictionaryRepository dictionaryRepository;
  69 +
  70 + @Value("${spring.datasource.url}")
  71 + protected String dbUrl;
  72 +
  73 + @Value("${spring.datasource.username}")
  74 + protected String dbUserName;
  75 +
  76 + @Value("${spring.datasource.password}")
  77 + protected String dbPassword;
  78 +
  79 + private final ConcurrentMap<String, Integer> tsKvDictionaryMap = new ConcurrentHashMap<>();
  80 +
  81 + protected static final ReentrantLock tsCreationLock = new ReentrantLock();
  82 +
  83 + @Override
  84 + public void migrate() throws Exception {
  85 + log.info("Performing migration of latest timeseries data from cassandra to SQL database ...");
  86 + entityDatabaseSchemaService.createDatabaseSchema(false);
  87 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  88 + conn.setAutoCommit(false);
  89 + for (CassandraToSqlTable table : tables) {
  90 + table.migrateToSql(cluster.getSession(), conn);
  91 + }
  92 + } catch (Exception e) {
  93 + log.error("Unexpected error during ThingsBoard entities data migration!", e);
  94 + throw e;
  95 + }
  96 + entityDatabaseSchemaService.createDatabaseIndexes();
  97 + }
  98 +
  99 + private List<CassandraToSqlTable> tables = Arrays.asList(
  100 + new CassandraToSqlTable("ts_kv_latest_cf",
  101 + idColumn("entity_id"),
  102 + stringColumn("key"),
  103 + bigintColumn("ts"),
  104 + booleanColumn("bool_v"),
  105 + stringColumn("str_v"),
  106 + bigintColumn("long_v"),
  107 + doubleColumn("dbl_v"),
  108 + jsonColumn("json_v")) {
  109 +
  110 + @Override
  111 + protected void batchInsert(List<CassandraToSqlColumnData[]> batchData, Connection conn) {
  112 + insertLatestTsRepository
  113 + .saveOrUpdate(batchData.stream().map(data -> getTsKvLatestEntity(data)).collect(Collectors.toList()));
  114 + }
  115 +
  116 + @Override
  117 + protected CassandraToSqlColumnData[] validateColumnData(CassandraToSqlColumnData[] data) {
  118 + return data;
  119 + }
  120 + });
  121 +
  122 + private TsKvLatestEntity getTsKvLatestEntity(CassandraToSqlColumnData[] data) {
  123 + TsKvLatestEntity latestEntity = new TsKvLatestEntity();
  124 + latestEntity.setEntityId(UUIDConverter.fromString(data[0].getValue()));
  125 + latestEntity.setKey(getOrSaveKeyId(data[1].getValue()));
  126 + latestEntity.setTs(Long.parseLong(data[2].getValue()));
  127 +
  128 + String strV = data[4].getValue();
  129 + if (strV != null) {
  130 + latestEntity.setStrValue(strV);
  131 + } else {
  132 + Long longV = null;
  133 + try {
  134 + longV = Long.parseLong(data[5].getValue());
  135 + } catch (Exception e) {
  136 + }
  137 + if (longV != null) {
  138 + latestEntity.setLongValue(longV);
  139 + } else {
  140 + Double doubleV = null;
  141 + try {
  142 + doubleV = Double.parseDouble(data[6].getValue());
  143 + } catch (Exception e) {
  144 + }
  145 + if (doubleV != null) {
  146 + latestEntity.setDoubleValue(doubleV);
  147 + } else {
  148 +
  149 + String jsonV = data[7].getValue();
  150 + if (StringUtils.isNoneEmpty(jsonV)) {
  151 + latestEntity.setJsonValue(jsonV);
  152 + } else {
  153 + Boolean boolV = null;
  154 + try {
  155 + boolV = Boolean.parseBoolean(data[3].getValue());
  156 + } catch (Exception e) {
  157 + }
  158 + if (boolV != null) {
  159 + latestEntity.setBooleanValue(boolV);
  160 + } else {
  161 + log.warn("All values in key-value row are nullable ");
  162 + }
  163 + }
  164 + }
  165 + }
  166 + }
  167 + return latestEntity;
  168 + }
  169 +
  170 + protected Integer getOrSaveKeyId(String strKey) {
  171 + Integer keyId = tsKvDictionaryMap.get(strKey);
  172 + if (keyId == null) {
  173 + Optional<TsKvDictionary> tsKvDictionaryOptional;
  174 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  175 + if (!tsKvDictionaryOptional.isPresent()) {
  176 + tsCreationLock.lock();
  177 + try {
  178 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  179 + if (!tsKvDictionaryOptional.isPresent()) {
  180 + TsKvDictionary tsKvDictionary = new TsKvDictionary();
  181 + tsKvDictionary.setKey(strKey);
  182 + try {
  183 + TsKvDictionary saved = dictionaryRepository.save(tsKvDictionary);
  184 + tsKvDictionaryMap.put(saved.getKey(), saved.getKeyId());
  185 + keyId = saved.getKeyId();
  186 + } catch (ConstraintViolationException e) {
  187 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  188 + TsKvDictionary dictionary = tsKvDictionaryOptional.orElseThrow(() -> new RuntimeException("Failed to get TsKvDictionary entity from DB!"));
  189 + tsKvDictionaryMap.put(dictionary.getKey(), dictionary.getKeyId());
  190 + keyId = dictionary.getKeyId();
  191 + }
  192 + } else {
  193 + keyId = tsKvDictionaryOptional.get().getKeyId();
  194 + }
  195 + } finally {
  196 + tsCreationLock.unlock();
  197 + }
  198 + } else {
  199 + keyId = tsKvDictionaryOptional.get().getKeyId();
  200 + tsKvDictionaryMap.put(strKey, keyId);
  201 + }
  202 + }
  203 + return keyId;
  204 + }
  205 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install.migrate;
  17 +
  18 +public interface TsLatestMigrateService {
  19 +
  20 + void migrate() throws Exception;
  21 +}
@@ -28,7 +28,6 @@ import org.thingsboard.server.common.msg.TbActorMsg; @@ -28,7 +28,6 @@ import org.thingsboard.server.common.msg.TbActorMsg;
28 import org.thingsboard.server.common.msg.queue.ServiceType; 28 import org.thingsboard.server.common.msg.queue.ServiceType;
29 import org.thingsboard.server.common.msg.queue.TbCallback; 29 import org.thingsboard.server.common.msg.queue.TbCallback;
30 import org.thingsboard.server.dao.util.mapping.JacksonUtil; 30 import org.thingsboard.server.dao.util.mapping.JacksonUtil;
31 -import org.thingsboard.server.gen.transport.TransportProtos;  
32 import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto; 31 import org.thingsboard.server.gen.transport.TransportProtos.DeviceStateServiceMsgProto;
33 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto; 32 import org.thingsboard.server.gen.transport.TransportProtos.FromDeviceRPCResponseProto;
34 import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto; 33 import org.thingsboard.server.gen.transport.TransportProtos.LocalSubscriptionServiceMsgProto;
@@ -61,7 +60,6 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra @@ -61,7 +60,6 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra
61 60
62 import javax.annotation.PostConstruct; 61 import javax.annotation.PostConstruct;
63 import javax.annotation.PreDestroy; 62 import javax.annotation.PreDestroy;
64 -import java.util.ArrayList;  
65 import java.util.List; 63 import java.util.List;
66 import java.util.Optional; 64 import java.util.Optional;
67 import java.util.UUID; 65 import java.util.UUID;
@@ -18,6 +18,6 @@ package org.thingsboard.server.service.security.permission; @@ -18,6 +18,6 @@ package org.thingsboard.server.service.security.permission;
18 public enum Operation { 18 public enum Operation {
19 19
20 ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL, 20 ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL,
21 - READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES 21 + READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES, ASSIGN_TO_TENANT
22 22
23 } 23 }
@@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Value; @@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Value;
20 import org.springframework.scheduling.annotation.Scheduled; 20 import org.springframework.scheduling.annotation.Scheduled;
21 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
22 import org.thingsboard.server.dao.util.PsqlDao; 22 import org.thingsboard.server.dao.util.PsqlDao;
23 -import org.thingsboard.server.dao.util.SqlDao;  
24 import org.thingsboard.server.service.ttl.AbstractCleanUpService; 23 import org.thingsboard.server.service.ttl.AbstractCleanUpService;
25 24
26 import java.sql.Connection; 25 import java.sql.Connection;
@@ -28,7 +27,6 @@ import java.sql.DriverManager; @@ -28,7 +27,6 @@ import java.sql.DriverManager;
28 import java.sql.SQLException; 27 import java.sql.SQLException;
29 28
30 @PsqlDao 29 @PsqlDao
31 -@SqlDao  
32 @Slf4j 30 @Slf4j
33 @Service 31 @Service
34 public class EventsCleanUpService extends AbstractCleanUpService { 32 public class EventsCleanUpService extends AbstractCleanUpService {
@@ -19,11 +19,13 @@ import lombok.extern.slf4j.Slf4j; @@ -19,11 +19,13 @@ import lombok.extern.slf4j.Slf4j;
19 import org.springframework.beans.factory.annotation.Value; 19 import org.springframework.beans.factory.annotation.Value;
20 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
21 import org.thingsboard.server.dao.model.ModelConstants; 21 import org.thingsboard.server.dao.model.ModelConstants;
22 -import org.thingsboard.server.dao.util.PsqlTsDao; 22 +import org.thingsboard.server.dao.util.PsqlDao;
  23 +import org.thingsboard.server.dao.util.SqlTsDao;
23 24
24 import java.sql.Connection; 25 import java.sql.Connection;
25 26
26 -@PsqlTsDao 27 +@SqlTsDao
  28 +@PsqlDao
27 @Service 29 @Service
28 @Slf4j 30 @Slf4j
29 public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService { 31 public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpService {
@@ -33,9 +35,9 @@ public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpServi @@ -33,9 +35,9 @@ public class PsqlTimeseriesCleanUpService extends AbstractTimeseriesCleanUpServi
33 35
34 @Override 36 @Override
35 protected void doCleanUp(Connection connection) { 37 protected void doCleanUp(Connection connection) {
36 - long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);");  
37 - log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved);  
38 - long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);");  
39 - log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved); 38 + long totalPartitionsRemoved = executeQuery(connection, "call drop_partitions_by_max_ttl('" + partitionType + "'," + systemTtl + ", 0);");
  39 + log.info("Total partitions removed by TTL: [{}]", totalPartitionsRemoved);
  40 + long totalEntitiesTelemetryRemoved = executeQuery(connection, "call cleanup_timeseries_by_ttl('" + ModelConstants.NULL_UUID + "'," + systemTtl + ", 0);");
  41 + log.info("Total telemetry removed stats by TTL for entities: [{}]", totalEntitiesTelemetryRemoved);
40 } 42 }
41 } 43 }
@@ -178,8 +178,8 @@ database: @@ -178,8 +178,8 @@ database:
178 ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records 178 ts_max_intervals: "${DATABASE_TS_MAX_INTERVALS:700}" # Max number of DB queries generated by single API call to fetch telemetry records
179 ts: 179 ts:
180 type: "${DATABASE_TS_TYPE:sql}" # cassandra, sql, or timescale (for hybrid mode, DATABASE_TS_TYPE value should be cassandra, or timescale) 180 type: "${DATABASE_TS_TYPE:sql}" # cassandra, sql, or timescale (for hybrid mode, DATABASE_TS_TYPE value should be cassandra, or timescale)
181 - latest_ts:  
182 - type: "${DATABASE_TS_TYPE:sql}" # cassandra, sql, or timescale (for hybrid mode, DATABASE_TS_TYPE value should be cassandra, or timescale) 181 + ts_latest:
  182 + type: "${DATABASE_TS_LATEST_TYPE:sql}" # cassandra, sql, or timescale (for hybrid mode, DATABASE_TS_TYPE value should be cassandra, or timescale)
183 183
184 # note: timescale works only with postgreSQL database for DATABASE_ENTITIES_TYPE. 184 # note: timescale works only with postgreSQL database for DATABASE_ENTITIES_TYPE.
185 185
@@ -15,74 +15,17 @@ @@ -15,74 +15,17 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
18 -import com.fasterxml.jackson.core.type.TypeReference;  
19 -import com.fasterxml.jackson.databind.JsonNode;  
20 -import com.fasterxml.jackson.databind.ObjectMapper;  
21 -import io.jsonwebtoken.Claims;  
22 -import io.jsonwebtoken.Header;  
23 -import io.jsonwebtoken.Jwt;  
24 -import io.jsonwebtoken.Jwts;  
25 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
26 -import org.apache.commons.lang3.StringUtils;  
27 -import org.hamcrest.Matcher;  
28 -import org.junit.After;  
29 -import org.junit.Assert;  
30 -import org.junit.Before;  
31 -import org.junit.Rule;  
32 -import org.junit.rules.TestRule;  
33 -import org.junit.rules.TestWatcher;  
34 -import org.junit.runner.Description;  
35 import org.junit.runner.RunWith; 19 import org.junit.runner.RunWith;
36 -import org.springframework.beans.factory.annotation.Autowired;  
37 import org.springframework.boot.test.context.SpringBootContextLoader; 20 import org.springframework.boot.test.context.SpringBootContextLoader;
38 import org.springframework.boot.test.context.SpringBootTest; 21 import org.springframework.boot.test.context.SpringBootTest;
39 import org.springframework.context.annotation.ComponentScan; 22 import org.springframework.context.annotation.ComponentScan;
40 import org.springframework.context.annotation.Configuration; 23 import org.springframework.context.annotation.Configuration;
41 -import org.springframework.http.HttpHeaders;  
42 -import org.springframework.http.MediaType;  
43 -import org.springframework.http.converter.HttpMessageConverter;  
44 -import org.springframework.http.converter.StringHttpMessageConverter;  
45 -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;  
46 -import org.springframework.mock.http.MockHttpInputMessage;  
47 -import org.springframework.mock.http.MockHttpOutputMessage;  
48 import org.springframework.test.annotation.DirtiesContext; 24 import org.springframework.test.annotation.DirtiesContext;
49 import org.springframework.test.context.ActiveProfiles; 25 import org.springframework.test.context.ActiveProfiles;
50 import org.springframework.test.context.ContextConfiguration; 26 import org.springframework.test.context.ContextConfiguration;
51 import org.springframework.test.context.junit4.SpringRunner; 27 import org.springframework.test.context.junit4.SpringRunner;
52 import org.springframework.test.context.web.WebAppConfiguration; 28 import org.springframework.test.context.web.WebAppConfiguration;
53 -import org.springframework.test.web.servlet.MockMvc;  
54 -import org.springframework.test.web.servlet.MvcResult;  
55 -import org.springframework.test.web.servlet.ResultActions;  
56 -import org.springframework.test.web.servlet.ResultMatcher;  
57 -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;  
58 -import org.springframework.util.LinkedMultiValueMap;  
59 -import org.springframework.util.MultiValueMap;  
60 -import org.springframework.web.context.WebApplicationContext;  
61 -import org.thingsboard.server.common.data.BaseData;  
62 -import org.thingsboard.server.common.data.Customer;  
63 -import org.thingsboard.server.common.data.Tenant;  
64 -import org.thingsboard.server.common.data.User;  
65 -import org.thingsboard.server.common.data.id.TenantId;  
66 -import org.thingsboard.server.common.data.id.UUIDBased;  
67 -import org.thingsboard.server.common.data.page.PageLink;  
68 -import org.thingsboard.server.common.data.page.SortOrder;  
69 -import org.thingsboard.server.common.data.page.TimePageLink;  
70 -import org.thingsboard.server.common.data.security.Authority;  
71 -import org.thingsboard.server.config.ThingsboardSecurityConfiguration;  
72 -import org.thingsboard.server.service.mail.TestMailService;  
73 -import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRequest;  
74 -import org.thingsboard.server.service.security.auth.rest.LoginRequest;  
75 -  
76 -import java.io.IOException;  
77 -import java.util.ArrayList;  
78 -import java.util.Arrays;  
79 -import java.util.Comparator;  
80 -import java.util.List;  
81 -  
82 -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;  
83 -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;  
84 -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;  
85 -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;  
86 29
87 @ActiveProfiles("test") 30 @ActiveProfiles("test")
88 @RunWith(SpringRunner.class) 31 @RunWith(SpringRunner.class)
@@ -218,6 +218,7 @@ public abstract class AbstractWebTest { @@ -218,6 +218,7 @@ public abstract class AbstractWebTest {
218 } 218 }
219 219
220 private Tenant savedDifferentTenant; 220 private Tenant savedDifferentTenant;
  221 +
221 protected void loginDifferentTenant() throws Exception { 222 protected void loginDifferentTenant() throws Exception {
222 loginSysAdmin(); 223 loginSysAdmin();
223 Tenant tenant = new Tenant(); 224 Tenant tenant = new Tenant();
@@ -313,6 +314,10 @@ public abstract class AbstractWebTest { @@ -313,6 +314,10 @@ public abstract class AbstractWebTest {
313 return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass); 314 return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
314 } 315 }
315 316
  317 + protected <T> T doGet(String urlTemplate, Class<T> responseClass, ResultMatcher resultMatcher, Object... urlVariables) throws Exception {
  318 + return readResponse(doGet(urlTemplate, urlVariables).andExpect(resultMatcher), responseClass);
  319 + }
  320 +
316 protected <T> T doGetAsync(String urlTemplate, Class<T> responseClass, Object... urlVariables) throws Exception { 321 protected <T> T doGetAsync(String urlTemplate, Class<T> responseClass, Object... urlVariables) throws Exception {
317 return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass); 322 return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
318 } 323 }
@@ -352,9 +357,9 @@ public abstract class AbstractWebTest { @@ -352,9 +357,9 @@ public abstract class AbstractWebTest {
352 return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType); 357 return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType);
353 } 358 }
354 359
355 - protected <T> T doGetTypedWithTimePageLink(String urlTemplate, TypeReference<T> responseType,  
356 - TimePageLink pageLink,  
357 - Object... urlVariables) throws Exception { 360 + protected <T> T doGetTypedWithTimePageLink(String urlTemplate, TypeReference<T> responseType,
  361 + TimePageLink pageLink,
  362 + Object... urlVariables) throws Exception {
358 List<Object> pageLinkVariables = new ArrayList<>(); 363 List<Object> pageLinkVariables = new ArrayList<>();
359 urlTemplate += "pageSize={pageSize}&page={page}"; 364 urlTemplate += "pageSize={pageSize}&page={page}";
360 pageLinkVariables.add(pageLink.getPageSize()); 365 pageLinkVariables.add(pageLink.getPageSize());
@@ -395,11 +400,11 @@ public abstract class AbstractWebTest { @@ -395,11 +400,11 @@ public abstract class AbstractWebTest {
395 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass); 400 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
396 } 401 }
397 402
398 - protected <T,R> R doPostWithResponse(String urlTemplate, T content, Class<R> responseClass, String... params) throws Exception { 403 + protected <T, R> R doPostWithResponse(String urlTemplate, T content, Class<R> responseClass, String... params) throws Exception {
399 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass); 404 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
400 } 405 }
401 406
402 - protected <T,R> R doPostWithTypedResponse(String urlTemplate, T content, TypeReference<R> responseType, String... params) throws Exception { 407 + protected <T, R> R doPostWithTypedResponse(String urlTemplate, T content, TypeReference<R> responseType, String... params) throws Exception {
403 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseType); 408 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseType);
404 } 409 }
405 410
@@ -430,7 +435,7 @@ public abstract class AbstractWebTest { @@ -430,7 +435,7 @@ public abstract class AbstractWebTest {
430 return mockMvc.perform(postRequest); 435 return mockMvc.perform(postRequest);
431 } 436 }
432 437
433 - protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception { 438 + protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception {
434 MockHttpServletRequestBuilder postRequest = post(urlTemplate, params); 439 MockHttpServletRequestBuilder postRequest = post(urlTemplate, params);
435 setJwtToken(postRequest); 440 setJwtToken(postRequest);
436 String json = json(content); 441 String json = json(content);
@@ -15,74 +15,79 @@ @@ -15,74 +15,79 @@
15 */ 15 */
16 package org.thingsboard.server.controller; 16 package org.thingsboard.server.controller;
17 17
18 -import static org.hamcrest.Matchers.containsString;  
19 -import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;  
20 -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;  
21 -  
22 -import java.util.ArrayList;  
23 -import java.util.Collections;  
24 -import java.util.List;  
25 -  
26 import com.datastax.oss.driver.api.core.uuid.Uuids; 18 import com.datastax.oss.driver.api.core.uuid.Uuids;
  19 +import com.fasterxml.jackson.core.type.TypeReference;
27 import org.apache.commons.lang3.RandomStringUtils; 20 import org.apache.commons.lang3.RandomStringUtils;
28 -import org.thingsboard.server.common.data.*; 21 +import org.junit.After;
  22 +import org.junit.Assert;
  23 +import org.junit.Before;
  24 +import org.junit.Test;
  25 +import org.thingsboard.server.common.data.Customer;
  26 +import org.thingsboard.server.common.data.Device;
  27 +import org.thingsboard.server.common.data.EntitySubtype;
  28 +import org.thingsboard.server.common.data.Tenant;
  29 +import org.thingsboard.server.common.data.User;
29 import org.thingsboard.server.common.data.id.CustomerId; 30 import org.thingsboard.server.common.data.id.CustomerId;
30 import org.thingsboard.server.common.data.id.DeviceCredentialsId; 31 import org.thingsboard.server.common.data.id.DeviceCredentialsId;
31 import org.thingsboard.server.common.data.id.DeviceId; 32 import org.thingsboard.server.common.data.id.DeviceId;
32 import org.thingsboard.server.common.data.page.PageData; 33 import org.thingsboard.server.common.data.page.PageData;
33 import org.thingsboard.server.common.data.page.PageLink; 34 import org.thingsboard.server.common.data.page.PageLink;
  35 +import org.thingsboard.server.common.data.relation.EntityRelation;
  36 +import org.thingsboard.server.common.data.relation.RelationTypeGroup;
34 import org.thingsboard.server.common.data.security.Authority; 37 import org.thingsboard.server.common.data.security.Authority;
35 import org.thingsboard.server.common.data.security.DeviceCredentials; 38 import org.thingsboard.server.common.data.security.DeviceCredentials;
36 import org.thingsboard.server.common.data.security.DeviceCredentialsType; 39 import org.thingsboard.server.common.data.security.DeviceCredentialsType;
37 import org.thingsboard.server.dao.model.ModelConstants; 40 import org.thingsboard.server.dao.model.ModelConstants;
38 -import org.junit.After;  
39 -import org.junit.Assert;  
40 -import org.junit.Before;  
41 -import org.junit.Test;  
42 41
43 -import com.fasterxml.jackson.core.type.TypeReference; 42 +import java.util.ArrayList;
  43 +import java.util.Collections;
  44 +import java.util.List;
  45 +
  46 +import static org.hamcrest.Matchers.containsString;
  47 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  48 +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
44 49
45 public abstract class BaseDeviceControllerTest extends AbstractControllerTest { 50 public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
46 - 51 +
47 private IdComparator<Device> idComparator = new IdComparator<>(); 52 private IdComparator<Device> idComparator = new IdComparator<>();
48 - 53 +
49 private Tenant savedTenant; 54 private Tenant savedTenant;
50 private User tenantAdmin; 55 private User tenantAdmin;
51 - 56 +
52 @Before 57 @Before
53 public void beforeTest() throws Exception { 58 public void beforeTest() throws Exception {
54 loginSysAdmin(); 59 loginSysAdmin();
55 - 60 +
56 Tenant tenant = new Tenant(); 61 Tenant tenant = new Tenant();
57 tenant.setTitle("My tenant"); 62 tenant.setTitle("My tenant");
58 savedTenant = doPost("/api/tenant", tenant, Tenant.class); 63 savedTenant = doPost("/api/tenant", tenant, Tenant.class);
59 Assert.assertNotNull(savedTenant); 64 Assert.assertNotNull(savedTenant);
60 - 65 +
61 tenantAdmin = new User(); 66 tenantAdmin = new User();
62 tenantAdmin.setAuthority(Authority.TENANT_ADMIN); 67 tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
63 tenantAdmin.setTenantId(savedTenant.getId()); 68 tenantAdmin.setTenantId(savedTenant.getId());
64 tenantAdmin.setEmail("tenant2@thingsboard.org"); 69 tenantAdmin.setEmail("tenant2@thingsboard.org");
65 tenantAdmin.setFirstName("Joe"); 70 tenantAdmin.setFirstName("Joe");
66 tenantAdmin.setLastName("Downs"); 71 tenantAdmin.setLastName("Downs");
67 - 72 +
68 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); 73 tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
69 } 74 }
70 - 75 +
71 @After 76 @After
72 public void afterTest() throws Exception { 77 public void afterTest() throws Exception {
73 loginSysAdmin(); 78 loginSysAdmin();
74 -  
75 - doDelete("/api/tenant/"+savedTenant.getId().getId().toString())  
76 - .andExpect(status().isOk()); 79 +
  80 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  81 + .andExpect(status().isOk());
77 } 82 }
78 - 83 +
79 @Test 84 @Test
80 public void testSaveDevice() throws Exception { 85 public void testSaveDevice() throws Exception {
81 Device device = new Device(); 86 Device device = new Device();
82 device.setName("My device"); 87 device.setName("My device");
83 device.setType("default"); 88 device.setType("default");
84 Device savedDevice = doPost("/api/device", device, Device.class); 89 Device savedDevice = doPost("/api/device", device, Device.class);
85 - 90 +
86 Assert.assertNotNull(savedDevice); 91 Assert.assertNotNull(savedDevice);
87 Assert.assertNotNull(savedDevice.getId()); 92 Assert.assertNotNull(savedDevice.getId());
88 Assert.assertTrue(savedDevice.getCreatedTime() > 0); 93 Assert.assertTrue(savedDevice.getCreatedTime() > 0);
@@ -90,9 +95,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -90,9 +95,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
90 Assert.assertNotNull(savedDevice.getCustomerId()); 95 Assert.assertNotNull(savedDevice.getCustomerId());
91 Assert.assertEquals(NULL_UUID, savedDevice.getCustomerId().getId()); 96 Assert.assertEquals(NULL_UUID, savedDevice.getCustomerId().getId());
92 Assert.assertEquals(device.getName(), savedDevice.getName()); 97 Assert.assertEquals(device.getName(), savedDevice.getName());
93 -  
94 - DeviceCredentials deviceCredentials =  
95 - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 98 +
  99 + DeviceCredentials deviceCredentials =
  100 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
96 101
97 Assert.assertNotNull(deviceCredentials); 102 Assert.assertNotNull(deviceCredentials);
98 Assert.assertNotNull(deviceCredentials.getId()); 103 Assert.assertNotNull(deviceCredentials.getId());
@@ -100,10 +105,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -100,10 +105,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
100 Assert.assertEquals(DeviceCredentialsType.ACCESS_TOKEN, deviceCredentials.getCredentialsType()); 105 Assert.assertEquals(DeviceCredentialsType.ACCESS_TOKEN, deviceCredentials.getCredentialsType());
101 Assert.assertNotNull(deviceCredentials.getCredentialsId()); 106 Assert.assertNotNull(deviceCredentials.getCredentialsId());
102 Assert.assertEquals(20, deviceCredentials.getCredentialsId().length()); 107 Assert.assertEquals(20, deviceCredentials.getCredentialsId().length());
103 - 108 +
104 savedDevice.setName("My new device"); 109 savedDevice.setName("My new device");
105 doPost("/api/device", savedDevice, Device.class); 110 doPost("/api/device", savedDevice, Device.class);
106 - 111 +
107 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class); 112 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
108 Assert.assertEquals(foundDevice.getName(), savedDevice.getName()); 113 Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
109 } 114 }
@@ -115,10 +120,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -115,10 +120,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
115 device.setType("default"); 120 device.setType("default");
116 Device savedDevice = doPost("/api/device", device, Device.class); 121 Device savedDevice = doPost("/api/device", device, Device.class);
117 loginDifferentTenant(); 122 loginDifferentTenant();
118 - doPost("/api/device", savedDevice, Device.class, status().isForbidden()); 123 + doPost("/api/device", savedDevice, Device.class, status().isNotFound());
119 deleteDifferentTenant(); 124 deleteDifferentTenant();
120 } 125 }
121 - 126 +
122 @Test 127 @Test
123 public void testFindDeviceById() throws Exception { 128 public void testFindDeviceById() throws Exception {
124 Device device = new Device(); 129 Device device = new Device();
@@ -133,26 +138,27 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -133,26 +138,27 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
133 @Test 138 @Test
134 public void testFindDeviceTypesByTenantId() throws Exception { 139 public void testFindDeviceTypesByTenantId() throws Exception {
135 List<Device> devices = new ArrayList<>(); 140 List<Device> devices = new ArrayList<>();
136 - for (int i=0;i<3;i++) { 141 + for (int i = 0; i < 3; i++) {
137 Device device = new Device(); 142 Device device = new Device();
138 - device.setName("My device B"+i); 143 + device.setName("My device B" + i);
139 device.setType("typeB"); 144 device.setType("typeB");
140 devices.add(doPost("/api/device", device, Device.class)); 145 devices.add(doPost("/api/device", device, Device.class));
141 } 146 }
142 - for (int i=0;i<7;i++) { 147 + for (int i = 0; i < 7; i++) {
143 Device device = new Device(); 148 Device device = new Device();
144 - device.setName("My device C"+i); 149 + device.setName("My device C" + i);
145 device.setType("typeC"); 150 device.setType("typeC");
146 devices.add(doPost("/api/device", device, Device.class)); 151 devices.add(doPost("/api/device", device, Device.class));
147 } 152 }
148 - for (int i=0;i<9;i++) { 153 + for (int i = 0; i < 9; i++) {
149 Device device = new Device(); 154 Device device = new Device();
150 - device.setName("My device A"+i); 155 + device.setName("My device A" + i);
151 device.setType("typeA"); 156 device.setType("typeA");
152 devices.add(doPost("/api/device", device, Device.class)); 157 devices.add(doPost("/api/device", device, Device.class));
153 } 158 }
154 List<EntitySubtype> deviceTypes = doGetTyped("/api/device/types", 159 List<EntitySubtype> deviceTypes = doGetTyped("/api/device/types",
155 - new TypeReference<List<EntitySubtype>>(){}); 160 + new TypeReference<List<EntitySubtype>>() {
  161 + });
156 162
157 Assert.assertNotNull(deviceTypes); 163 Assert.assertNotNull(deviceTypes);
158 Assert.assertEquals(3, deviceTypes.size()); 164 Assert.assertEquals(3, deviceTypes.size());
@@ -160,19 +166,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -160,19 +166,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
160 Assert.assertEquals("typeB", deviceTypes.get(1).getType()); 166 Assert.assertEquals("typeB", deviceTypes.get(1).getType());
161 Assert.assertEquals("typeC", deviceTypes.get(2).getType()); 167 Assert.assertEquals("typeC", deviceTypes.get(2).getType());
162 } 168 }
163 - 169 +
164 @Test 170 @Test
165 public void testDeleteDevice() throws Exception { 171 public void testDeleteDevice() throws Exception {
166 Device device = new Device(); 172 Device device = new Device();
167 device.setName("My device"); 173 device.setName("My device");
168 device.setType("default"); 174 device.setType("default");
169 Device savedDevice = doPost("/api/device", device, Device.class); 175 Device savedDevice = doPost("/api/device", device, Device.class);
170 -  
171 - doDelete("/api/device/"+savedDevice.getId().getId().toString())  
172 - .andExpect(status().isOk());  
173 176
174 - doGet("/api/device/"+savedDevice.getId().getId().toString())  
175 - .andExpect(status().isNotFound()); 177 + doDelete("/api/device/" + savedDevice.getId().getId().toString())
  178 + .andExpect(status().isOk());
  179 +
  180 + doGet("/api/device/" + savedDevice.getId().getId().toString())
  181 + .andExpect(status().isNotFound());
176 } 182 }
177 183
178 @Test 184 @Test
@@ -189,52 +195,51 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -189,52 +195,51 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
189 Device device = new Device(); 195 Device device = new Device();
190 device.setType("default"); 196 device.setType("default");
191 doPost("/api/device", device) 197 doPost("/api/device", device)
192 - .andExpect(status().isBadRequest())  
193 - .andExpect(statusReason(containsString("Device name should be specified"))); 198 + .andExpect(status().isBadRequest())
  199 + .andExpect(statusReason(containsString("Device name should be specified")));
194 } 200 }
195 - 201 +
196 @Test 202 @Test
197 public void testAssignUnassignDeviceToCustomer() throws Exception { 203 public void testAssignUnassignDeviceToCustomer() throws Exception {
198 Device device = new Device(); 204 Device device = new Device();
199 device.setName("My device"); 205 device.setName("My device");
200 device.setType("default"); 206 device.setType("default");
201 Device savedDevice = doPost("/api/device", device, Device.class); 207 Device savedDevice = doPost("/api/device", device, Device.class);
202 - 208 +
203 Customer customer = new Customer(); 209 Customer customer = new Customer();
204 customer.setTitle("My customer"); 210 customer.setTitle("My customer");
205 Customer savedCustomer = doPost("/api/customer", customer, Customer.class); 211 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
206 -  
207 - Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId().toString() 212 +
  213 + Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
208 + "/device/" + savedDevice.getId().getId().toString(), Device.class); 214 + "/device/" + savedDevice.getId().getId().toString(), Device.class);
209 Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId()); 215 Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId());
210 - 216 +
211 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class); 217 Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
212 Assert.assertEquals(savedCustomer.getId(), foundDevice.getCustomerId()); 218 Assert.assertEquals(savedCustomer.getId(), foundDevice.getCustomerId());
213 219
214 - Device unassignedDevice = 220 + Device unassignedDevice =
215 doDelete("/api/customer/device/" + savedDevice.getId().getId().toString(), Device.class); 221 doDelete("/api/customer/device/" + savedDevice.getId().getId().toString(), Device.class);
216 Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId()); 222 Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId());
217 - 223 +
218 foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class); 224 foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
219 Assert.assertEquals(ModelConstants.NULL_UUID, foundDevice.getCustomerId().getId()); 225 Assert.assertEquals(ModelConstants.NULL_UUID, foundDevice.getCustomerId().getId());
220 } 226 }
221 - 227 +
222 @Test 228 @Test
223 public void testAssignDeviceToNonExistentCustomer() throws Exception { 229 public void testAssignDeviceToNonExistentCustomer() throws Exception {
224 Device device = new Device(); 230 Device device = new Device();
225 device.setName("My device"); 231 device.setName("My device");
226 device.setType("default"); 232 device.setType("default");
227 Device savedDevice = doPost("/api/device", device, Device.class); 233 Device savedDevice = doPost("/api/device", device, Device.class);
228 -  
229 doPost("/api/customer/" + Uuids.timeBased().toString() 234 doPost("/api/customer/" + Uuids.timeBased().toString()
230 + "/device/" + savedDevice.getId().getId().toString()) 235 + "/device/" + savedDevice.getId().getId().toString())
231 - .andExpect(status().isNotFound()); 236 + .andExpect(status().isNotFound());
232 } 237 }
233 - 238 +
234 @Test 239 @Test
235 public void testAssignDeviceToCustomerFromDifferentTenant() throws Exception { 240 public void testAssignDeviceToCustomerFromDifferentTenant() throws Exception {
236 loginSysAdmin(); 241 loginSysAdmin();
237 - 242 +
238 Tenant tenant2 = new Tenant(); 243 Tenant tenant2 = new Tenant();
239 tenant2.setTitle("Different tenant"); 244 tenant2.setTitle("Different tenant");
240 Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class); 245 Tenant savedTenant2 = doPost("/api/tenant", tenant2, Tenant.class);
@@ -246,103 +251,103 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -246,103 +251,103 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
246 tenantAdmin2.setEmail("tenant3@thingsboard.org"); 251 tenantAdmin2.setEmail("tenant3@thingsboard.org");
247 tenantAdmin2.setFirstName("Joe"); 252 tenantAdmin2.setFirstName("Joe");
248 tenantAdmin2.setLastName("Downs"); 253 tenantAdmin2.setLastName("Downs");
249 - 254 +
250 tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1"); 255 tenantAdmin2 = createUserAndLogin(tenantAdmin2, "testPassword1");
251 - 256 +
252 Customer customer = new Customer(); 257 Customer customer = new Customer();
253 customer.setTitle("Different customer"); 258 customer.setTitle("Different customer");
254 Customer savedCustomer = doPost("/api/customer", customer, Customer.class); 259 Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
255 260
256 login(tenantAdmin.getEmail(), "testPassword1"); 261 login(tenantAdmin.getEmail(), "testPassword1");
257 - 262 +
258 Device device = new Device(); 263 Device device = new Device();
259 device.setName("My device"); 264 device.setName("My device");
260 device.setType("default"); 265 device.setType("default");
261 Device savedDevice = doPost("/api/device", device, Device.class); 266 Device savedDevice = doPost("/api/device", device, Device.class);
262 - 267 +
263 doPost("/api/customer/" + savedCustomer.getId().getId().toString() 268 doPost("/api/customer/" + savedCustomer.getId().getId().toString()
264 + "/device/" + savedDevice.getId().getId().toString()) 269 + "/device/" + savedDevice.getId().getId().toString())
265 - .andExpect(status().isForbidden());  
266 - 270 + .andExpect(status().isForbidden());
  271 +
267 loginSysAdmin(); 272 loginSysAdmin();
268 -  
269 - doDelete("/api/tenant/"+savedTenant2.getId().getId().toString())  
270 - .andExpect(status().isOk()); 273 +
  274 + doDelete("/api/tenant/" + savedTenant2.getId().getId().toString())
  275 + .andExpect(status().isOk());
271 } 276 }
272 - 277 +
273 @Test 278 @Test
274 public void testFindDeviceCredentialsByDeviceId() throws Exception { 279 public void testFindDeviceCredentialsByDeviceId() throws Exception {
275 Device device = new Device(); 280 Device device = new Device();
276 device.setName("My device"); 281 device.setName("My device");
277 device.setType("default"); 282 device.setType("default");
278 Device savedDevice = doPost("/api/device", device, Device.class); 283 Device savedDevice = doPost("/api/device", device, Device.class);
279 - DeviceCredentials deviceCredentials =  
280 - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 284 + DeviceCredentials deviceCredentials =
  285 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
281 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); 286 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
282 } 287 }
283 - 288 +
284 @Test 289 @Test
285 public void testSaveDeviceCredentials() throws Exception { 290 public void testSaveDeviceCredentials() throws Exception {
286 Device device = new Device(); 291 Device device = new Device();
287 device.setName("My device"); 292 device.setName("My device");
288 device.setType("default"); 293 device.setType("default");
289 Device savedDevice = doPost("/api/device", device, Device.class); 294 Device savedDevice = doPost("/api/device", device, Device.class);
290 - DeviceCredentials deviceCredentials =  
291 - doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 295 + DeviceCredentials deviceCredentials =
  296 + doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
292 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); 297 Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
293 deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN); 298 deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
294 deviceCredentials.setCredentialsId("access_token"); 299 deviceCredentials.setCredentialsId("access_token");
295 doPost("/api/device/credentials", deviceCredentials) 300 doPost("/api/device/credentials", deviceCredentials)
296 - .andExpect(status().isOk());  
297 -  
298 - DeviceCredentials foundDeviceCredentials = 301 + .andExpect(status().isOk());
  302 +
  303 + DeviceCredentials foundDeviceCredentials =
299 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 304 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
300 - 305 +
301 Assert.assertEquals(deviceCredentials, foundDeviceCredentials); 306 Assert.assertEquals(deviceCredentials, foundDeviceCredentials);
302 } 307 }
303 - 308 +
304 @Test 309 @Test
305 public void testSaveDeviceCredentialsWithEmptyDevice() throws Exception { 310 public void testSaveDeviceCredentialsWithEmptyDevice() throws Exception {
306 DeviceCredentials deviceCredentials = new DeviceCredentials(); 311 DeviceCredentials deviceCredentials = new DeviceCredentials();
307 doPost("/api/device/credentials", deviceCredentials) 312 doPost("/api/device/credentials", deviceCredentials)
308 - .andExpect(status().isBadRequest()); 313 + .andExpect(status().isBadRequest());
309 } 314 }
310 - 315 +
311 @Test 316 @Test
312 public void testSaveDeviceCredentialsWithEmptyCredentialsType() throws Exception { 317 public void testSaveDeviceCredentialsWithEmptyCredentialsType() throws Exception {
313 Device device = new Device(); 318 Device device = new Device();
314 device.setName("My device"); 319 device.setName("My device");
315 device.setType("default"); 320 device.setType("default");
316 Device savedDevice = doPost("/api/device", device, Device.class); 321 Device savedDevice = doPost("/api/device", device, Device.class);
317 - DeviceCredentials deviceCredentials = 322 + DeviceCredentials deviceCredentials =
318 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 323 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
319 deviceCredentials.setCredentialsType(null); 324 deviceCredentials.setCredentialsType(null);
320 doPost("/api/device/credentials", deviceCredentials) 325 doPost("/api/device/credentials", deviceCredentials)
321 - .andExpect(status().isBadRequest())  
322 - .andExpect(statusReason(containsString("Device credentials type should be specified"))); 326 + .andExpect(status().isBadRequest())
  327 + .andExpect(statusReason(containsString("Device credentials type should be specified")));
323 } 328 }
324 - 329 +
325 @Test 330 @Test
326 public void testSaveDeviceCredentialsWithEmptyCredentialsId() throws Exception { 331 public void testSaveDeviceCredentialsWithEmptyCredentialsId() throws Exception {
327 Device device = new Device(); 332 Device device = new Device();
328 device.setName("My device"); 333 device.setName("My device");
329 device.setType("default"); 334 device.setType("default");
330 Device savedDevice = doPost("/api/device", device, Device.class); 335 Device savedDevice = doPost("/api/device", device, Device.class);
331 - DeviceCredentials deviceCredentials = 336 + DeviceCredentials deviceCredentials =
332 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 337 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
333 deviceCredentials.setCredentialsId(null); 338 deviceCredentials.setCredentialsId(null);
334 doPost("/api/device/credentials", deviceCredentials) 339 doPost("/api/device/credentials", deviceCredentials)
335 - .andExpect(status().isBadRequest())  
336 - .andExpect(statusReason(containsString("Device credentials id should be specified"))); 340 + .andExpect(status().isBadRequest())
  341 + .andExpect(statusReason(containsString("Device credentials id should be specified")));
337 } 342 }
338 - 343 +
339 @Test 344 @Test
340 public void testSaveNonExistentDeviceCredentials() throws Exception { 345 public void testSaveNonExistentDeviceCredentials() throws Exception {
341 Device device = new Device(); 346 Device device = new Device();
342 device.setName("My device"); 347 device.setName("My device");
343 device.setType("default"); 348 device.setType("default");
344 Device savedDevice = doPost("/api/device", device, Device.class); 349 Device savedDevice = doPost("/api/device", device, Device.class);
345 - DeviceCredentials deviceCredentials = 350 + DeviceCredentials deviceCredentials =
346 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 351 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
347 DeviceCredentials newDeviceCredentials = new DeviceCredentials(new DeviceCredentialsId(Uuids.timeBased())); 352 DeviceCredentials newDeviceCredentials = new DeviceCredentials(new DeviceCredentialsId(Uuids.timeBased()));
348 newDeviceCredentials.setCreatedTime(deviceCredentials.getCreatedTime()); 353 newDeviceCredentials.setCreatedTime(deviceCredentials.getCreatedTime());
@@ -350,29 +355,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -350,29 +355,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
350 newDeviceCredentials.setCredentialsType(deviceCredentials.getCredentialsType()); 355 newDeviceCredentials.setCredentialsType(deviceCredentials.getCredentialsType());
351 newDeviceCredentials.setCredentialsId(deviceCredentials.getCredentialsId()); 356 newDeviceCredentials.setCredentialsId(deviceCredentials.getCredentialsId());
352 doPost("/api/device/credentials", newDeviceCredentials) 357 doPost("/api/device/credentials", newDeviceCredentials)
353 - .andExpect(status().isBadRequest())  
354 - .andExpect(statusReason(containsString("Unable to update non-existent device credentials"))); 358 + .andExpect(status().isBadRequest())
  359 + .andExpect(statusReason(containsString("Unable to update non-existent device credentials")));
355 } 360 }
356 - 361 +
357 @Test 362 @Test
358 public void testSaveDeviceCredentialsWithNonExistentDevice() throws Exception { 363 public void testSaveDeviceCredentialsWithNonExistentDevice() throws Exception {
359 Device device = new Device(); 364 Device device = new Device();
360 device.setName("My device"); 365 device.setName("My device");
361 device.setType("default"); 366 device.setType("default");
362 Device savedDevice = doPost("/api/device", device, Device.class); 367 Device savedDevice = doPost("/api/device", device, Device.class);
363 - DeviceCredentials deviceCredentials = 368 + DeviceCredentials deviceCredentials =
364 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 369 doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
365 deviceCredentials.setDeviceId(new DeviceId(Uuids.timeBased())); 370 deviceCredentials.setDeviceId(new DeviceId(Uuids.timeBased()));
366 doPost("/api/device/credentials", deviceCredentials) 371 doPost("/api/device/credentials", deviceCredentials)
367 - .andExpect(status().isNotFound()); 372 + .andExpect(status().isNotFound());
368 } 373 }
369 374
370 @Test 375 @Test
371 public void testFindTenantDevices() throws Exception { 376 public void testFindTenantDevices() throws Exception {
372 List<Device> devices = new ArrayList<>(); 377 List<Device> devices = new ArrayList<>();
373 - for (int i=0;i<178;i++) { 378 + for (int i = 0; i < 178; i++) {
374 Device device = new Device(); 379 Device device = new Device();
375 - device.setName("Device"+i); 380 + device.setName("Device" + i);
376 device.setType("default"); 381 device.setType("default");
377 devices.add(doPost("/api/device", device, Device.class)); 382 devices.add(doPost("/api/device", device, Device.class));
378 } 383 }
@@ -380,28 +385,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -380,28 +385,29 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
380 PageLink pageLink = new PageLink(23); 385 PageLink pageLink = new PageLink(23);
381 PageData<Device> pageData = null; 386 PageData<Device> pageData = null;
382 do { 387 do {
383 - pageData = doGetTypedWithPageLink("/api/tenant/devices?", 388 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
384 new TypeReference<PageData<Device>>(){}, pageLink); 389 new TypeReference<PageData<Device>>(){}, pageLink);
  390 +
385 loadedDevices.addAll(pageData.getData()); 391 loadedDevices.addAll(pageData.getData());
386 if (pageData.hasNext()) { 392 if (pageData.hasNext()) {
387 pageLink = pageLink.nextPageLink(); 393 pageLink = pageLink.nextPageLink();
388 } 394 }
389 } while (pageData.hasNext()); 395 } while (pageData.hasNext());
390 - 396 +
391 Collections.sort(devices, idComparator); 397 Collections.sort(devices, idComparator);
392 Collections.sort(loadedDevices, idComparator); 398 Collections.sort(loadedDevices, idComparator);
393 - 399 +
394 Assert.assertEquals(devices, loadedDevices); 400 Assert.assertEquals(devices, loadedDevices);
395 } 401 }
396 - 402 +
397 @Test 403 @Test
398 public void testFindTenantDevicesByName() throws Exception { 404 public void testFindTenantDevicesByName() throws Exception {
399 String title1 = "Device title 1"; 405 String title1 = "Device title 1";
400 List<Device> devicesTitle1 = new ArrayList<>(); 406 List<Device> devicesTitle1 = new ArrayList<>();
401 - for (int i=0;i<143;i++) { 407 + for (int i = 0; i < 143; i++) {
402 Device device = new Device(); 408 Device device = new Device();
403 String suffix = RandomStringUtils.randomAlphanumeric(15); 409 String suffix = RandomStringUtils.randomAlphanumeric(15);
404 - String name = title1+suffix; 410 + String name = title1 + suffix;
405 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 411 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
406 device.setName(name); 412 device.setName(name);
407 device.setType("default"); 413 device.setType("default");
@@ -409,37 +415,37 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -409,37 +415,37 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
409 } 415 }
410 String title2 = "Device title 2"; 416 String title2 = "Device title 2";
411 List<Device> devicesTitle2 = new ArrayList<>(); 417 List<Device> devicesTitle2 = new ArrayList<>();
412 - for (int i=0;i<75;i++) { 418 + for (int i = 0; i < 75; i++) {
413 Device device = new Device(); 419 Device device = new Device();
414 String suffix = RandomStringUtils.randomAlphanumeric(15); 420 String suffix = RandomStringUtils.randomAlphanumeric(15);
415 - String name = title2+suffix; 421 + String name = title2 + suffix;
416 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 422 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
417 device.setName(name); 423 device.setName(name);
418 device.setType("default"); 424 device.setType("default");
419 devicesTitle2.add(doPost("/api/device", device, Device.class)); 425 devicesTitle2.add(doPost("/api/device", device, Device.class));
420 } 426 }
421 - 427 +
422 List<Device> loadedDevicesTitle1 = new ArrayList<>(); 428 List<Device> loadedDevicesTitle1 = new ArrayList<>();
423 PageLink pageLink = new PageLink(15, 0, title1); 429 PageLink pageLink = new PageLink(15, 0, title1);
424 PageData<Device> pageData = null; 430 PageData<Device> pageData = null;
425 do { 431 do {
426 - pageData = doGetTypedWithPageLink("/api/tenant/devices?", 432 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
427 new TypeReference<PageData<Device>>(){}, pageLink); 433 new TypeReference<PageData<Device>>(){}, pageLink);
428 loadedDevicesTitle1.addAll(pageData.getData()); 434 loadedDevicesTitle1.addAll(pageData.getData());
429 if (pageData.hasNext()) { 435 if (pageData.hasNext()) {
430 pageLink = pageLink.nextPageLink(); 436 pageLink = pageLink.nextPageLink();
431 } 437 }
432 } while (pageData.hasNext()); 438 } while (pageData.hasNext());
433 - 439 +
434 Collections.sort(devicesTitle1, idComparator); 440 Collections.sort(devicesTitle1, idComparator);
435 Collections.sort(loadedDevicesTitle1, idComparator); 441 Collections.sort(loadedDevicesTitle1, idComparator);
436 - 442 +
437 Assert.assertEquals(devicesTitle1, loadedDevicesTitle1); 443 Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
438 - 444 +
439 List<Device> loadedDevicesTitle2 = new ArrayList<>(); 445 List<Device> loadedDevicesTitle2 = new ArrayList<>();
440 pageLink = new PageLink(4, 0, title2); 446 pageLink = new PageLink(4, 0, title2);
441 do { 447 do {
442 - pageData = doGetTypedWithPageLink("/api/tenant/devices?", 448 + pageData = doGetTypedWithPageLink("/api/tenant/devices?",
443 new TypeReference<PageData<Device>>(){}, pageLink); 449 new TypeReference<PageData<Device>>(){}, pageLink);
444 loadedDevicesTitle2.addAll(pageData.getData()); 450 loadedDevicesTitle2.addAll(pageData.getData());
445 if (pageData.hasNext()) { 451 if (pageData.hasNext()) {
@@ -449,25 +455,23 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -449,25 +455,23 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
449 455
450 Collections.sort(devicesTitle2, idComparator); 456 Collections.sort(devicesTitle2, idComparator);
451 Collections.sort(loadedDevicesTitle2, idComparator); 457 Collections.sort(loadedDevicesTitle2, idComparator);
452 - 458 +
453 Assert.assertEquals(devicesTitle2, loadedDevicesTitle2); 459 Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
454 - 460 +
455 for (Device device : loadedDevicesTitle1) { 461 for (Device device : loadedDevicesTitle1) {
456 - doDelete("/api/device/"+device.getId().getId().toString())  
457 - .andExpect(status().isOk()); 462 + doDelete("/api/device/" + device.getId().getId().toString())
  463 + .andExpect(status().isOk());
458 } 464 }
459 -  
460 pageLink = new PageLink(4, 0, title1); 465 pageLink = new PageLink(4, 0, title1);
461 pageData = doGetTypedWithPageLink("/api/tenant/devices?", 466 pageData = doGetTypedWithPageLink("/api/tenant/devices?",
462 new TypeReference<PageData<Device>>(){}, pageLink); 467 new TypeReference<PageData<Device>>(){}, pageLink);
463 Assert.assertFalse(pageData.hasNext()); 468 Assert.assertFalse(pageData.hasNext());
464 Assert.assertEquals(0, pageData.getData().size()); 469 Assert.assertEquals(0, pageData.getData().size());
465 - 470 +
466 for (Device device : loadedDevicesTitle2) { 471 for (Device device : loadedDevicesTitle2) {
467 - doDelete("/api/device/"+device.getId().getId().toString())  
468 - .andExpect(status().isOk()); 472 + doDelete("/api/device/" + device.getId().getId().toString())
  473 + .andExpect(status().isOk());
469 } 474 }
470 -  
471 pageLink = new PageLink(4, 0, title2); 475 pageLink = new PageLink(4, 0, title2);
472 pageData = doGetTypedWithPageLink("/api/tenant/devices?", 476 pageData = doGetTypedWithPageLink("/api/tenant/devices?",
473 new TypeReference<PageData<Device>>(){}, pageLink); 477 new TypeReference<PageData<Device>>(){}, pageLink);
@@ -480,10 +484,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -480,10 +484,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
480 String title1 = "Device title 1"; 484 String title1 = "Device title 1";
481 String type1 = "typeA"; 485 String type1 = "typeA";
482 List<Device> devicesType1 = new ArrayList<>(); 486 List<Device> devicesType1 = new ArrayList<>();
483 - for (int i=0;i<143;i++) { 487 + for (int i = 0; i < 143; i++) {
484 Device device = new Device(); 488 Device device = new Device();
485 String suffix = RandomStringUtils.randomAlphanumeric(15); 489 String suffix = RandomStringUtils.randomAlphanumeric(15);
486 - String name = title1+suffix; 490 + String name = title1 + suffix;
487 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 491 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
488 device.setName(name); 492 device.setName(name);
489 device.setType(type1); 493 device.setType(type1);
@@ -492,10 +496,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -492,10 +496,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
492 String title2 = "Device title 2"; 496 String title2 = "Device title 2";
493 String type2 = "typeB"; 497 String type2 = "typeB";
494 List<Device> devicesType2 = new ArrayList<>(); 498 List<Device> devicesType2 = new ArrayList<>();
495 - for (int i=0;i<75;i++) { 499 + for (int i = 0; i < 75; i++) {
496 Device device = new Device(); 500 Device device = new Device();
497 String suffix = RandomStringUtils.randomAlphanumeric(15); 501 String suffix = RandomStringUtils.randomAlphanumeric(15);
498 - String name = title2+suffix; 502 + String name = title2 + suffix;
499 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 503 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
500 device.setName(name); 504 device.setName(name);
501 device.setType(type2); 505 device.setType(type2);
@@ -536,7 +540,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -536,7 +540,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
536 Assert.assertEquals(devicesType2, loadedDevicesType2); 540 Assert.assertEquals(devicesType2, loadedDevicesType2);
537 541
538 for (Device device : loadedDevicesType1) { 542 for (Device device : loadedDevicesType1) {
539 - doDelete("/api/device/"+device.getId().getId().toString()) 543 + doDelete("/api/device/" + device.getId().getId().toString())
540 .andExpect(status().isOk()); 544 .andExpect(status().isOk());
541 } 545 }
542 546
@@ -547,7 +551,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -547,7 +551,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
547 Assert.assertEquals(0, pageData.getData().size()); 551 Assert.assertEquals(0, pageData.getData().size());
548 552
549 for (Device device : loadedDevicesType2) { 553 for (Device device : loadedDevicesType2) {
550 - doDelete("/api/device/"+device.getId().getId().toString()) 554 + doDelete("/api/device/" + device.getId().getId().toString())
551 .andExpect(status().isOk()); 555 .andExpect(status().isOk());
552 } 556 }
553 557
@@ -557,42 +561,42 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -557,42 +561,42 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
557 Assert.assertFalse(pageData.hasNext()); 561 Assert.assertFalse(pageData.hasNext());
558 Assert.assertEquals(0, pageData.getData().size()); 562 Assert.assertEquals(0, pageData.getData().size());
559 } 563 }
560 - 564 +
561 @Test 565 @Test
562 public void testFindCustomerDevices() throws Exception { 566 public void testFindCustomerDevices() throws Exception {
563 Customer customer = new Customer(); 567 Customer customer = new Customer();
564 customer.setTitle("Test customer"); 568 customer.setTitle("Test customer");
565 customer = doPost("/api/customer", customer, Customer.class); 569 customer = doPost("/api/customer", customer, Customer.class);
566 CustomerId customerId = customer.getId(); 570 CustomerId customerId = customer.getId();
567 - 571 +
568 List<Device> devices = new ArrayList<>(); 572 List<Device> devices = new ArrayList<>();
569 - for (int i=0;i<128;i++) { 573 + for (int i = 0; i < 128; i++) {
570 Device device = new Device(); 574 Device device = new Device();
571 - device.setName("Device"+i); 575 + device.setName("Device" + i);
572 device.setType("default"); 576 device.setType("default");
573 device = doPost("/api/device", device, Device.class); 577 device = doPost("/api/device", device, Device.class);
574 - devices.add(doPost("/api/customer/" + customerId.getId().toString()  
575 - + "/device/" + device.getId().getId().toString(), Device.class)); 578 + devices.add(doPost("/api/customer/" + customerId.getId().toString()
  579 + + "/device/" + device.getId().getId().toString(), Device.class));
576 } 580 }
577 - 581 +
578 List<Device> loadedDevices = new ArrayList<>(); 582 List<Device> loadedDevices = new ArrayList<>();
579 PageLink pageLink = new PageLink(23); 583 PageLink pageLink = new PageLink(23);
580 PageData<Device> pageData = null; 584 PageData<Device> pageData = null;
581 do { 585 do {
582 - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", 586 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
583 new TypeReference<PageData<Device>>(){}, pageLink); 587 new TypeReference<PageData<Device>>(){}, pageLink);
584 loadedDevices.addAll(pageData.getData()); 588 loadedDevices.addAll(pageData.getData());
585 if (pageData.hasNext()) { 589 if (pageData.hasNext()) {
586 pageLink = pageLink.nextPageLink(); 590 pageLink = pageLink.nextPageLink();
587 } 591 }
588 } while (pageData.hasNext()); 592 } while (pageData.hasNext());
589 - 593 +
590 Collections.sort(devices, idComparator); 594 Collections.sort(devices, idComparator);
591 Collections.sort(loadedDevices, idComparator); 595 Collections.sort(loadedDevices, idComparator);
592 - 596 +
593 Assert.assertEquals(devices, loadedDevices); 597 Assert.assertEquals(devices, loadedDevices);
594 } 598 }
595 - 599 +
596 @Test 600 @Test
597 public void testFindCustomerDevicesByName() throws Exception { 601 public void testFindCustomerDevicesByName() throws Exception {
598 Customer customer = new Customer(); 602 Customer customer = new Customer();
@@ -602,52 +606,52 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -602,52 +606,52 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
602 606
603 String title1 = "Device title 1"; 607 String title1 = "Device title 1";
604 List<Device> devicesTitle1 = new ArrayList<>(); 608 List<Device> devicesTitle1 = new ArrayList<>();
605 - for (int i=0;i<125;i++) { 609 + for (int i = 0; i < 125; i++) {
606 Device device = new Device(); 610 Device device = new Device();
607 String suffix = RandomStringUtils.randomAlphanumeric(15); 611 String suffix = RandomStringUtils.randomAlphanumeric(15);
608 - String name = title1+suffix; 612 + String name = title1 + suffix;
609 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 613 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
610 device.setName(name); 614 device.setName(name);
611 device.setType("default"); 615 device.setType("default");
612 device = doPost("/api/device", device, Device.class); 616 device = doPost("/api/device", device, Device.class);
613 - devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString() 617 + devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
614 + "/device/" + device.getId().getId().toString(), Device.class)); 618 + "/device/" + device.getId().getId().toString(), Device.class));
615 } 619 }
616 String title2 = "Device title 2"; 620 String title2 = "Device title 2";
617 List<Device> devicesTitle2 = new ArrayList<>(); 621 List<Device> devicesTitle2 = new ArrayList<>();
618 - for (int i=0;i<143;i++) { 622 + for (int i = 0; i < 143; i++) {
619 Device device = new Device(); 623 Device device = new Device();
620 String suffix = RandomStringUtils.randomAlphanumeric(15); 624 String suffix = RandomStringUtils.randomAlphanumeric(15);
621 - String name = title2+suffix; 625 + String name = title2 + suffix;
622 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 626 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
623 device.setName(name); 627 device.setName(name);
624 device.setType("default"); 628 device.setType("default");
625 device = doPost("/api/device", device, Device.class); 629 device = doPost("/api/device", device, Device.class);
626 - devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString() 630 + devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
627 + "/device/" + device.getId().getId().toString(), Device.class)); 631 + "/device/" + device.getId().getId().toString(), Device.class));
628 } 632 }
629 - 633 +
630 List<Device> loadedDevicesTitle1 = new ArrayList<>(); 634 List<Device> loadedDevicesTitle1 = new ArrayList<>();
631 PageLink pageLink = new PageLink(15, 0, title1); 635 PageLink pageLink = new PageLink(15, 0, title1);
632 PageData<Device> pageData = null; 636 PageData<Device> pageData = null;
633 do { 637 do {
634 - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", 638 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
635 new TypeReference<PageData<Device>>(){}, pageLink); 639 new TypeReference<PageData<Device>>(){}, pageLink);
636 loadedDevicesTitle1.addAll(pageData.getData()); 640 loadedDevicesTitle1.addAll(pageData.getData());
637 if (pageData.hasNext()) { 641 if (pageData.hasNext()) {
638 pageLink = pageLink.nextPageLink(); 642 pageLink = pageLink.nextPageLink();
639 } 643 }
640 } while (pageData.hasNext()); 644 } while (pageData.hasNext());
641 - 645 +
642 Collections.sort(devicesTitle1, idComparator); 646 Collections.sort(devicesTitle1, idComparator);
643 Collections.sort(loadedDevicesTitle1, idComparator); 647 Collections.sort(loadedDevicesTitle1, idComparator);
644 - 648 +
645 Assert.assertEquals(devicesTitle1, loadedDevicesTitle1); 649 Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
646 - 650 +
647 List<Device> loadedDevicesTitle2 = new ArrayList<>(); 651 List<Device> loadedDevicesTitle2 = new ArrayList<>();
648 pageLink = new PageLink(4, 0, title2); 652 pageLink = new PageLink(4, 0, title2);
649 do { 653 do {
650 - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", 654 + pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
651 new TypeReference<PageData<Device>>(){}, pageLink); 655 new TypeReference<PageData<Device>>(){}, pageLink);
652 loadedDevicesTitle2.addAll(pageData.getData()); 656 loadedDevicesTitle2.addAll(pageData.getData());
653 if (pageData.hasNext()) { 657 if (pageData.hasNext()) {
@@ -657,25 +661,23 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -657,25 +661,23 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
657 661
658 Collections.sort(devicesTitle2, idComparator); 662 Collections.sort(devicesTitle2, idComparator);
659 Collections.sort(loadedDevicesTitle2, idComparator); 663 Collections.sort(loadedDevicesTitle2, idComparator);
660 - 664 +
661 Assert.assertEquals(devicesTitle2, loadedDevicesTitle2); 665 Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
662 - 666 +
663 for (Device device : loadedDevicesTitle1) { 667 for (Device device : loadedDevicesTitle1) {
664 doDelete("/api/customer/device/" + device.getId().getId().toString()) 668 doDelete("/api/customer/device/" + device.getId().getId().toString())
665 - .andExpect(status().isOk()); 669 + .andExpect(status().isOk());
666 } 670 }
667 -  
668 pageLink = new PageLink(4, 0, title1); 671 pageLink = new PageLink(4, 0, title1);
669 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", 672 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
670 new TypeReference<PageData<Device>>(){}, pageLink); 673 new TypeReference<PageData<Device>>(){}, pageLink);
671 Assert.assertFalse(pageData.hasNext()); 674 Assert.assertFalse(pageData.hasNext());
672 Assert.assertEquals(0, pageData.getData().size()); 675 Assert.assertEquals(0, pageData.getData().size());
673 - 676 +
674 for (Device device : loadedDevicesTitle2) { 677 for (Device device : loadedDevicesTitle2) {
675 doDelete("/api/customer/device/" + device.getId().getId().toString()) 678 doDelete("/api/customer/device/" + device.getId().getId().toString())
676 - .andExpect(status().isOk()); 679 + .andExpect(status().isOk());
677 } 680 }
678 -  
679 pageLink = new PageLink(4, 0, title2); 681 pageLink = new PageLink(4, 0, title2);
680 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", 682 pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
681 new TypeReference<PageData<Device>>(){}, pageLink); 683 new TypeReference<PageData<Device>>(){}, pageLink);
@@ -693,10 +695,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -693,10 +695,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
693 String title1 = "Device title 1"; 695 String title1 = "Device title 1";
694 String type1 = "typeC"; 696 String type1 = "typeC";
695 List<Device> devicesType1 = new ArrayList<>(); 697 List<Device> devicesType1 = new ArrayList<>();
696 - for (int i=0;i<125;i++) { 698 + for (int i = 0; i < 125; i++) {
697 Device device = new Device(); 699 Device device = new Device();
698 String suffix = RandomStringUtils.randomAlphanumeric(15); 700 String suffix = RandomStringUtils.randomAlphanumeric(15);
699 - String name = title1+suffix; 701 + String name = title1 + suffix;
700 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 702 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
701 device.setName(name); 703 device.setName(name);
702 device.setType(type1); 704 device.setType(type1);
@@ -707,10 +709,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -707,10 +709,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
707 String title2 = "Device title 2"; 709 String title2 = "Device title 2";
708 String type2 = "typeD"; 710 String type2 = "typeD";
709 List<Device> devicesType2 = new ArrayList<>(); 711 List<Device> devicesType2 = new ArrayList<>();
710 - for (int i=0;i<143;i++) { 712 + for (int i = 0; i < 143; i++) {
711 Device device = new Device(); 713 Device device = new Device();
712 String suffix = RandomStringUtils.randomAlphanumeric(15); 714 String suffix = RandomStringUtils.randomAlphanumeric(15);
713 - String name = title2+suffix; 715 + String name = title2 + suffix;
714 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); 716 name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
715 device.setName(name); 717 device.setName(name);
716 device.setType(type2); 718 device.setType(type2);
@@ -775,4 +777,54 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @@ -775,4 +777,54 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
775 Assert.assertEquals(0, pageData.getData().size()); 777 Assert.assertEquals(0, pageData.getData().size());
776 } 778 }
777 779
  780 + @Test
  781 + public void testAssignDeviceToTenant() throws Exception {
  782 + Device device = new Device();
  783 + device.setName("My device");
  784 + device.setType("default");
  785 + Device savedDevice = doPost("/api/device", device, Device.class);
  786 +
  787 + Device anotherDevice = new Device();
  788 + anotherDevice.setName("My device1");
  789 + anotherDevice.setType("default");
  790 + Device savedAnotherDevice = doPost("/api/device", anotherDevice, Device.class);
  791 +
  792 + EntityRelation relation = new EntityRelation();
  793 + relation.setFrom(savedDevice.getId());
  794 + relation.setTo(savedAnotherDevice.getId());
  795 + relation.setTypeGroup(RelationTypeGroup.COMMON);
  796 + relation.setType("Contains");
  797 + doPost("/api/relation", relation).andExpect(status().isOk());
  798 +
  799 + loginSysAdmin();
  800 + Tenant tenant = new Tenant();
  801 + tenant.setTitle("Different tenant");
  802 + Tenant savedDifferentTenant = doPost("/api/tenant", tenant, Tenant.class);
  803 + Assert.assertNotNull(savedDifferentTenant);
  804 +
  805 + User user = new User();
  806 + user.setAuthority(Authority.TENANT_ADMIN);
  807 + user.setTenantId(savedDifferentTenant.getId());
  808 + user.setEmail("tenant9@thingsboard.org");
  809 + user.setFirstName("Sam");
  810 + user.setLastName("Downs");
  811 +
  812 + createUserAndLogin(user, "testPassword1");
  813 +
  814 + login("tenant2@thingsboard.org", "testPassword1");
  815 + Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class);
  816 +
  817 + doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class, status().isNotFound());
  818 +
  819 + login("tenant9@thingsboard.org", "testPassword1");
  820 +
  821 + Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class);
  822 + Assert.assertNotNull(foundDevice1);
  823 +
  824 + doGet("/api/relation?fromId=" + savedDevice.getId().getId() + "&fromType=DEVICE&relationType=Contains&toId=" + savedAnotherDevice.getId().getId() + "&toType=DEVICE", EntityRelation.class, status().isNotFound());
  825 +
  826 + loginSysAdmin();
  827 + doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString())
  828 + .andExpect(status().isOk());
  829 + }
778 } 830 }
@@ -41,7 +41,9 @@ public class MqttNoSqlTestSuite { @@ -41,7 +41,9 @@ public class MqttNoSqlTestSuite {
41 public static CustomCassandraCQLUnit cassandraUnit = 41 public static CustomCassandraCQLUnit cassandraUnit =
42 new CustomCassandraCQLUnit( 42 new CustomCassandraCQLUnit(
43 Arrays.asList( 43 Arrays.asList(
44 - new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false)), 44 + new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
  45 + new ClassPathCQLDataSet("cassandra/schema-ts-latest.cql", false, false)
  46 + ),
45 "cassandra-test.yaml", 30000l); 47 "cassandra-test.yaml", 30000l);
46 48
47 @BeforeClass 49 @BeforeClass
@@ -16,14 +16,12 @@ @@ -16,14 +16,12 @@
16 package org.thingsboard.server.dao.audit; 16 package org.thingsboard.server.dao.audit;
17 17
18 import com.google.common.util.concurrent.ListenableFuture; 18 import com.google.common.util.concurrent.ListenableFuture;
19 -import org.thingsboard.server.common.data.BaseData;  
20 import org.thingsboard.server.common.data.HasName; 19 import org.thingsboard.server.common.data.HasName;
21 import org.thingsboard.server.common.data.audit.ActionType; 20 import org.thingsboard.server.common.data.audit.ActionType;
22 import org.thingsboard.server.common.data.audit.AuditLog; 21 import org.thingsboard.server.common.data.audit.AuditLog;
23 import org.thingsboard.server.common.data.id.CustomerId; 22 import org.thingsboard.server.common.data.id.CustomerId;
24 import org.thingsboard.server.common.data.id.EntityId; 23 import org.thingsboard.server.common.data.id.EntityId;
25 import org.thingsboard.server.common.data.id.TenantId; 24 import org.thingsboard.server.common.data.id.TenantId;
26 -import org.thingsboard.server.common.data.id.UUIDBased;  
27 import org.thingsboard.server.common.data.id.UserId; 25 import org.thingsboard.server.common.data.id.UserId;
28 import org.thingsboard.server.common.data.page.PageData; 26 import org.thingsboard.server.common.data.page.PageData;
29 import org.thingsboard.server.common.data.page.TimePageLink; 27 import org.thingsboard.server.common.data.page.TimePageLink;
@@ -49,5 +47,4 @@ public interface AuditLogService { @@ -49,5 +47,4 @@ public interface AuditLogService {
49 E entity, 47 E entity,
50 ActionType actionType, 48 ActionType actionType,
51 Exception e, Object... additionalInfo); 49 Exception e, Object... additionalInfo);
52 -  
53 } 50 }
@@ -76,4 +76,6 @@ public interface DeviceService { @@ -76,4 +76,6 @@ public interface DeviceService {
76 76
77 ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId); 77 ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId);
78 78
  79 + Device assignDeviceToTenant(TenantId tenantId, Device device);
  80 +
79 } 81 }
@@ -41,4 +41,6 @@ public interface EventService { @@ -41,4 +41,6 @@ public interface EventService {
41 41
42 List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit); 42 List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit);
43 43
  44 + void removeEvents(TenantId tenantId, EntityId entityId);
  45 +
44 } 46 }
@@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery; @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
24 import org.thingsboard.server.common.data.relation.RelationTypeGroup; 24 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
25 25
26 import java.util.List; 26 import java.util.List;
27 -import java.util.concurrent.ExecutionException;  
28 27
29 /** 28 /**
30 * Created by ashvayka on 27.04.17. 29 * Created by ashvayka on 27.04.17.
@@ -77,6 +76,8 @@ public interface RelationService { @@ -77,6 +76,8 @@ public interface RelationService {
77 76
78 ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query); 77 ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query);
79 78
  79 + void removeRelations(TenantId tenantId, EntityId entityId);
  80 +
80 // TODO: This method may be useful for some validations in the future 81 // TODO: This method may be useful for some validations in the future
81 // ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to); 82 // ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to);
82 83
@@ -21,6 +21,6 @@ import java.lang.annotation.Retention; @@ -21,6 +21,6 @@ import java.lang.annotation.Retention;
21 import java.lang.annotation.RetentionPolicy; 21 import java.lang.annotation.RetentionPolicy;
22 22
23 @Retention(RetentionPolicy.RUNTIME) 23 @Retention(RetentionPolicy.RUNTIME)
24 -@ConditionalOnExpression("'${database.ts.type}'=='cassandra' || '${database.entities.type}'=='cassandra'") 24 +@ConditionalOnExpression("'${database.ts.type}'=='cassandra' || '${database.ts_latest.type}'=='cassandra'")
25 public @interface NoSqlAnyDao { 25 public @interface NoSqlAnyDao {
26 } 26 }
common/dao-api/src/main/java/org/thingsboard/server/dao/util/NoSqlTsLatestDao.java renamed from common/dao-api/src/main/java/org/thingsboard/server/dao/util/SqlDao.java
@@ -15,9 +15,12 @@ @@ -15,9 +15,12 @@
15 */ 15 */
16 package org.thingsboard.server.dao.util; 16 package org.thingsboard.server.dao.util;
17 17
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  19 +
18 import java.lang.annotation.Retention; 20 import java.lang.annotation.Retention;
19 import java.lang.annotation.RetentionPolicy; 21 import java.lang.annotation.RetentionPolicy;
20 22
21 @Retention(RetentionPolicy.RUNTIME) 23 @Retention(RetentionPolicy.RUNTIME)
22 -public @interface SqlDao { 24 +@ConditionalOnProperty(prefix = "database.ts_latest", value = "type", havingValue = "cassandra")
  25 +public @interface NoSqlTsLatestDao {
23 } 26 }
common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlTsLatestAnyDao.java renamed from common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlTsAnyDao.java
@@ -21,7 +21,7 @@ import java.lang.annotation.Retention; @@ -21,7 +21,7 @@ import java.lang.annotation.Retention;
21 import java.lang.annotation.RetentionPolicy; 21 import java.lang.annotation.RetentionPolicy;
22 22
23 @Retention(RetentionPolicy.RUNTIME) 23 @Retention(RetentionPolicy.RUNTIME)
24 -@ConditionalOnExpression("('${database.ts.type}'=='sql' || '${database.ts.type}'=='timescale') " + 24 +@ConditionalOnExpression("('${database.ts_latest.type}'=='sql' || '${database.ts_latest.type}'=='timescale') " +
25 "&& '${spring.jpa.database-platform}'=='org.hibernate.dialect.PostgreSQLDialect'") 25 "&& '${spring.jpa.database-platform}'=='org.hibernate.dialect.PostgreSQLDialect'")
26 -public @interface PsqlTsAnyDao { 26 +public @interface PsqlTsLatestAnyDao {
27 } 27 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.util;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  19 +
  20 +import java.lang.annotation.Retention;
  21 +import java.lang.annotation.RetentionPolicy;
  22 +
  23 +@Retention(RetentionPolicy.RUNTIME)
  24 +@ConditionalOnExpression("'${database.ts_latest.type}'=='sql' || '${database.ts_latest.type}'=='timescale'")
  25 +public @interface SqlTsLatestAnyDao {
  26 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.util;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  19 +
  20 +import java.lang.annotation.Retention;
  21 +import java.lang.annotation.RetentionPolicy;
  22 +
  23 +@Retention(RetentionPolicy.RUNTIME)
  24 +@ConditionalOnProperty(prefix = "database.ts_latest", value = "type", havingValue = "sql")
  25 +public @interface SqlTsLatestDao {
  26 +}
common/dao-api/src/main/java/org/thingsboard/server/dao/util/SqlTsOrTsLatestAnyDao.java renamed from common/dao-api/src/main/java/org/thingsboard/server/dao/util/SqlTsAnyDao.java
@@ -21,6 +21,6 @@ import java.lang.annotation.Retention; @@ -21,6 +21,6 @@ import java.lang.annotation.Retention;
21 import java.lang.annotation.RetentionPolicy; 21 import java.lang.annotation.RetentionPolicy;
22 22
23 @Retention(RetentionPolicy.RUNTIME) 23 @Retention(RetentionPolicy.RUNTIME)
24 -@ConditionalOnExpression("'${database.ts.type}'=='sql' || '${database.ts.type}'=='timescale'")  
25 -public @interface SqlTsAnyDao { 24 +@ConditionalOnExpression("'${database.ts.type}'=='sql' || '${database.ts.type}'=='timescale' || '${database.ts_latest.type}'=='sql' || '${database.ts_latest.type}'=='timescale'")
  25 +public @interface SqlTsOrTsLatestAnyDao {
26 } 26 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.util;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  19 +
  20 +import java.lang.annotation.Retention;
  21 +import java.lang.annotation.RetentionPolicy;
  22 +
  23 +@Retention(RetentionPolicy.RUNTIME)
  24 +@ConditionalOnProperty(prefix = "database.ts_latest", value = "type", havingValue = "timescale")
  25 +public @interface TimescaleDBTsLatestDao {
  26 +}
common/dao-api/src/main/java/org/thingsboard/server/dao/util/TimescaleDBTsOrTsLatestDao.java renamed from common/dao-api/src/main/java/org/thingsboard/server/dao/util/PsqlTsDao.java
@@ -21,5 +21,6 @@ import java.lang.annotation.Retention; @@ -21,5 +21,6 @@ import java.lang.annotation.Retention;
21 import java.lang.annotation.RetentionPolicy; 21 import java.lang.annotation.RetentionPolicy;
22 22
23 @Retention(RetentionPolicy.RUNTIME) 23 @Retention(RetentionPolicy.RUNTIME)
24 -@ConditionalOnExpression("'${database.ts.type}'=='sql' && '${spring.jpa.database-platform}'=='org.hibernate.dialect.PostgreSQLDialect'")  
25 -public @interface PsqlTsDao { }  
  24 +@ConditionalOnExpression("'${database.ts.type}'=='timescale' || '${database.ts_latest.type}'=='timescale'")
  25 +public @interface TimescaleDBTsOrTsLatestDao {
  26 +}
@@ -57,6 +57,8 @@ public class DataConstants { @@ -57,6 +57,8 @@ public class DataConstants {
57 public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED"; 57 public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED";
58 public static final String ALARM_ACK = "ALARM_ACK"; 58 public static final String ALARM_ACK = "ALARM_ACK";
59 public static final String ALARM_CLEAR = "ALARM_CLEAR"; 59 public static final String ALARM_CLEAR = "ALARM_CLEAR";
  60 + public static final String ENTITY_ASSIGNED_FROM_TENANT = "ENTITY_ASSIGNED_FROM_TENANT";
  61 + public static final String ENTITY_ASSIGNED_TO_TENANT = "ENTITY_ASSIGNED_TO_TENANT";
60 62
61 public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; 63 public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE";
62 64
@@ -40,7 +40,9 @@ public enum ActionType { @@ -40,7 +40,9 @@ public enum ActionType {
40 ALARM_CLEAR(false), 40 ALARM_CLEAR(false),
41 LOGIN(false), 41 LOGIN(false),
42 LOGOUT(false), 42 LOGOUT(false),
43 - LOCKOUT(false); 43 + LOCKOUT(false),
  44 + ASSIGNED_FROM_TENANT(false),
  45 + ASSIGNED_TO_TENANT(false);
44 46
45 private final boolean isRead; 47 private final boolean isRead;
46 48
@@ -27,8 +27,8 @@ import org.thingsboard.server.dao.util.SqlTsDao; @@ -27,8 +27,8 @@ import org.thingsboard.server.dao.util.SqlTsDao;
27 @Configuration 27 @Configuration
28 @EnableAutoConfiguration 28 @EnableAutoConfiguration
29 @ComponentScan({"org.thingsboard.server.dao.sqlts.hsql"}) 29 @ComponentScan({"org.thingsboard.server.dao.sqlts.hsql"})
30 -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.hsql", "org.thingsboard.server.dao.sqlts.insert.latest.hsql", "org.thingsboard.server.dao.sqlts.latest", "org.thingsboard.server.dao.sqlts.dictionary"})  
31 -@EntityScan({"org.thingsboard.server.dao.model.sqlts.ts", "org.thingsboard.server.dao.model.sqlts.latest", "org.thingsboard.server.dao.model.sqlts.dictionary"}) 30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.hsql"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.ts"})
32 @EnableTransactionManagement 32 @EnableTransactionManagement
33 @SqlTsDao 33 @SqlTsDao
34 @HsqlDao 34 @HsqlDao
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  19 +import org.springframework.boot.autoconfigure.domain.EntityScan;
  20 +import org.springframework.context.annotation.ComponentScan;
  21 +import org.springframework.context.annotation.Configuration;
  22 +import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  23 +import org.springframework.transaction.annotation.EnableTransactionManagement;
  24 +import org.thingsboard.server.dao.util.HsqlDao;
  25 +import org.thingsboard.server.dao.util.SqlTsLatestDao;
  26 +
  27 +@Configuration
  28 +@EnableAutoConfiguration
  29 +@ComponentScan({"org.thingsboard.server.dao.sqlts.hsql"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.hsql", "org.thingsboard.server.dao.sqlts.latest"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.latest"})
  32 +@EnableTransactionManagement
  33 +@SqlTsLatestDao
  34 +@HsqlDao
  35 +public class HsqlTsLatestDaoConfig {
  36 +
  37 +}
@@ -21,7 +21,6 @@ import org.springframework.context.annotation.ComponentScan; @@ -21,7 +21,6 @@ import org.springframework.context.annotation.ComponentScan;
21 import org.springframework.context.annotation.Configuration; 21 import org.springframework.context.annotation.Configuration;
22 import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 22 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
23 import org.springframework.transaction.annotation.EnableTransactionManagement; 23 import org.springframework.transaction.annotation.EnableTransactionManagement;
24 -import org.thingsboard.server.dao.util.SqlDao;  
25 24
26 /** 25 /**
27 * @author Valerii Sosliuk 26 * @author Valerii Sosliuk
@@ -32,7 +31,6 @@ import org.thingsboard.server.dao.util.SqlDao; @@ -32,7 +31,6 @@ import org.thingsboard.server.dao.util.SqlDao;
32 @EnableJpaRepositories("org.thingsboard.server.dao.sql") 31 @EnableJpaRepositories("org.thingsboard.server.dao.sql")
33 @EntityScan("org.thingsboard.server.dao.model.sql") 32 @EntityScan("org.thingsboard.server.dao.model.sql")
34 @EnableTransactionManagement 33 @EnableTransactionManagement
35 -@SqlDao  
36 public class JpaDaoConfig { 34 public class JpaDaoConfig {
37 35
38 } 36 }
@@ -27,11 +27,11 @@ import org.thingsboard.server.dao.util.SqlTsDao; @@ -27,11 +27,11 @@ import org.thingsboard.server.dao.util.SqlTsDao;
27 @Configuration 27 @Configuration
28 @EnableAutoConfiguration 28 @EnableAutoConfiguration
29 @ComponentScan({"org.thingsboard.server.dao.sqlts.psql"}) 29 @ComponentScan({"org.thingsboard.server.dao.sqlts.psql"})
30 -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.psql", "org.thingsboard.server.dao.sqlts.insert.latest.psql", "org.thingsboard.server.dao.sqlts.latest", "org.thingsboard.server.dao.sqlts.dictionary"})  
31 -@EntityScan({"org.thingsboard.server.dao.model.sqlts.ts", "org.thingsboard.server.dao.model.sqlts.latest", "org.thingsboard.server.dao.model.sqlts.dictionary"}) 30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.ts", "org.thingsboard.server.dao.sqlts.insert.psql"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.ts"})
32 @EnableTransactionManagement 32 @EnableTransactionManagement
33 -@SqlTsDao  
34 @PsqlDao 33 @PsqlDao
  34 +@SqlTsDao
35 public class PsqlTsDaoConfig { 35 public class PsqlTsDaoConfig {
36 36
37 } 37 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  19 +import org.springframework.boot.autoconfigure.domain.EntityScan;
  20 +import org.springframework.context.annotation.ComponentScan;
  21 +import org.springframework.context.annotation.Configuration;
  22 +import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  23 +import org.springframework.transaction.annotation.EnableTransactionManagement;
  24 +import org.thingsboard.server.dao.util.PsqlDao;
  25 +import org.thingsboard.server.dao.util.SqlTsLatestDao;
  26 +
  27 +@Configuration
  28 +@EnableAutoConfiguration
  29 +@ComponentScan({"org.thingsboard.server.dao.sqlts.psql"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.psql", "org.thingsboard.server.dao.sqlts.latest"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.latest"})
  32 +@EnableTransactionManagement
  33 +@SqlTsLatestDao
  34 +@PsqlDao
  35 +public class PsqlTsLatestDaoConfig {
  36 +
  37 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  19 +import org.springframework.boot.autoconfigure.domain.EntityScan;
  20 +import org.springframework.context.annotation.Configuration;
  21 +import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  22 +import org.springframework.transaction.annotation.EnableTransactionManagement;
  23 +import org.thingsboard.server.dao.util.PsqlDao;
  24 +import org.thingsboard.server.dao.util.SqlTsOrTsLatestAnyDao;
  25 +
  26 +@Configuration
  27 +@EnableAutoConfiguration
  28 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.dictionary"})
  29 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.dictionary"})
  30 +@EnableTransactionManagement
  31 +@SqlTsOrTsLatestAnyDao
  32 +public class SqlTimeseriesDaoConfig {
  33 +
  34 +}
@@ -27,8 +27,8 @@ import org.thingsboard.server.dao.util.TimescaleDBTsDao; @@ -27,8 +27,8 @@ import org.thingsboard.server.dao.util.TimescaleDBTsDao;
27 @Configuration 27 @Configuration
28 @EnableAutoConfiguration 28 @EnableAutoConfiguration
29 @ComponentScan({"org.thingsboard.server.dao.sqlts.timescale"}) 29 @ComponentScan({"org.thingsboard.server.dao.sqlts.timescale"})
30 -@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.timescale", "org.thingsboard.server.dao.sqlts.insert.latest.psql", "org.thingsboard.server.dao.sqlts.insert.timescale", "org.thingsboard.server.dao.sqlts.dictionary", "org.thingsboard.server.dao.sqlts.latest"})  
31 -@EntityScan({"org.thingsboard.server.dao.model.sqlts.timescale", "org.thingsboard.server.dao.model.sqlts.dictionary", "org.thingsboard.server.dao.model.sqlts.latest"}) 30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.timescale", "org.thingsboard.server.dao.sqlts.insert.timescale"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.timescale"})
32 @EnableTransactionManagement 32 @EnableTransactionManagement
33 @TimescaleDBTsDao 33 @TimescaleDBTsDao
34 @PsqlDao 34 @PsqlDao
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  19 +import org.springframework.boot.autoconfigure.domain.EntityScan;
  20 +import org.springframework.context.annotation.ComponentScan;
  21 +import org.springframework.context.annotation.Configuration;
  22 +import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  23 +import org.springframework.transaction.annotation.EnableTransactionManagement;
  24 +import org.thingsboard.server.dao.util.PsqlDao;
  25 +import org.thingsboard.server.dao.util.TimescaleDBTsLatestDao;
  26 +
  27 +@Configuration
  28 +@EnableAutoConfiguration
  29 +@ComponentScan({"org.thingsboard.server.dao.sqlts.timescale"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.insert.latest.psql", "org.thingsboard.server.dao.sqlts.latest"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.latest"})
  32 +@EnableTransactionManagement
  33 +@TimescaleDBTsLatestDao
  34 +@PsqlDao
  35 +public class TimescaleTsLatestDaoConfig {
  36 +
  37 +}
@@ -163,7 +163,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ @@ -163,7 +163,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
163 try { 163 try {
164 List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get(); 164 List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(asset.getTenantId(), assetId).get();
165 if (entityViews != null && !entityViews.isEmpty()) { 165 if (entityViews != null && !entityViews.isEmpty()) {
166 - throw new DataValidationException("Can't delete asset that is assigned to entity views!"); 166 + throw new DataValidationException("Can't delete asset that has entity views!");
167 } 167 }
168 } catch (ExecutionException | InterruptedException e) { 168 } catch (ExecutionException | InterruptedException e) {
169 log.error("Exception while finding entity views for assetId [{}]", assetId, e); 169 log.error("Exception while finding entity views for assetId [{}]", assetId, e);
@@ -23,11 +23,12 @@ import org.thingsboard.server.common.data.id.EntityId; @@ -23,11 +23,12 @@ import org.thingsboard.server.common.data.id.EntityId;
23 import org.thingsboard.server.common.data.id.UserId; 23 import org.thingsboard.server.common.data.id.UserId;
24 import org.thingsboard.server.common.data.page.PageData; 24 import org.thingsboard.server.common.data.page.PageData;
25 import org.thingsboard.server.common.data.page.TimePageLink; 25 import org.thingsboard.server.common.data.page.TimePageLink;
  26 +import org.thingsboard.server.dao.Dao;
26 27
27 import java.util.List; 28 import java.util.List;
28 import java.util.UUID; 29 import java.util.UUID;
29 30
30 -public interface AuditLogDao { 31 +public interface AuditLogDao extends Dao<AuditLog> {
31 32
32 ListenableFuture<Void> saveByTenantId(AuditLog auditLog); 33 ListenableFuture<Void> saveByTenantId(AuditLog auditLog);
33 34
@@ -163,6 +163,7 @@ public class AuditLogServiceImpl implements AuditLogService { @@ -163,6 +163,7 @@ public class AuditLogServiceImpl implements AuditLogService {
163 case ALARM_ACK: 163 case ALARM_ACK:
164 case ALARM_CLEAR: 164 case ALARM_CLEAR:
165 case RELATIONS_DELETED: 165 case RELATIONS_DELETED:
  166 + case ASSIGNED_TO_TENANT:
166 if (entity != null) { 167 if (entity != null) {
167 ObjectNode entityNode = objectMapper.valueToTree(entity); 168 ObjectNode entityNode = objectMapper.valueToTree(entity);
168 if (entityId.getEntityType() == EntityType.DASHBOARD) { 169 if (entityId.getEntityType() == EntityType.DASHBOARD) {
@@ -58,5 +58,4 @@ public class DummyAuditLogServiceImpl implements AuditLogService { @@ -58,5 +58,4 @@ public class DummyAuditLogServiceImpl implements AuditLogService {
58 public <E extends HasName, I extends EntityId> ListenableFuture<List<Void>> logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) { 58 public <E extends HasName, I extends EntityId> ListenableFuture<List<Void>> logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity, ActionType actionType, Exception e, Object... additionalInfo) {
59 return null; 59 return null;
60 } 60 }
61 -  
62 } 61 }
@@ -166,4 +166,21 @@ public interface DeviceDao extends Dao<Device> { @@ -166,4 +166,21 @@ public interface DeviceDao extends Dao<Device> {
166 * @return the list of tenant device type objects 166 * @return the list of tenant device type objects
167 */ 167 */
168 ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId); 168 ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId);
  169 +
  170 + /**
  171 + * Find devices by tenantId and device id.
  172 + * @param tenantId the tenant Id
  173 + * @param id the device Id
  174 + * @return the device object
  175 + */
  176 + Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id);
  177 +
  178 + /**
  179 + * Find devices by tenantId and device id.
  180 + * @param tenantId tenantId the tenantId
  181 + * @param id the deviceId
  182 + * @return the device object
  183 + */
  184 + ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id);
  185 +
169 } 186 }
@@ -28,6 +28,8 @@ import org.springframework.cache.CacheManager; @@ -28,6 +28,8 @@ import org.springframework.cache.CacheManager;
28 import org.springframework.cache.annotation.CacheEvict; 28 import org.springframework.cache.annotation.CacheEvict;
29 import org.springframework.cache.annotation.Cacheable; 29 import org.springframework.cache.annotation.Cacheable;
30 import org.springframework.stereotype.Service; 30 import org.springframework.stereotype.Service;
  31 +import org.springframework.transaction.annotation.Transactional;
  32 +import org.springframework.util.CollectionUtils;
31 import org.springframework.util.StringUtils; 33 import org.springframework.util.StringUtils;
32 import org.thingsboard.server.common.data.Customer; 34 import org.thingsboard.server.common.data.Customer;
33 import org.thingsboard.server.common.data.Device; 35 import org.thingsboard.server.common.data.Device;
@@ -50,6 +52,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType; @@ -50,6 +52,7 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType;
50 import org.thingsboard.server.dao.customer.CustomerDao; 52 import org.thingsboard.server.dao.customer.CustomerDao;
51 import org.thingsboard.server.dao.entity.AbstractEntityService; 53 import org.thingsboard.server.dao.entity.AbstractEntityService;
52 import org.thingsboard.server.dao.entityview.EntityViewService; 54 import org.thingsboard.server.dao.entityview.EntityViewService;
  55 +import org.thingsboard.server.dao.event.EventService;
53 import org.thingsboard.server.dao.exception.DataValidationException; 56 import org.thingsboard.server.dao.exception.DataValidationException;
54 import org.thingsboard.server.dao.service.DataValidator; 57 import org.thingsboard.server.dao.service.DataValidator;
55 import org.thingsboard.server.dao.service.PaginatedRemover; 58 import org.thingsboard.server.dao.service.PaginatedRemover;
@@ -98,6 +101,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -98,6 +101,9 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
98 @Autowired 101 @Autowired
99 private CacheManager cacheManager; 102 private CacheManager cacheManager;
100 103
  104 + @Autowired
  105 + private EventService eventService;
  106 +
101 @Override 107 @Override
102 public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) { 108 public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) {
103 log.trace("Executing findDeviceInfoById [{}]", deviceId); 109 log.trace("Executing findDeviceInfoById [{}]", deviceId);
@@ -109,14 +115,22 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -109,14 +115,22 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
109 public Device findDeviceById(TenantId tenantId, DeviceId deviceId) { 115 public Device findDeviceById(TenantId tenantId, DeviceId deviceId) {
110 log.trace("Executing findDeviceById [{}]", deviceId); 116 log.trace("Executing findDeviceById [{}]", deviceId);
111 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); 117 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
112 - return deviceDao.findById(tenantId, deviceId.getId()); 118 + if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
  119 + return deviceDao.findById(tenantId, deviceId.getId());
  120 + } else {
  121 + return deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId());
  122 + }
113 } 123 }
114 124
115 @Override 125 @Override
116 public ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) { 126 public ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) {
117 log.trace("Executing findDeviceById [{}]", deviceId); 127 log.trace("Executing findDeviceById [{}]", deviceId);
118 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId); 128 validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
119 - return deviceDao.findByIdAsync(tenantId, deviceId.getId()); 129 + if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
  130 + return deviceDao.findByIdAsync(tenantId, deviceId.getId());
  131 + } else {
  132 + return deviceDao.findDeviceByTenantIdAndIdAsync(tenantId, deviceId.getId());
  133 + }
120 } 134 }
121 135
122 @Cacheable(cacheNames = DEVICE_CACHE, key = "{#tenantId, #name}") 136 @Cacheable(cacheNames = DEVICE_CACHE, key = "{#tenantId, #name}")
@@ -187,7 +201,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -187,7 +201,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
187 try { 201 try {
188 List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get(); 202 List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), deviceId).get();
189 if (entityViews != null && !entityViews.isEmpty()) { 203 if (entityViews != null && !entityViews.isEmpty()) {
190 - throw new DataValidationException("Can't delete device that is assigned to entity views!"); 204 + throw new DataValidationException("Can't delete device that has entity views!");
191 } 205 }
192 } catch (ExecutionException | InterruptedException e) { 206 } catch (ExecutionException | InterruptedException e) {
193 log.error("Exception while finding entity views for deviceId [{}]", deviceId, e); 207 log.error("Exception while finding entity views for deviceId [{}]", deviceId, e);
@@ -353,6 +367,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @@ -353,6 +367,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
353 }, MoreExecutors.directExecutor()); 367 }, MoreExecutors.directExecutor());
354 } 368 }
355 369
  370 + @Transactional
  371 + @CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}")
  372 + @Override
  373 + public Device assignDeviceToTenant(TenantId tenantId, Device device) {
  374 + log.trace("Executing assignDeviceToTenant [{}][{}]", tenantId, device);
  375 +
  376 + try {
  377 + List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), device.getId()).get();
  378 + if (!CollectionUtils.isEmpty(entityViews)) {
  379 + throw new DataValidationException("Can't assign device that has entity views to another tenant!");
  380 + }
  381 + } catch (ExecutionException | InterruptedException e) {
  382 + log.error("Exception while finding entity views for deviceId [{}]", device.getId(), e);
  383 + throw new RuntimeException("Exception while finding entity views for deviceId [" + device.getId() + "]", e);
  384 + }
  385 +
  386 + eventService.removeEvents(device.getTenantId(), device.getId());
  387 +
  388 + relationService.removeRelations(device.getTenantId(), device.getId());
  389 +
  390 + device.setTenantId(tenantId);
  391 + device.setCustomerId(null);
  392 + return doSaveDevice(device, null);
  393 + }
  394 +
356 private DataValidator<Device> deviceValidator = 395 private DataValidator<Device> deviceValidator =
357 new DataValidator<Device>() { 396 new DataValidator<Device>() {
358 397
@@ -92,6 +92,21 @@ public class BaseEventService implements EventService { @@ -92,6 +92,21 @@ public class BaseEventService implements EventService {
92 return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit); 92 return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit);
93 } 93 }
94 94
  95 + @Override
  96 + public void removeEvents(TenantId tenantId, EntityId entityId) {
  97 + PageData<Event> eventPageData;
  98 + TimePageLink eventPageLink = new TimePageLink(1000);
  99 + do {
  100 + eventPageData = findEvents(tenantId, entityId, eventPageLink);
  101 + for (Event event : eventPageData.getData()) {
  102 + eventDao.removeById(tenantId, event.getUuidId());
  103 + }
  104 + if (eventPageData.hasNext()) {
  105 + eventPageLink = eventPageLink.nextPageLink();
  106 + }
  107 + } while (eventPageData.hasNext());
  108 + }
  109 +
95 private DataValidator<Event> eventValidator = 110 private DataValidator<Event> eventValidator =
96 new DataValidator<Event>() { 111 new DataValidator<Event>() {
97 @Override 112 @Override
@@ -506,6 +506,22 @@ public class BaseRelationService implements RelationService { @@ -506,6 +506,22 @@ public class BaseRelationService implements RelationService {
506 }, MoreExecutors.directExecutor()); 506 }, MoreExecutors.directExecutor());
507 } 507 }
508 508
  509 + @Override
  510 + public void removeRelations(TenantId tenantId, EntityId entityId) {
  511 + Cache cache = cacheManager.getCache(RELATIONS_CACHE);
  512 +
  513 + List<EntityRelation> relations = new ArrayList<>();
  514 + for (RelationTypeGroup relationTypeGroup : RelationTypeGroup.values()) {
  515 + relations.addAll(findByFrom(tenantId, entityId, relationTypeGroup));
  516 + relations.addAll(findByTo(tenantId, entityId, relationTypeGroup));
  517 + }
  518 +
  519 + for (EntityRelation relation : relations) {
  520 + cacheEviction(relation, cache);
  521 + deleteRelation(tenantId, relation);
  522 + }
  523 + }
  524 +
509 protected void validate(EntityRelation relation) { 525 protected void validate(EntityRelation relation) {
510 if (relation == null) { 526 if (relation == null) {
511 throw new DataValidationException("Relation type should be specified!"); 527 throw new DataValidationException("Relation type should be specified!");
@@ -18,10 +18,8 @@ package org.thingsboard.server.dao.sql; @@ -18,10 +18,8 @@ package org.thingsboard.server.dao.sql;
18 import org.springframework.beans.factory.annotation.Value; 18 import org.springframework.beans.factory.annotation.Value;
19 import org.springframework.stereotype.Component; 19 import org.springframework.stereotype.Component;
20 import org.thingsboard.common.util.AbstractListeningExecutor; 20 import org.thingsboard.common.util.AbstractListeningExecutor;
21 -import org.thingsboard.server.dao.util.SqlDao;  
22 21
23 @Component 22 @Component
24 -@SqlDao  
25 public class JpaExecutorService extends AbstractListeningExecutor { 23 public class JpaExecutorService extends AbstractListeningExecutor {
26 24
27 @Value("${spring.datasource.hikari.maximumPoolSize}") 25 @Value("${spring.datasource.hikari.maximumPoolSize}")
@@ -21,25 +21,15 @@ import org.springframework.data.jpa.repository.Query; @@ -21,25 +21,15 @@ import org.springframework.data.jpa.repository.Query;
21 import org.springframework.data.repository.CrudRepository; 21 import org.springframework.data.repository.CrudRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.common.data.alarm.AlarmStatus; 23 import org.thingsboard.server.common.data.alarm.AlarmStatus;
24 -import org.thingsboard.server.common.data.id.CustomerId;  
25 -import org.thingsboard.server.common.data.id.EntityId;  
26 -import org.thingsboard.server.common.data.id.TenantId;  
27 -import org.thingsboard.server.common.data.page.PageData;  
28 -import org.thingsboard.server.common.data.query.AlarmData;  
29 -import org.thingsboard.server.common.data.query.AlarmDataQuery;  
30 import org.thingsboard.server.dao.model.sql.AlarmEntity; 24 import org.thingsboard.server.dao.model.sql.AlarmEntity;
31 import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; 25 import org.thingsboard.server.dao.model.sql.AlarmInfoEntity;
32 -import org.thingsboard.server.dao.util.SqlDao;  
33 26
34 -import java.util.Collection;  
35 import java.util.List; 27 import java.util.List;
36 -import java.util.Set;  
37 import java.util.UUID; 28 import java.util.UUID;
38 29
39 /** 30 /**
40 * Created by Valerii Sosliuk on 5/21/2017. 31 * Created by Valerii Sosliuk on 5/21/2017.
41 */ 32 */
42 -@SqlDao  
43 public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> { 33 public interface AlarmRepository extends CrudRepository<AlarmEntity, UUID> {
44 34
45 @Query("SELECT a FROM AlarmEntity a WHERE a.originatorId = :originatorId AND a.type = :alarmType ORDER BY a.startTs DESC") 35 @Query("SELECT a FROM AlarmEntity a WHERE a.originatorId = :originatorId AND a.type = :alarmType ORDER BY a.startTs DESC")
@@ -39,7 +39,6 @@ import org.thingsboard.server.dao.model.sql.AlarmEntity; @@ -39,7 +39,6 @@ import org.thingsboard.server.dao.model.sql.AlarmEntity;
39 import org.thingsboard.server.dao.relation.RelationDao; 39 import org.thingsboard.server.dao.relation.RelationDao;
40 import org.thingsboard.server.dao.sql.JpaAbstractDao; 40 import org.thingsboard.server.dao.sql.JpaAbstractDao;
41 import org.thingsboard.server.dao.sql.query.AlarmQueryRepository; 41 import org.thingsboard.server.dao.sql.query.AlarmQueryRepository;
42 -import org.thingsboard.server.dao.util.SqlDao;  
43 42
44 import java.util.ArrayList; 43 import java.util.ArrayList;
45 import java.util.Collection; 44 import java.util.Collection;
@@ -54,7 +53,6 @@ import java.util.UUID; @@ -54,7 +53,6 @@ import java.util.UUID;
54 */ 53 */
55 @Slf4j 54 @Slf4j
56 @Component 55 @Component
57 -@SqlDao  
58 public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements AlarmDao { 56 public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements AlarmDao {
59 57
60 @Autowired 58 @Autowired
@@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository; @@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.dao.model.sql.AssetEntity; 23 import org.thingsboard.server.dao.model.sql.AssetEntity;
24 import org.thingsboard.server.dao.model.sql.AssetInfoEntity; 24 import org.thingsboard.server.dao.model.sql.AssetInfoEntity;
25 -import org.thingsboard.server.dao.util.SqlDao;  
26 25
27 import java.util.List; 26 import java.util.List;
28 import java.util.UUID; 27 import java.util.UUID;
@@ -30,7 +29,6 @@ import java.util.UUID; @@ -30,7 +29,6 @@ import java.util.UUID;
30 /** 29 /**
31 * Created by Valerii Sosliuk on 5/21/2017. 30 * Created by Valerii Sosliuk on 5/21/2017.
32 */ 31 */
33 -@SqlDao  
34 public interface AssetRepository extends PagingAndSortingRepository<AssetEntity, UUID> { 32 public interface AssetRepository extends PagingAndSortingRepository<AssetEntity, UUID> {
35 33
36 @Query("SELECT new org.thingsboard.server.dao.model.sql.AssetInfoEntity(a, c.title, c.additionalInfo) " + 34 @Query("SELECT new org.thingsboard.server.dao.model.sql.AssetInfoEntity(a, c.title, c.additionalInfo) " +
@@ -31,7 +31,6 @@ import org.thingsboard.server.dao.asset.AssetDao; @@ -31,7 +31,6 @@ import org.thingsboard.server.dao.asset.AssetDao;
31 import org.thingsboard.server.dao.model.sql.AssetEntity; 31 import org.thingsboard.server.dao.model.sql.AssetEntity;
32 import org.thingsboard.server.dao.model.sql.AssetInfoEntity; 32 import org.thingsboard.server.dao.model.sql.AssetInfoEntity;
33 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 33 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
34 -import org.thingsboard.server.dao.util.SqlDao;  
35 34
36 import java.util.ArrayList; 35 import java.util.ArrayList;
37 import java.util.Collections; 36 import java.util.Collections;
@@ -44,7 +43,6 @@ import java.util.UUID; @@ -44,7 +43,6 @@ import java.util.UUID;
44 * Created by Valerii Sosliuk on 5/19/2017. 43 * Created by Valerii Sosliuk on 5/19/2017.
45 */ 44 */
46 @Component 45 @Component
47 -@SqlDao  
48 public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> implements AssetDao { 46 public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> implements AssetDao {
49 47
50 @Autowired 48 @Autowired
@@ -25,7 +25,6 @@ import org.springframework.transaction.TransactionStatus; @@ -25,7 +25,6 @@ import org.springframework.transaction.TransactionStatus;
25 import org.springframework.transaction.support.TransactionCallbackWithoutResult; 25 import org.springframework.transaction.support.TransactionCallbackWithoutResult;
26 import org.springframework.transaction.support.TransactionTemplate; 26 import org.springframework.transaction.support.TransactionTemplate;
27 import org.thingsboard.server.dao.model.sql.AttributeKvEntity; 27 import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
28 -import org.thingsboard.server.dao.util.SqlDao;  
29 28
30 import java.sql.PreparedStatement; 29 import java.sql.PreparedStatement;
31 import java.sql.SQLException; 30 import java.sql.SQLException;
@@ -35,7 +34,6 @@ import java.util.ArrayList; @@ -35,7 +34,6 @@ import java.util.ArrayList;
35 import java.util.List; 34 import java.util.List;
36 import java.util.regex.Pattern; 35 import java.util.regex.Pattern;
37 36
38 -@SqlDao  
39 @Repository 37 @Repository
40 @Slf4j 38 @Slf4j
41 public abstract class AttributeKvInsertRepository { 39 public abstract class AttributeKvInsertRepository {
@@ -23,12 +23,10 @@ import org.springframework.transaction.annotation.Transactional; @@ -23,12 +23,10 @@ import org.springframework.transaction.annotation.Transactional;
23 import org.thingsboard.server.common.data.EntityType; 23 import org.thingsboard.server.common.data.EntityType;
24 import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; 24 import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey;
25 import org.thingsboard.server.dao.model.sql.AttributeKvEntity; 25 import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
26 -import org.thingsboard.server.dao.util.SqlDao;  
27 26
28 import java.util.List; 27 import java.util.List;
29 import java.util.UUID; 28 import java.util.UUID;
30 29
31 -@SqlDao  
32 public interface AttributeKvRepository extends CrudRepository<AttributeKvEntity, AttributeKvCompositeKey> { 30 public interface AttributeKvRepository extends CrudRepository<AttributeKvEntity, AttributeKvCompositeKey> {
33 31
34 @Query("SELECT a FROM AttributeKvEntity a WHERE a.id.entityType = :entityType " + 32 @Query("SELECT a FROM AttributeKvEntity a WHERE a.id.entityType = :entityType " +
@@ -19,12 +19,10 @@ import org.springframework.stereotype.Repository; @@ -19,12 +19,10 @@ import org.springframework.stereotype.Repository;
19 import org.springframework.transaction.annotation.Transactional; 19 import org.springframework.transaction.annotation.Transactional;
20 import org.thingsboard.server.dao.model.sql.AttributeKvEntity; 20 import org.thingsboard.server.dao.model.sql.AttributeKvEntity;
21 import org.thingsboard.server.dao.util.HsqlDao; 21 import org.thingsboard.server.dao.util.HsqlDao;
22 -import org.thingsboard.server.dao.util.SqlDao;  
23 22
24 import java.sql.Types; 23 import java.sql.Types;
25 import java.util.List; 24 import java.util.List;
26 25
27 -@SqlDao  
28 @HsqlDao 26 @HsqlDao
29 @Repository 27 @Repository
30 @Transactional 28 @Transactional
@@ -34,7 +34,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; @@ -34,7 +34,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
34 import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; 34 import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;
35 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams; 35 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
36 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper; 36 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper;
37 -import org.thingsboard.server.dao.util.SqlDao;  
38 37
39 import javax.annotation.PostConstruct; 38 import javax.annotation.PostConstruct;
40 import javax.annotation.PreDestroy; 39 import javax.annotation.PreDestroy;
@@ -46,7 +45,6 @@ import java.util.stream.Collectors; @@ -46,7 +45,6 @@ import java.util.stream.Collectors;
46 45
47 @Component 46 @Component
48 @Slf4j 47 @Slf4j
49 -@SqlDao  
50 public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService implements AttributesDao { 48 public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService implements AttributesDao {
51 49
52 @Autowired 50 @Autowired
@@ -18,9 +18,7 @@ package org.thingsboard.server.dao.sql.attributes; @@ -18,9 +18,7 @@ package org.thingsboard.server.dao.sql.attributes;
18 import org.springframework.stereotype.Repository; 18 import org.springframework.stereotype.Repository;
19 import org.springframework.transaction.annotation.Transactional; 19 import org.springframework.transaction.annotation.Transactional;
20 import org.thingsboard.server.dao.util.PsqlDao; 20 import org.thingsboard.server.dao.util.PsqlDao;
21 -import org.thingsboard.server.dao.util.SqlDao;  
22 21
23 -@SqlDao  
24 @PsqlDao 22 @PsqlDao
25 @Repository 23 @Repository
26 @Transactional 24 @Transactional
@@ -30,14 +30,12 @@ import org.thingsboard.server.dao.DaoUtil; @@ -30,14 +30,12 @@ import org.thingsboard.server.dao.DaoUtil;
30 import org.thingsboard.server.dao.audit.AuditLogDao; 30 import org.thingsboard.server.dao.audit.AuditLogDao;
31 import org.thingsboard.server.dao.model.sql.AuditLogEntity; 31 import org.thingsboard.server.dao.model.sql.AuditLogEntity;
32 import org.thingsboard.server.dao.sql.JpaAbstractDao; 32 import org.thingsboard.server.dao.sql.JpaAbstractDao;
33 -import org.thingsboard.server.dao.util.SqlDao;  
34 33
35 import java.util.List; 34 import java.util.List;
36 import java.util.Objects; 35 import java.util.Objects;
37 import java.util.UUID; 36 import java.util.UUID;
38 37
39 @Component 38 @Component
40 -@SqlDao  
41 public class JpaAuditLogDao extends JpaAbstractDao<AuditLogEntity, AuditLog> implements AuditLogDao { 39 public class JpaAuditLogDao extends JpaAbstractDao<AuditLogEntity, AuditLog> implements AuditLogDao {
42 40
43 @Autowired 41 @Autowired
@@ -23,14 +23,12 @@ import org.springframework.data.repository.query.Param; @@ -23,14 +23,12 @@ import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.common.data.plugin.ComponentScope; 23 import org.thingsboard.server.common.data.plugin.ComponentScope;
24 import org.thingsboard.server.common.data.plugin.ComponentType; 24 import org.thingsboard.server.common.data.plugin.ComponentType;
25 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; 25 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity;
26 -import org.thingsboard.server.dao.util.SqlDao;  
27 26
28 import java.util.UUID; 27 import java.util.UUID;
29 28
30 /** 29 /**
31 * Created by Valerii Sosliuk on 5/6/2017. 30 * Created by Valerii Sosliuk on 5/6/2017.
32 */ 31 */
33 -@SqlDao  
34 public interface ComponentDescriptorRepository extends PagingAndSortingRepository<ComponentDescriptorEntity, UUID> { 32 public interface ComponentDescriptorRepository extends PagingAndSortingRepository<ComponentDescriptorEntity, UUID> {
35 33
36 ComponentDescriptorEntity findByClazz(String clazz); 34 ComponentDescriptorEntity findByClazz(String clazz);
@@ -18,11 +18,9 @@ package org.thingsboard.server.dao.sql.component; @@ -18,11 +18,9 @@ package org.thingsboard.server.dao.sql.component;
18 import org.springframework.stereotype.Repository; 18 import org.springframework.stereotype.Repository;
19 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; 19 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity;
20 import org.thingsboard.server.dao.util.HsqlDao; 20 import org.thingsboard.server.dao.util.HsqlDao;
21 -import org.thingsboard.server.dao.util.SqlDao;  
22 21
23 import javax.persistence.Query; 22 import javax.persistence.Query;
24 23
25 -@SqlDao  
26 @HsqlDao 24 @HsqlDao
27 @Repository 25 @Repository
28 public class HsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository { 26 public class HsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository {
@@ -31,7 +31,6 @@ import org.thingsboard.server.dao.DaoUtil; @@ -31,7 +31,6 @@ import org.thingsboard.server.dao.DaoUtil;
31 import org.thingsboard.server.dao.component.ComponentDescriptorDao; 31 import org.thingsboard.server.dao.component.ComponentDescriptorDao;
32 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; 32 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity;
33 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 33 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
34 -import org.thingsboard.server.dao.util.SqlDao;  
35 34
36 import java.util.Objects; 35 import java.util.Objects;
37 import java.util.Optional; 36 import java.util.Optional;
@@ -41,7 +40,6 @@ import java.util.UUID; @@ -41,7 +40,6 @@ import java.util.UUID;
41 * Created by Valerii Sosliuk on 5/6/2017. 40 * Created by Valerii Sosliuk on 5/6/2017.
42 */ 41 */
43 @Component 42 @Component
44 -@SqlDao  
45 public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao<ComponentDescriptorEntity, ComponentDescriptor> 43 public class JpaBaseComponentDescriptorDao extends JpaAbstractSearchTextDao<ComponentDescriptorEntity, ComponentDescriptor>
46 implements ComponentDescriptorDao { 44 implements ComponentDescriptorDao {
47 45
@@ -18,9 +18,7 @@ package org.thingsboard.server.dao.sql.component; @@ -18,9 +18,7 @@ package org.thingsboard.server.dao.sql.component;
18 import org.springframework.stereotype.Repository; 18 import org.springframework.stereotype.Repository;
19 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity; 19 import org.thingsboard.server.dao.model.sql.ComponentDescriptorEntity;
20 import org.thingsboard.server.dao.util.PsqlDao; 20 import org.thingsboard.server.dao.util.PsqlDao;
21 -import org.thingsboard.server.dao.util.SqlDao;  
22 21
23 -@SqlDao  
24 @PsqlDao 22 @PsqlDao
25 @Repository 23 @Repository
26 public class PsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository { 24 public class PsqlComponentDescriptorInsertRepository extends AbstractComponentDescriptorInsertRepository {
@@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query; @@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query;
21 import org.springframework.data.repository.PagingAndSortingRepository; 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.dao.model.sql.CustomerEntity; 23 import org.thingsboard.server.dao.model.sql.CustomerEntity;
24 -import org.thingsboard.server.dao.util.SqlDao;  
25 24
26 import java.util.UUID; 25 import java.util.UUID;
27 26
28 /** 27 /**
29 * Created by Valerii Sosliuk on 5/6/2017. 28 * Created by Valerii Sosliuk on 5/6/2017.
30 */ 29 */
31 -@SqlDao  
32 public interface CustomerRepository extends PagingAndSortingRepository<CustomerEntity, UUID> { 30 public interface CustomerRepository extends PagingAndSortingRepository<CustomerEntity, UUID> {
33 31
34 @Query("SELECT c FROM CustomerEntity c WHERE c.tenantId = :tenantId " + 32 @Query("SELECT c FROM CustomerEntity c WHERE c.tenantId = :tenantId " +
@@ -25,7 +25,6 @@ import org.thingsboard.server.dao.DaoUtil; @@ -25,7 +25,6 @@ import org.thingsboard.server.dao.DaoUtil;
25 import org.thingsboard.server.dao.customer.CustomerDao; 25 import org.thingsboard.server.dao.customer.CustomerDao;
26 import org.thingsboard.server.dao.model.sql.CustomerEntity; 26 import org.thingsboard.server.dao.model.sql.CustomerEntity;
27 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 27 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
28 -import org.thingsboard.server.dao.util.SqlDao;  
29 28
30 import java.util.Objects; 29 import java.util.Objects;
31 import java.util.Optional; 30 import java.util.Optional;
@@ -35,7 +34,6 @@ import java.util.UUID; @@ -35,7 +34,6 @@ import java.util.UUID;
35 * Created by Valerii Sosliuk on 5/6/2017. 34 * Created by Valerii Sosliuk on 5/6/2017.
36 */ 35 */
37 @Component 36 @Component
38 -@SqlDao  
39 public class JpaCustomerDao extends JpaAbstractSearchTextDao<CustomerEntity, Customer> implements CustomerDao { 37 public class JpaCustomerDao extends JpaAbstractSearchTextDao<CustomerEntity, Customer> implements CustomerDao {
40 38
41 @Autowired 39 @Autowired
@@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query; @@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query;
21 import org.springframework.data.repository.PagingAndSortingRepository; 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; 23 import org.thingsboard.server.dao.model.sql.DashboardInfoEntity;
24 -import org.thingsboard.server.dao.util.SqlDao;  
25 24
26 import java.util.UUID; 25 import java.util.UUID;
27 26
28 /** 27 /**
29 * Created by Valerii Sosliuk on 5/6/2017. 28 * Created by Valerii Sosliuk on 5/6/2017.
30 */ 29 */
31 -@SqlDao  
32 public interface DashboardInfoRepository extends PagingAndSortingRepository<DashboardInfoEntity, UUID> { 30 public interface DashboardInfoRepository extends PagingAndSortingRepository<DashboardInfoEntity, UUID> {
33 31
34 @Query("SELECT di FROM DashboardInfoEntity di WHERE di.tenantId = :tenantId " + 32 @Query("SELECT di FROM DashboardInfoEntity di WHERE di.tenantId = :tenantId " +
@@ -17,13 +17,11 @@ package org.thingsboard.server.dao.sql.dashboard; @@ -17,13 +17,11 @@ package org.thingsboard.server.dao.sql.dashboard;
17 17
18 import org.springframework.data.repository.CrudRepository; 18 import org.springframework.data.repository.CrudRepository;
19 import org.thingsboard.server.dao.model.sql.DashboardEntity; 19 import org.thingsboard.server.dao.model.sql.DashboardEntity;
20 -import org.thingsboard.server.dao.util.SqlDao;  
21 20
22 import java.util.UUID; 21 import java.util.UUID;
23 22
24 /** 23 /**
25 * Created by Valerii Sosliuk on 5/6/2017. 24 * Created by Valerii Sosliuk on 5/6/2017.
26 */ 25 */
27 -@SqlDao  
28 public interface DashboardRepository extends CrudRepository<DashboardEntity, UUID> { 26 public interface DashboardRepository extends CrudRepository<DashboardEntity, UUID> {
29 } 27 }
@@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.Dashboard; @@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.Dashboard;
22 import org.thingsboard.server.dao.dashboard.DashboardDao; 22 import org.thingsboard.server.dao.dashboard.DashboardDao;
23 import org.thingsboard.server.dao.model.sql.DashboardEntity; 23 import org.thingsboard.server.dao.model.sql.DashboardEntity;
24 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 24 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
25 -import org.thingsboard.server.dao.util.SqlDao;  
26 25
27 import java.util.UUID; 26 import java.util.UUID;
28 27
@@ -30,7 +29,6 @@ import java.util.UUID; @@ -30,7 +29,6 @@ import java.util.UUID;
30 * Created by Valerii Sosliuk on 5/6/2017. 29 * Created by Valerii Sosliuk on 5/6/2017.
31 */ 30 */
32 @Component 31 @Component
33 -@SqlDao  
34 public class JpaDashboardDao extends JpaAbstractSearchTextDao<DashboardEntity, Dashboard> implements DashboardDao { 32 public class JpaDashboardDao extends JpaAbstractSearchTextDao<DashboardEntity, Dashboard> implements DashboardDao {
35 33
36 @Autowired 34 @Autowired
@@ -27,7 +27,6 @@ import org.thingsboard.server.dao.dashboard.DashboardInfoDao; @@ -27,7 +27,6 @@ import org.thingsboard.server.dao.dashboard.DashboardInfoDao;
27 import org.thingsboard.server.dao.model.sql.DashboardInfoEntity; 27 import org.thingsboard.server.dao.model.sql.DashboardInfoEntity;
28 import org.thingsboard.server.dao.relation.RelationDao; 28 import org.thingsboard.server.dao.relation.RelationDao;
29 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 29 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
30 -import org.thingsboard.server.dao.util.SqlDao;  
31 30
32 import java.util.Objects; 31 import java.util.Objects;
33 import java.util.UUID; 32 import java.util.UUID;
@@ -37,7 +36,6 @@ import java.util.UUID; @@ -37,7 +36,6 @@ import java.util.UUID;
37 */ 36 */
38 @Slf4j 37 @Slf4j
39 @Component 38 @Component
40 -@SqlDao  
41 public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao { 39 public class JpaDashboardInfoDao extends JpaAbstractSearchTextDao<DashboardInfoEntity, DashboardInfo> implements DashboardInfoDao {
42 40
43 @Autowired 41 @Autowired
@@ -17,14 +17,12 @@ package org.thingsboard.server.dao.sql.device; @@ -17,14 +17,12 @@ package org.thingsboard.server.dao.sql.device;
17 17
18 import org.springframework.data.repository.CrudRepository; 18 import org.springframework.data.repository.CrudRepository;
19 import org.thingsboard.server.dao.model.sql.DeviceCredentialsEntity; 19 import org.thingsboard.server.dao.model.sql.DeviceCredentialsEntity;
20 -import org.thingsboard.server.dao.util.SqlDao;  
21 20
22 import java.util.UUID; 21 import java.util.UUID;
23 22
24 /** 23 /**
25 * Created by Valerii Sosliuk on 5/6/2017. 24 * Created by Valerii Sosliuk on 5/6/2017.
26 */ 25 */
27 -@SqlDao  
28 public interface DeviceCredentialsRepository extends CrudRepository<DeviceCredentialsEntity, UUID> { 26 public interface DeviceCredentialsRepository extends CrudRepository<DeviceCredentialsEntity, UUID> {
29 27
30 DeviceCredentialsEntity findByDeviceId(UUID deviceId); 28 DeviceCredentialsEntity findByDeviceId(UUID deviceId);
@@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository; @@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.dao.model.sql.DeviceEntity; 23 import org.thingsboard.server.dao.model.sql.DeviceEntity;
24 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity; 24 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity;
25 -import org.thingsboard.server.dao.util.SqlDao;  
26 25
27 import java.util.List; 26 import java.util.List;
28 import java.util.UUID; 27 import java.util.UUID;
@@ -30,7 +29,6 @@ import java.util.UUID; @@ -30,7 +29,6 @@ import java.util.UUID;
30 /** 29 /**
31 * Created by Valerii Sosliuk on 5/6/2017. 30 * Created by Valerii Sosliuk on 5/6/2017.
32 */ 31 */
33 -@SqlDao  
34 public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntity, UUID> { 32 public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntity, UUID> {
35 33
36 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo) " + 34 @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo) " +
@@ -127,4 +125,7 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit @@ -127,4 +125,7 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
127 List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndIdIn(UUID tenantId, UUID customerId, List<UUID> deviceIds); 125 List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndIdIn(UUID tenantId, UUID customerId, List<UUID> deviceIds);
128 126
129 List<DeviceEntity> findDevicesByTenantIdAndIdIn(UUID tenantId, List<UUID> deviceIds); 127 List<DeviceEntity> findDevicesByTenantIdAndIdIn(UUID tenantId, List<UUID> deviceIds);
  128 +
  129 + DeviceEntity findByTenantIdAndId(UUID tenantId, UUID id);
  130 +
130 } 131 }
@@ -24,7 +24,6 @@ import org.thingsboard.server.dao.DaoUtil; @@ -24,7 +24,6 @@ import org.thingsboard.server.dao.DaoUtil;
24 import org.thingsboard.server.dao.device.DeviceCredentialsDao; 24 import org.thingsboard.server.dao.device.DeviceCredentialsDao;
25 import org.thingsboard.server.dao.model.sql.DeviceCredentialsEntity; 25 import org.thingsboard.server.dao.model.sql.DeviceCredentialsEntity;
26 import org.thingsboard.server.dao.sql.JpaAbstractDao; 26 import org.thingsboard.server.dao.sql.JpaAbstractDao;
27 -import org.thingsboard.server.dao.util.SqlDao;  
28 27
29 import java.util.UUID; 28 import java.util.UUID;
30 29
@@ -32,7 +31,6 @@ import java.util.UUID; @@ -32,7 +31,6 @@ import java.util.UUID;
32 * Created by Valerii Sosliuk on 5/6/2017. 31 * Created by Valerii Sosliuk on 5/6/2017.
33 */ 32 */
34 @Component 33 @Component
35 -@SqlDao  
36 public class JpaDeviceCredentialsDao extends JpaAbstractDao<DeviceCredentialsEntity, DeviceCredentials> implements DeviceCredentialsDao { 34 public class JpaDeviceCredentialsDao extends JpaAbstractDao<DeviceCredentialsEntity, DeviceCredentials> implements DeviceCredentialsDao {
37 35
38 @Autowired 36 @Autowired
@@ -32,7 +32,6 @@ import org.thingsboard.server.dao.device.DeviceDao; @@ -32,7 +32,6 @@ import org.thingsboard.server.dao.device.DeviceDao;
32 import org.thingsboard.server.dao.model.sql.DeviceEntity; 32 import org.thingsboard.server.dao.model.sql.DeviceEntity;
33 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity; 33 import org.thingsboard.server.dao.model.sql.DeviceInfoEntity;
34 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 34 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
35 -import org.thingsboard.server.dao.util.SqlDao;  
36 35
37 import java.util.ArrayList; 36 import java.util.ArrayList;
38 import java.util.Collections; 37 import java.util.Collections;
@@ -45,7 +44,6 @@ import java.util.UUID; @@ -45,7 +44,6 @@ import java.util.UUID;
45 * Created by Valerii Sosliuk on 5/6/2017. 44 * Created by Valerii Sosliuk on 5/6/2017.
46 */ 45 */
47 @Component 46 @Component
48 -@SqlDao  
49 public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> implements DeviceDao { 47 public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> implements DeviceDao {
50 48
51 @Autowired 49 @Autowired
@@ -175,6 +173,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> @@ -175,6 +173,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
175 return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(tenantId))); 173 return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(tenantId)));
176 } 174 }
177 175
  176 + @Override
  177 + public Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id) {
  178 + return DaoUtil.getData(deviceRepository.findByTenantIdAndId(tenantId.getId(), id));
  179 + }
  180 +
  181 + @Override
  182 + public ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id) {
  183 + return service.submit(() -> DaoUtil.getData(deviceRepository.findByTenantIdAndId(tenantId.getId(), id)));
  184 + }
  185 +
178 private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) { 186 private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) {
179 List<EntitySubtype> list = Collections.emptyList(); 187 List<EntitySubtype> list = Collections.emptyList();
180 if (types != null && !types.isEmpty()) { 188 if (types != null && !types.isEmpty()) {
@@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository; @@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.dao.model.sql.EntityViewEntity; 23 import org.thingsboard.server.dao.model.sql.EntityViewEntity;
24 import org.thingsboard.server.dao.model.sql.EntityViewInfoEntity; 24 import org.thingsboard.server.dao.model.sql.EntityViewInfoEntity;
25 -import org.thingsboard.server.dao.util.SqlDao;  
26 25
27 import java.util.List; 26 import java.util.List;
28 import java.util.UUID; 27 import java.util.UUID;
@@ -30,7 +29,6 @@ import java.util.UUID; @@ -30,7 +29,6 @@ import java.util.UUID;
30 /** 29 /**
31 * Created by Victor Basanets on 8/31/2017. 30 * Created by Victor Basanets on 8/31/2017.
32 */ 31 */
33 -@SqlDao  
34 public interface EntityViewRepository extends PagingAndSortingRepository<EntityViewEntity, UUID> { 32 public interface EntityViewRepository extends PagingAndSortingRepository<EntityViewEntity, UUID> {
35 33
36 @Query("SELECT new org.thingsboard.server.dao.model.sql.EntityViewInfoEntity(e, c.title, c.additionalInfo) " + 34 @Query("SELECT new org.thingsboard.server.dao.model.sql.EntityViewInfoEntity(e, c.title, c.additionalInfo) " +
@@ -31,7 +31,6 @@ import org.thingsboard.server.dao.entityview.EntityViewDao; @@ -31,7 +31,6 @@ import org.thingsboard.server.dao.entityview.EntityViewDao;
31 import org.thingsboard.server.dao.model.sql.EntityViewEntity; 31 import org.thingsboard.server.dao.model.sql.EntityViewEntity;
32 import org.thingsboard.server.dao.model.sql.EntityViewInfoEntity; 32 import org.thingsboard.server.dao.model.sql.EntityViewInfoEntity;
33 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 33 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
34 -import org.thingsboard.server.dao.util.SqlDao;  
35 34
36 import java.util.ArrayList; 35 import java.util.ArrayList;
37 import java.util.Collections; 36 import java.util.Collections;
@@ -44,7 +43,6 @@ import java.util.UUID; @@ -44,7 +43,6 @@ import java.util.UUID;
44 * Created by Victor Basanets on 8/31/2017. 43 * Created by Victor Basanets on 8/31/2017.
45 */ 44 */
46 @Component 45 @Component
47 -@SqlDao  
48 public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity, EntityView> 46 public class JpaEntityViewDao extends JpaAbstractSearchTextDao<EntityViewEntity, EntityView>
49 implements EntityViewDao { 47 implements EntityViewDao {
50 48
@@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository; @@ -22,7 +22,6 @@ import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.common.data.EntityType; 23 import org.thingsboard.server.common.data.EntityType;
24 import org.thingsboard.server.dao.model.sql.EventEntity; 24 import org.thingsboard.server.dao.model.sql.EventEntity;
25 -import org.thingsboard.server.dao.util.SqlDao;  
26 25
27 import java.util.List; 26 import java.util.List;
28 import java.util.UUID; 27 import java.util.UUID;
@@ -30,7 +29,6 @@ import java.util.UUID; @@ -30,7 +29,6 @@ import java.util.UUID;
30 /** 29 /**
31 * Created by Valerii Sosliuk on 5/3/2017. 30 * Created by Valerii Sosliuk on 5/3/2017.
32 */ 31 */
33 -@SqlDao  
34 public interface EventRepository extends PagingAndSortingRepository<EventEntity, UUID> { 32 public interface EventRepository extends PagingAndSortingRepository<EventEntity, UUID> {
35 33
36 EventEntity findByTenantIdAndEntityTypeAndEntityIdAndEventTypeAndEventUid(UUID tenantId, 34 EventEntity findByTenantIdAndEntityTypeAndEntityIdAndEventTypeAndEventUid(UUID tenantId,
@@ -18,11 +18,9 @@ package org.thingsboard.server.dao.sql.event; @@ -18,11 +18,9 @@ package org.thingsboard.server.dao.sql.event;
18 import org.springframework.stereotype.Repository; 18 import org.springframework.stereotype.Repository;
19 import org.thingsboard.server.dao.model.sql.EventEntity; 19 import org.thingsboard.server.dao.model.sql.EventEntity;
20 import org.thingsboard.server.dao.util.HsqlDao; 20 import org.thingsboard.server.dao.util.HsqlDao;
21 -import org.thingsboard.server.dao.util.SqlDao;  
22 21
23 import javax.persistence.Query; 22 import javax.persistence.Query;
24 23
25 -@SqlDao  
26 @HsqlDao 24 @HsqlDao
27 @Repository 25 @Repository
28 public class HsqlEventInsertRepository extends AbstractEventInsertRepository { 26 public class HsqlEventInsertRepository extends AbstractEventInsertRepository {
@@ -21,7 +21,6 @@ import lombok.extern.slf4j.Slf4j; @@ -21,7 +21,6 @@ import lombok.extern.slf4j.Slf4j;
21 import org.apache.commons.lang3.StringUtils; 21 import org.apache.commons.lang3.StringUtils;
22 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.data.domain.PageRequest; 23 import org.springframework.data.domain.PageRequest;
24 -import org.springframework.data.jpa.domain.Specification;  
25 import org.springframework.data.repository.CrudRepository; 24 import org.springframework.data.repository.CrudRepository;
26 import org.springframework.stereotype.Component; 25 import org.springframework.stereotype.Component;
27 import org.thingsboard.server.common.data.Event; 26 import org.thingsboard.server.common.data.Event;
@@ -34,10 +33,7 @@ import org.thingsboard.server.dao.DaoUtil; @@ -34,10 +33,7 @@ import org.thingsboard.server.dao.DaoUtil;
34 import org.thingsboard.server.dao.event.EventDao; 33 import org.thingsboard.server.dao.event.EventDao;
35 import org.thingsboard.server.dao.model.sql.EventEntity; 34 import org.thingsboard.server.dao.model.sql.EventEntity;
36 import org.thingsboard.server.dao.sql.JpaAbstractDao; 35 import org.thingsboard.server.dao.sql.JpaAbstractDao;
37 -import org.thingsboard.server.dao.util.SqlDao;  
38 36
39 -import javax.persistence.criteria.Predicate;  
40 -import java.util.ArrayList;  
41 import java.util.List; 37 import java.util.List;
42 import java.util.Objects; 38 import java.util.Objects;
43 import java.util.Optional; 39 import java.util.Optional;
@@ -50,7 +46,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -50,7 +46,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
50 */ 46 */
51 @Slf4j 47 @Slf4j
52 @Component 48 @Component
53 -@SqlDao  
54 public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implements EventDao { 49 public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implements EventDao {
55 50
56 private final UUID systemTenantId = NULL_UUID; 51 private final UUID systemTenantId = NULL_UUID;
@@ -19,10 +19,8 @@ import lombok.extern.slf4j.Slf4j; @@ -19,10 +19,8 @@ import lombok.extern.slf4j.Slf4j;
19 import org.springframework.stereotype.Repository; 19 import org.springframework.stereotype.Repository;
20 import org.thingsboard.server.dao.model.sql.EventEntity; 20 import org.thingsboard.server.dao.model.sql.EventEntity;
21 import org.thingsboard.server.dao.util.PsqlDao; 21 import org.thingsboard.server.dao.util.PsqlDao;
22 -import org.thingsboard.server.dao.util.SqlDao;  
23 22
24 @Slf4j 23 @Slf4j
25 -@SqlDao  
26 @PsqlDao 24 @PsqlDao
27 @Repository 25 @Repository
28 public class PsqlEventInsertRepository extends AbstractEventInsertRepository { 26 public class PsqlEventInsertRepository extends AbstractEventInsertRepository {
@@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder; @@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder;
39 import org.thingsboard.server.common.data.query.EntityKey; 39 import org.thingsboard.server.common.data.query.EntityKey;
40 import org.thingsboard.server.common.data.query.EntityKeyType; 40 import org.thingsboard.server.common.data.query.EntityKeyType;
41 import org.thingsboard.server.dao.model.ModelConstants; 41 import org.thingsboard.server.dao.model.ModelConstants;
42 -import org.thingsboard.server.dao.util.SqlDao;  
43 42
44 import java.util.ArrayList; 43 import java.util.ArrayList;
45 import java.util.Arrays; 44 import java.util.Arrays;
@@ -52,7 +51,6 @@ import java.util.Objects; @@ -52,7 +51,6 @@ import java.util.Objects;
52 import java.util.Set; 51 import java.util.Set;
53 import java.util.stream.Collectors; 52 import java.util.stream.Collectors;
54 53
55 -@SqlDao  
56 @Repository 54 @Repository
57 @Slf4j 55 @Slf4j
58 public class DefaultAlarmQueryRepository implements AlarmQueryRepository { 56 public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
@@ -50,7 +50,6 @@ import org.thingsboard.server.common.data.query.RelationsQueryFilter; @@ -50,7 +50,6 @@ import org.thingsboard.server.common.data.query.RelationsQueryFilter;
50 import org.thingsboard.server.common.data.query.SingleEntityFilter; 50 import org.thingsboard.server.common.data.query.SingleEntityFilter;
51 import org.thingsboard.server.common.data.relation.EntitySearchDirection; 51 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
52 import org.thingsboard.server.common.data.relation.EntityTypeFilter; 52 import org.thingsboard.server.common.data.relation.EntityTypeFilter;
53 -import org.thingsboard.server.dao.util.SqlDao;  
54 53
55 import java.util.Arrays; 54 import java.util.Arrays;
56 import java.util.Collections; 55 import java.util.Collections;
@@ -61,7 +60,6 @@ import java.util.Optional; @@ -61,7 +60,6 @@ import java.util.Optional;
61 import java.util.UUID; 60 import java.util.UUID;
62 import java.util.stream.Collectors; 61 import java.util.stream.Collectors;
63 62
64 -@SqlDao  
65 @Repository 63 @Repository
66 @Slf4j 64 @Slf4j
67 public class DefaultEntityQueryRepository implements EntityQueryRepository { 65 public class DefaultEntityQueryRepository implements EntityQueryRepository {
@@ -24,10 +24,8 @@ import org.thingsboard.server.common.data.query.EntityCountQuery; @@ -24,10 +24,8 @@ import org.thingsboard.server.common.data.query.EntityCountQuery;
24 import org.thingsboard.server.common.data.query.EntityData; 24 import org.thingsboard.server.common.data.query.EntityData;
25 import org.thingsboard.server.common.data.query.EntityDataQuery; 25 import org.thingsboard.server.common.data.query.EntityDataQuery;
26 import org.thingsboard.server.dao.entity.EntityQueryDao; 26 import org.thingsboard.server.dao.entity.EntityQueryDao;
27 -import org.thingsboard.server.dao.util.SqlDao;  
28 27
29 @Component 28 @Component
30 -@SqlDao  
31 public class JpaEntityQueryDao implements EntityQueryDao { 29 public class JpaEntityQueryDao implements EntityQueryDao {
32 30
33 @Autowired 31 @Autowired
@@ -20,12 +20,10 @@ import org.springframework.transaction.annotation.Transactional; @@ -20,12 +20,10 @@ import org.springframework.transaction.annotation.Transactional;
20 import org.thingsboard.server.dao.model.sql.RelationCompositeKey; 20 import org.thingsboard.server.dao.model.sql.RelationCompositeKey;
21 import org.thingsboard.server.dao.model.sql.RelationEntity; 21 import org.thingsboard.server.dao.model.sql.RelationEntity;
22 import org.thingsboard.server.dao.util.HsqlDao; 22 import org.thingsboard.server.dao.util.HsqlDao;
23 -import org.thingsboard.server.dao.util.SqlDao;  
24 23
25 import javax.persistence.Query; 24 import javax.persistence.Query;
26 25
27 @HsqlDao 26 @HsqlDao
28 -@SqlDao  
29 @Repository 27 @Repository
30 @Transactional 28 @Transactional
31 public class HsqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository { 29 public class HsqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository {
@@ -30,7 +30,6 @@ import org.thingsboard.server.dao.model.sql.RelationCompositeKey; @@ -30,7 +30,6 @@ import org.thingsboard.server.dao.model.sql.RelationCompositeKey;
30 import org.thingsboard.server.dao.model.sql.RelationEntity; 30 import org.thingsboard.server.dao.model.sql.RelationEntity;
31 import org.thingsboard.server.dao.relation.RelationDao; 31 import org.thingsboard.server.dao.relation.RelationDao;
32 import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; 32 import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
33 -import org.thingsboard.server.dao.util.SqlDao;  
34 33
35 import javax.persistence.criteria.Predicate; 34 import javax.persistence.criteria.Predicate;
36 import java.util.ArrayList; 35 import java.util.ArrayList;
@@ -41,7 +40,6 @@ import java.util.List; @@ -41,7 +40,6 @@ import java.util.List;
41 */ 40 */
42 @Slf4j 41 @Slf4j
43 @Component 42 @Component
44 -@SqlDao  
45 public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService implements RelationDao { 43 public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService implements RelationDao {
46 44
47 @Autowired 45 @Autowired
@@ -19,10 +19,8 @@ import org.springframework.stereotype.Repository; @@ -19,10 +19,8 @@ import org.springframework.stereotype.Repository;
19 import org.springframework.transaction.annotation.Transactional; 19 import org.springframework.transaction.annotation.Transactional;
20 import org.thingsboard.server.dao.model.sql.RelationEntity; 20 import org.thingsboard.server.dao.model.sql.RelationEntity;
21 import org.thingsboard.server.dao.util.PsqlDao; 21 import org.thingsboard.server.dao.util.PsqlDao;
22 -import org.thingsboard.server.dao.util.SqlDao;  
23 22
24 @PsqlDao 23 @PsqlDao
25 -@SqlDao  
26 @Repository 24 @Repository
27 @Transactional 25 @Transactional
28 public class PsqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository { 26 public class PsqlRelationInsertRepository extends AbstractRelationInsertRepository implements RelationInsertRepository {
@@ -20,12 +20,10 @@ import org.springframework.data.repository.CrudRepository; @@ -20,12 +20,10 @@ import org.springframework.data.repository.CrudRepository;
20 import org.springframework.transaction.annotation.Transactional; 20 import org.springframework.transaction.annotation.Transactional;
21 import org.thingsboard.server.dao.model.sql.RelationCompositeKey; 21 import org.thingsboard.server.dao.model.sql.RelationCompositeKey;
22 import org.thingsboard.server.dao.model.sql.RelationEntity; 22 import org.thingsboard.server.dao.model.sql.RelationEntity;
23 -import org.thingsboard.server.dao.util.SqlDao;  
24 23
25 import java.util.List; 24 import java.util.List;
26 import java.util.UUID; 25 import java.util.UUID;
27 26
28 -@SqlDao  
29 public interface RelationRepository 27 public interface RelationRepository
30 extends CrudRepository<RelationEntity, RelationCompositeKey>, JpaSpecificationExecutor<RelationEntity> { 28 extends CrudRepository<RelationEntity, RelationCompositeKey>, JpaSpecificationExecutor<RelationEntity> {
31 29
@@ -26,14 +26,12 @@ import org.thingsboard.server.dao.DaoUtil; @@ -26,14 +26,12 @@ import org.thingsboard.server.dao.DaoUtil;
26 import org.thingsboard.server.dao.model.sql.RuleChainEntity; 26 import org.thingsboard.server.dao.model.sql.RuleChainEntity;
27 import org.thingsboard.server.dao.rule.RuleChainDao; 27 import org.thingsboard.server.dao.rule.RuleChainDao;
28 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 28 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
29 -import org.thingsboard.server.dao.util.SqlDao;  
30 29
31 import java.util.Objects; 30 import java.util.Objects;
32 import java.util.UUID; 31 import java.util.UUID;
33 32
34 @Slf4j 33 @Slf4j
35 @Component 34 @Component
36 -@SqlDao  
37 public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, RuleChain> implements RuleChainDao { 35 public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, RuleChain> implements RuleChainDao {
38 36
39 @Autowired 37 @Autowired
@@ -23,11 +23,9 @@ import org.thingsboard.server.common.data.rule.RuleNode; @@ -23,11 +23,9 @@ import org.thingsboard.server.common.data.rule.RuleNode;
23 import org.thingsboard.server.dao.model.sql.RuleNodeEntity; 23 import org.thingsboard.server.dao.model.sql.RuleNodeEntity;
24 import org.thingsboard.server.dao.rule.RuleNodeDao; 24 import org.thingsboard.server.dao.rule.RuleNodeDao;
25 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 25 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
26 -import org.thingsboard.server.dao.util.SqlDao;  
27 26
28 @Slf4j 27 @Slf4j
29 @Component 28 @Component
30 -@SqlDao  
31 public class JpaRuleNodeDao extends JpaAbstractSearchTextDao<RuleNodeEntity, RuleNode> implements RuleNodeDao { 29 public class JpaRuleNodeDao extends JpaAbstractSearchTextDao<RuleNodeEntity, RuleNode> implements RuleNodeDao {
32 30
33 @Autowired 31 @Autowired
@@ -21,11 +21,9 @@ import org.springframework.data.jpa.repository.Query; @@ -21,11 +21,9 @@ import org.springframework.data.jpa.repository.Query;
21 import org.springframework.data.repository.PagingAndSortingRepository; 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.dao.model.sql.RuleChainEntity; 23 import org.thingsboard.server.dao.model.sql.RuleChainEntity;
24 -import org.thingsboard.server.dao.util.SqlDao;  
25 24
26 import java.util.UUID; 25 import java.util.UUID;
27 26
28 -@SqlDao  
29 public interface RuleChainRepository extends PagingAndSortingRepository<RuleChainEntity, UUID> { 27 public interface RuleChainRepository extends PagingAndSortingRepository<RuleChainEntity, UUID> {
30 28
31 @Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " + 29 @Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " +
@@ -17,9 +17,7 @@ package org.thingsboard.server.dao.sql.rule; @@ -17,9 +17,7 @@ package org.thingsboard.server.dao.sql.rule;
17 17
18 import org.springframework.data.repository.CrudRepository; 18 import org.springframework.data.repository.CrudRepository;
19 import org.thingsboard.server.dao.model.sql.RuleNodeEntity; 19 import org.thingsboard.server.dao.model.sql.RuleNodeEntity;
20 -import org.thingsboard.server.dao.util.SqlDao;  
21 20
22 -@SqlDao  
23 public interface RuleNodeRepository extends CrudRepository<RuleNodeEntity, String> { 21 public interface RuleNodeRepository extends CrudRepository<RuleNodeEntity, String> {
24 22
25 } 23 }
@@ -25,13 +25,11 @@ import org.thingsboard.server.dao.DaoUtil; @@ -25,13 +25,11 @@ import org.thingsboard.server.dao.DaoUtil;
25 import org.thingsboard.server.dao.model.sql.AdminSettingsEntity; 25 import org.thingsboard.server.dao.model.sql.AdminSettingsEntity;
26 import org.thingsboard.server.dao.settings.AdminSettingsDao; 26 import org.thingsboard.server.dao.settings.AdminSettingsDao;
27 import org.thingsboard.server.dao.sql.JpaAbstractDao; 27 import org.thingsboard.server.dao.sql.JpaAbstractDao;
28 -import org.thingsboard.server.dao.util.SqlDao;  
29 28
30 import java.util.UUID; 29 import java.util.UUID;
31 30
32 @Component 31 @Component
33 @Slf4j 32 @Slf4j
34 -@SqlDao  
35 public class JpaAdminSettingsDao extends JpaAbstractDao<AdminSettingsEntity, AdminSettings> implements AdminSettingsDao { 33 public class JpaAdminSettingsDao extends JpaAbstractDao<AdminSettingsEntity, AdminSettings> implements AdminSettingsDao {
36 34
37 @Autowired 35 @Autowired
@@ -26,7 +26,6 @@ import org.thingsboard.server.dao.DaoUtil; @@ -26,7 +26,6 @@ import org.thingsboard.server.dao.DaoUtil;
26 import org.thingsboard.server.dao.model.sql.TenantEntity; 26 import org.thingsboard.server.dao.model.sql.TenantEntity;
27 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 27 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
28 import org.thingsboard.server.dao.tenant.TenantDao; 28 import org.thingsboard.server.dao.tenant.TenantDao;
29 -import org.thingsboard.server.dao.util.SqlDao;  
30 29
31 import java.util.Objects; 30 import java.util.Objects;
32 import java.util.UUID; 31 import java.util.UUID;
@@ -36,7 +35,6 @@ import java.util.UUID; @@ -36,7 +35,6 @@ import java.util.UUID;
36 * Created by Valerii Sosliuk on 4/30/2017. 35 * Created by Valerii Sosliuk on 4/30/2017.
37 */ 36 */
38 @Component 37 @Component
39 -@SqlDao  
40 public class JpaTenantDao extends JpaAbstractSearchTextDao<TenantEntity, Tenant> implements TenantDao { 38 public class JpaTenantDao extends JpaAbstractSearchTextDao<TenantEntity, Tenant> implements TenantDao {
41 39
42 @Autowired 40 @Autowired
@@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query; @@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query;
21 import org.springframework.data.repository.PagingAndSortingRepository; 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.dao.model.sql.TenantEntity; 23 import org.thingsboard.server.dao.model.sql.TenantEntity;
24 -import org.thingsboard.server.dao.util.SqlDao;  
25 24
26 import java.util.UUID; 25 import java.util.UUID;
27 26
28 /** 27 /**
29 * Created by Valerii Sosliuk on 4/30/2017. 28 * Created by Valerii Sosliuk on 4/30/2017.
30 */ 29 */
31 -@SqlDao  
32 public interface TenantRepository extends PagingAndSortingRepository<TenantEntity, UUID> { 30 public interface TenantRepository extends PagingAndSortingRepository<TenantEntity, UUID> {
33 31
34 @Query("SELECT t FROM TenantEntity t WHERE t.region = :region " + 32 @Query("SELECT t FROM TenantEntity t WHERE t.region = :region " +
@@ -24,7 +24,6 @@ import org.thingsboard.server.dao.DaoUtil; @@ -24,7 +24,6 @@ import org.thingsboard.server.dao.DaoUtil;
24 import org.thingsboard.server.dao.model.sql.UserCredentialsEntity; 24 import org.thingsboard.server.dao.model.sql.UserCredentialsEntity;
25 import org.thingsboard.server.dao.sql.JpaAbstractDao; 25 import org.thingsboard.server.dao.sql.JpaAbstractDao;
26 import org.thingsboard.server.dao.user.UserCredentialsDao; 26 import org.thingsboard.server.dao.user.UserCredentialsDao;
27 -import org.thingsboard.server.dao.util.SqlDao;  
28 27
29 import java.util.UUID; 28 import java.util.UUID;
30 29
@@ -32,7 +31,6 @@ import java.util.UUID; @@ -32,7 +31,6 @@ import java.util.UUID;
32 * Created by Valerii Sosliuk on 4/22/2017. 31 * Created by Valerii Sosliuk on 4/22/2017.
33 */ 32 */
34 @Component 33 @Component
35 -@SqlDao  
36 public class JpaUserCredentialsDao extends JpaAbstractDao<UserCredentialsEntity, UserCredentials> implements UserCredentialsDao { 34 public class JpaUserCredentialsDao extends JpaAbstractDao<UserCredentialsEntity, UserCredentials> implements UserCredentialsDao {
37 35
38 @Autowired 36 @Autowired
@@ -27,7 +27,6 @@ import org.thingsboard.server.dao.DaoUtil; @@ -27,7 +27,6 @@ import org.thingsboard.server.dao.DaoUtil;
27 import org.thingsboard.server.dao.model.sql.UserEntity; 27 import org.thingsboard.server.dao.model.sql.UserEntity;
28 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 28 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
29 import org.thingsboard.server.dao.user.UserDao; 29 import org.thingsboard.server.dao.user.UserDao;
30 -import org.thingsboard.server.dao.util.SqlDao;  
31 30
32 import java.util.Objects; 31 import java.util.Objects;
33 import java.util.UUID; 32 import java.util.UUID;
@@ -38,7 +37,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -38,7 +37,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
38 * @author Valerii Sosliuk 37 * @author Valerii Sosliuk
39 */ 38 */
40 @Component 39 @Component
41 -@SqlDao  
42 public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> implements UserDao { 40 public class JpaUserDao extends JpaAbstractSearchTextDao<UserEntity, User> implements UserDao {
43 41
44 @Autowired 42 @Autowired
@@ -17,14 +17,12 @@ package org.thingsboard.server.dao.sql.user; @@ -17,14 +17,12 @@ package org.thingsboard.server.dao.sql.user;
17 17
18 import org.springframework.data.repository.CrudRepository; 18 import org.springframework.data.repository.CrudRepository;
19 import org.thingsboard.server.dao.model.sql.UserCredentialsEntity; 19 import org.thingsboard.server.dao.model.sql.UserCredentialsEntity;
20 -import org.thingsboard.server.dao.util.SqlDao;  
21 20
22 import java.util.UUID; 21 import java.util.UUID;
23 22
24 /** 23 /**
25 * Created by Valerii Sosliuk on 4/22/2017. 24 * Created by Valerii Sosliuk on 4/22/2017.
26 */ 25 */
27 -@SqlDao  
28 public interface UserCredentialsRepository extends CrudRepository<UserCredentialsEntity, UUID> { 26 public interface UserCredentialsRepository extends CrudRepository<UserCredentialsEntity, UUID> {
29 27
30 UserCredentialsEntity findByUserId(UUID userId); 28 UserCredentialsEntity findByUserId(UUID userId);
@@ -22,14 +22,12 @@ import org.springframework.data.repository.PagingAndSortingRepository; @@ -22,14 +22,12 @@ import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.common.data.security.Authority; 23 import org.thingsboard.server.common.data.security.Authority;
24 import org.thingsboard.server.dao.model.sql.UserEntity; 24 import org.thingsboard.server.dao.model.sql.UserEntity;
25 -import org.thingsboard.server.dao.util.SqlDao;  
26 25
27 import java.util.UUID; 26 import java.util.UUID;
28 27
29 /** 28 /**
30 * @author Valerii Sosliuk 29 * @author Valerii Sosliuk
31 */ 30 */
32 -@SqlDao  
33 public interface UserRepository extends PagingAndSortingRepository<UserEntity, UUID> { 31 public interface UserRepository extends PagingAndSortingRepository<UserEntity, UUID> {
34 32
35 UserEntity findByEmail(String email); 33 UserEntity findByEmail(String email);
@@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.widget.WidgetType; @@ -22,7 +22,6 @@ import org.thingsboard.server.common.data.widget.WidgetType;
22 import org.thingsboard.server.dao.DaoUtil; 22 import org.thingsboard.server.dao.DaoUtil;
23 import org.thingsboard.server.dao.model.sql.WidgetTypeEntity; 23 import org.thingsboard.server.dao.model.sql.WidgetTypeEntity;
24 import org.thingsboard.server.dao.sql.JpaAbstractDao; 24 import org.thingsboard.server.dao.sql.JpaAbstractDao;
25 -import org.thingsboard.server.dao.util.SqlDao;  
26 import org.thingsboard.server.dao.widget.WidgetTypeDao; 25 import org.thingsboard.server.dao.widget.WidgetTypeDao;
27 26
28 import java.util.List; 27 import java.util.List;
@@ -32,7 +31,6 @@ import java.util.UUID; @@ -32,7 +31,6 @@ import java.util.UUID;
32 * Created by Valerii Sosliuk on 4/29/2017. 31 * Created by Valerii Sosliuk on 4/29/2017.
33 */ 32 */
34 @Component 33 @Component
35 -@SqlDao  
36 public class JpaWidgetTypeDao extends JpaAbstractDao<WidgetTypeEntity, WidgetType> implements WidgetTypeDao { 34 public class JpaWidgetTypeDao extends JpaAbstractDao<WidgetTypeEntity, WidgetType> implements WidgetTypeDao {
37 35
38 @Autowired 36 @Autowired
@@ -25,7 +25,6 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle; @@ -25,7 +25,6 @@ import org.thingsboard.server.common.data.widget.WidgetsBundle;
25 import org.thingsboard.server.dao.DaoUtil; 25 import org.thingsboard.server.dao.DaoUtil;
26 import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity; 26 import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity;
27 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 27 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
28 -import org.thingsboard.server.dao.util.SqlDao;  
29 import org.thingsboard.server.dao.widget.WidgetsBundleDao; 28 import org.thingsboard.server.dao.widget.WidgetsBundleDao;
30 29
31 import java.util.Objects; 30 import java.util.Objects;
@@ -37,7 +36,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @@ -37,7 +36,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
37 * Created by Valerii Sosliuk on 4/23/2017. 36 * Created by Valerii Sosliuk on 4/23/2017.
38 */ 37 */
39 @Component 38 @Component
40 -@SqlDao  
41 public class JpaWidgetsBundleDao extends JpaAbstractSearchTextDao<WidgetsBundleEntity, WidgetsBundle> implements WidgetsBundleDao { 39 public class JpaWidgetsBundleDao extends JpaAbstractSearchTextDao<WidgetsBundleEntity, WidgetsBundle> implements WidgetsBundleDao {
42 40
43 @Autowired 41 @Autowired
@@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sql.widget; @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sql.widget;
17 17
18 import org.springframework.data.repository.CrudRepository; 18 import org.springframework.data.repository.CrudRepository;
19 import org.thingsboard.server.dao.model.sql.WidgetTypeEntity; 19 import org.thingsboard.server.dao.model.sql.WidgetTypeEntity;
20 -import org.thingsboard.server.dao.util.SqlDao;  
21 20
22 import java.util.List; 21 import java.util.List;
23 import java.util.UUID; 22 import java.util.UUID;
@@ -25,7 +24,6 @@ import java.util.UUID; @@ -25,7 +24,6 @@ import java.util.UUID;
25 /** 24 /**
26 * Created by Valerii Sosliuk on 4/29/2017. 25 * Created by Valerii Sosliuk on 4/29/2017.
27 */ 26 */
28 -@SqlDao  
29 public interface WidgetTypeRepository extends CrudRepository<WidgetTypeEntity, UUID> { 27 public interface WidgetTypeRepository extends CrudRepository<WidgetTypeEntity, UUID> {
30 28
31 List<WidgetTypeEntity> findByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias); 29 List<WidgetTypeEntity> findByTenantIdAndBundleAlias(UUID tenantId, String bundleAlias);
@@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query; @@ -21,14 +21,12 @@ import org.springframework.data.jpa.repository.Query;
21 import org.springframework.data.repository.PagingAndSortingRepository; 21 import org.springframework.data.repository.PagingAndSortingRepository;
22 import org.springframework.data.repository.query.Param; 22 import org.springframework.data.repository.query.Param;
23 import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity; 23 import org.thingsboard.server.dao.model.sql.WidgetsBundleEntity;
24 -import org.thingsboard.server.dao.util.SqlDao;  
25 24
26 import java.util.UUID; 25 import java.util.UUID;
27 26
28 /** 27 /**
29 * Created by Valerii Sosliuk on 4/23/2017. 28 * Created by Valerii Sosliuk on 4/23/2017.
30 */ 29 */
31 -@SqlDao  
32 public interface WidgetsBundleRepository extends PagingAndSortingRepository<WidgetsBundleEntity, UUID> { 30 public interface WidgetsBundleRepository extends PagingAndSortingRepository<WidgetsBundleEntity, UUID> {
33 31
34 WidgetsBundleEntity findWidgetsBundleByTenantIdAndAlias(UUID tenantId, String alias); 32 WidgetsBundleEntity findWidgetsBundleByTenantIdAndAlias(UUID tenantId, String alias);
@@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sqlts; @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sqlts;
17 17
18 import com.google.common.util.concurrent.Futures; 18 import com.google.common.util.concurrent.Futures;
19 import com.google.common.util.concurrent.ListenableFuture; 19 import com.google.common.util.concurrent.ListenableFuture;
20 -import com.google.common.util.concurrent.ListeningExecutorService;  
21 import com.google.common.util.concurrent.MoreExecutors; 20 import com.google.common.util.concurrent.MoreExecutors;
22 import com.google.common.util.concurrent.SettableFuture; 21 import com.google.common.util.concurrent.SettableFuture;
23 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
@@ -33,7 +32,6 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -33,7 +32,6 @@ import org.thingsboard.server.common.data.kv.TsKvEntry;
33 import org.thingsboard.server.common.stats.StatsFactory; 32 import org.thingsboard.server.common.stats.StatsFactory;
34 import org.thingsboard.server.dao.DaoUtil; 33 import org.thingsboard.server.dao.DaoUtil;
35 import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; 34 import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity;
36 -import org.thingsboard.server.dao.sql.TbSqlBlockingQueue;  
37 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams; 35 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
38 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper; 36 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper;
39 import org.thingsboard.server.dao.sqlts.insert.InsertTsRepository; 37 import org.thingsboard.server.dao.sqlts.insert.InsertTsRepository;
@@ -64,7 +62,6 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq @@ -64,7 +62,6 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq
64 62
65 @PostConstruct 63 @PostConstruct
66 protected void init() { 64 protected void init() {
67 - super.init();  
68 TbSqlBlockingQueueParams tsParams = TbSqlBlockingQueueParams.builder() 65 TbSqlBlockingQueueParams tsParams = TbSqlBlockingQueueParams.builder()
69 .logName("TS") 66 .logName("TS")
70 .batchSize(tsBatchSize) 67 .batchSize(tsBatchSize)
@@ -80,7 +77,6 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq @@ -80,7 +77,6 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq
80 77
81 @PreDestroy 78 @PreDestroy
82 protected void destroy() { 79 protected void destroy() {
83 - super.destroy();  
84 if (tsQueue != null) { 80 if (tsQueue != null) {
85 tsQueue.destroy(); 81 tsQueue.destroy();
86 } 82 }
@@ -99,26 +95,6 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq @@ -99,26 +95,6 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq
99 } 95 }
100 96
101 @Override 97 @Override
102 - public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {  
103 - return getSaveLatestFuture(entityId, tsKvEntry);  
104 - }  
105 -  
106 - @Override  
107 - public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
108 - return getRemoveLatestFuture(entityId, query);  
109 - }  
110 -  
111 - @Override  
112 - public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {  
113 - return getFindLatestFuture(entityId, key);  
114 - }  
115 -  
116 - @Override  
117 - public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {  
118 - return getFindAllLatestFuture(entityId);  
119 - }  
120 -  
121 - @Override  
122 public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) { 98 public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
123 return Futures.immediateFuture(null); 99 return Futures.immediateFuture(null);
124 } 100 }
@@ -134,7 +110,7 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq @@ -134,7 +110,7 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq
134 } 110 }
135 111
136 @Override 112 @Override
137 - protected ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) { 113 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
138 if (query.getAggregation() == Aggregation.NONE) { 114 if (query.getAggregation() == Aggregation.NONE) {
139 return findAllAsyncWithLimit(entityId, query); 115 return findAllAsyncWithLimit(entityId, query);
140 } else { 116 } else {
@@ -151,8 +127,7 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq @@ -151,8 +127,7 @@ public abstract class AbstractChunkedAggregationTimeseriesDao extends AbstractSq
151 } 127 }
152 } 128 }
153 129
154 - @Override  
155 - protected ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) { 130 + private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
156 Integer keyId = getOrSaveKeyId(query.getKey()); 131 Integer keyId = getOrSaveKeyId(query.getKey());
157 List<TsKvEntity> tsKvEntities = tsKvRepository.findAllWithLimit( 132 List<TsKvEntity> tsKvEntities = tsKvRepository.findAllWithLimit(
158 entityId.getId(), 133 entityId.getId(),
@@ -16,96 +16,28 @@ @@ -16,96 +16,28 @@
16 package org.thingsboard.server.dao.sqlts; 16 package org.thingsboard.server.dao.sqlts;
17 17
18 import com.google.common.base.Function; 18 import com.google.common.base.Function;
19 -import com.google.common.collect.Lists;  
20 -import com.google.common.util.concurrent.FutureCallback;  
21 import com.google.common.util.concurrent.Futures; 19 import com.google.common.util.concurrent.Futures;
22 import com.google.common.util.concurrent.ListenableFuture; 20 import com.google.common.util.concurrent.ListenableFuture;
23 -import com.google.common.util.concurrent.MoreExecutors;  
24 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
25 -import org.hibernate.exception.ConstraintViolationException;  
26 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
27 import org.springframework.beans.factory.annotation.Value; 23 import org.springframework.beans.factory.annotation.Value;
28 import org.thingsboard.server.common.data.id.EntityId; 24 import org.thingsboard.server.common.data.id.EntityId;
29 import org.thingsboard.server.common.data.id.TenantId; 25 import org.thingsboard.server.common.data.id.TenantId;
30 -import org.thingsboard.server.common.data.kv.Aggregation;  
31 -import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;  
32 -import org.thingsboard.server.common.data.kv.BasicTsKvEntry;  
33 -import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;  
34 import org.thingsboard.server.common.data.kv.ReadTsKvQuery; 26 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
35 -import org.thingsboard.server.common.data.kv.StringDataEntry;  
36 import org.thingsboard.server.common.data.kv.TsKvEntry; 27 import org.thingsboard.server.common.data.kv.TsKvEntry;
37 -import org.thingsboard.server.common.stats.StatsFactory;  
38 -import org.thingsboard.server.dao.DaoUtil;  
39 -import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;  
40 -import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;  
41 -import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey;  
42 -import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;  
43 -import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;  
44 import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; 28 import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;
45 -import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;  
46 -import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper;  
47 -import org.thingsboard.server.dao.sqlts.dictionary.TsKvDictionaryRepository;  
48 -import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository;  
49 -import org.thingsboard.server.dao.sqlts.latest.SearchTsKvLatestRepository;  
50 -import org.thingsboard.server.dao.sqlts.latest.TsKvLatestRepository;  
51 -import org.thingsboard.server.dao.timeseries.SimpleListenableFuture;  
52 29
53 import javax.annotation.Nullable; 30 import javax.annotation.Nullable;
54 -import javax.annotation.PostConstruct;  
55 -import javax.annotation.PreDestroy;  
56 -import java.util.ArrayList;  
57 -import java.util.HashMap;  
58 import java.util.List; 31 import java.util.List;
59 -import java.util.Map;  
60 import java.util.Objects; 32 import java.util.Objects;
61 -import java.util.Optional;  
62 -import java.util.concurrent.ConcurrentHashMap;  
63 -import java.util.concurrent.ConcurrentMap;  
64 -import java.util.concurrent.ExecutionException;  
65 -import java.util.concurrent.locks.ReentrantLock;  
66 import java.util.stream.Collectors; 33 import java.util.stream.Collectors;
67 34
68 @Slf4j 35 @Slf4j
69 -public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningExecutorService {  
70 -  
71 - private static final String DESC_ORDER = "DESC";  
72 -  
73 - private final ConcurrentMap<String, Integer> tsKvDictionaryMap = new ConcurrentHashMap<>();  
74 -  
75 - private static final ReentrantLock tsCreationLock = new ReentrantLock();  
76 -  
77 - @Autowired  
78 - private TsKvLatestRepository tsKvLatestRepository;  
79 -  
80 - @Autowired  
81 - private SearchTsKvLatestRepository searchTsKvLatestRepository;  
82 -  
83 - @Autowired  
84 - private InsertLatestTsRepository insertLatestTsRepository;  
85 -  
86 - @Autowired  
87 - private TsKvDictionaryRepository dictionaryRepository;  
88 -  
89 - private TbSqlBlockingQueueWrapper<TsKvLatestEntity> tsLatestQueue;  
90 -  
91 - @Value("${sql.ts_latest.batch_size:1000}")  
92 - private int tsLatestBatchSize;  
93 -  
94 - @Value("${sql.ts_latest.batch_max_delay:100}")  
95 - private long tsLatestMaxDelay;  
96 -  
97 - @Value("${sql.ts_latest.stats_print_interval_ms:1000}")  
98 - private long tsLatestStatsPrintIntervalMs;  
99 -  
100 - @Value("${sql.ts_latest.batch_threads:4}")  
101 - private int tsLatestBatchThreads; 36 +public abstract class AbstractSqlTimeseriesDao extends BaseAbstractSqlTimeseriesDao implements AggregationTimeseriesDao {
102 37
103 @Autowired 38 @Autowired
104 protected ScheduledLogExecutorComponent logExecutor; 39 protected ScheduledLogExecutorComponent logExecutor;
105 40
106 - @Autowired  
107 - private StatsFactory statsFactory;  
108 -  
109 @Value("${sql.ts.batch_size:1000}") 41 @Value("${sql.ts.batch_size:1000}")
110 protected int tsBatchSize; 42 protected int tsBatchSize;
111 43
@@ -121,44 +53,10 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx @@ -121,44 +53,10 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx
121 @Value("${sql.timescale.batch_threads:4}") 53 @Value("${sql.timescale.batch_threads:4}")
122 protected int timescaleBatchThreads; 54 protected int timescaleBatchThreads;
123 55
124 - @PostConstruct  
125 - protected void init() {  
126 - TbSqlBlockingQueueParams tsLatestParams = TbSqlBlockingQueueParams.builder()  
127 - .logName("TS Latest")  
128 - .batchSize(tsLatestBatchSize)  
129 - .maxDelay(tsLatestMaxDelay)  
130 - .statsPrintIntervalMs(tsLatestStatsPrintIntervalMs)  
131 - .statsNamePrefix("ts.latest")  
132 - .build();  
133 -  
134 - java.util.function.Function<TsKvLatestEntity, Integer> hashcodeFunction = entity -> entity.getEntityId().hashCode();  
135 - tsLatestQueue = new TbSqlBlockingQueueWrapper<>(tsLatestParams, hashcodeFunction, tsLatestBatchThreads, statsFactory);  
136 -  
137 - tsLatestQueue.init(logExecutor, v -> {  
138 - Map<TsKey, TsKvLatestEntity> trueLatest = new HashMap<>();  
139 - v.forEach(ts -> {  
140 - TsKey key = new TsKey(ts.getEntityId(), ts.getKey());  
141 - TsKvLatestEntity old = trueLatest.get(key);  
142 - if (old == null || old.getTs() < ts.getTs()) {  
143 - trueLatest.put(key, ts);  
144 - }  
145 - });  
146 - List<TsKvLatestEntity> latestEntities = new ArrayList<>(trueLatest.values());  
147 - insertLatestTsRepository.saveOrUpdate(latestEntities);  
148 - });  
149 - }  
150 -  
151 - @PreDestroy  
152 - protected void destroy() {  
153 - if (tsLatestQueue != null) {  
154 - tsLatestQueue.destroy();  
155 - }  
156 - }  
157 -  
158 protected ListenableFuture<List<TsKvEntry>> processFindAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) { 56 protected ListenableFuture<List<TsKvEntry>> processFindAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
159 List<ListenableFuture<List<TsKvEntry>>> futures = queries 57 List<ListenableFuture<List<TsKvEntry>>> futures = queries
160 .stream() 58 .stream()
161 - .map(query -> findAllAsync(entityId, query)) 59 + .map(query -> findAllAsync(tenantId, entityId, query))
162 .collect(Collectors.toList()); 60 .collect(Collectors.toList());
163 return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() { 61 return Futures.transform(Futures.allAsList(futures), new Function<List<List<TsKvEntry>>, List<TsKvEntry>>() {
164 @Nullable 62 @Nullable
@@ -174,168 +72,4 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx @@ -174,168 +72,4 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx
174 } 72 }
175 }, service); 73 }, service);
176 } 74 }
177 -  
178 - protected abstract ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query);  
179 -  
180 - protected abstract ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query);  
181 -  
182 - protected ListenableFuture<List<TsKvEntry>> getTskvEntriesFuture(ListenableFuture<List<Optional<TsKvEntry>>> future) {  
183 - return Futures.transform(future, new Function<List<Optional<TsKvEntry>>, List<TsKvEntry>>() {  
184 - @Nullable  
185 - @Override  
186 - public List<TsKvEntry> apply(@Nullable List<Optional<TsKvEntry>> results) {  
187 - if (results == null || results.isEmpty()) {  
188 - return null;  
189 - }  
190 - return results.stream()  
191 - .filter(Optional::isPresent)  
192 - .map(Optional::get)  
193 - .collect(Collectors.toList());  
194 - }  
195 - }, service);  
196 - }  
197 -  
198 - protected ListenableFuture<List<TsKvEntry>> findNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) {  
199 - long startTs = 0;  
200 - long endTs = query.getStartTs() - 1;  
201 - ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,  
202 - Aggregation.NONE, DESC_ORDER);  
203 - return findAllAsync(entityId, findNewLatestQuery);  
204 - }  
205 -  
206 - protected ListenableFuture<TsKvEntry> getFindLatestFuture(EntityId entityId, String key) {  
207 - TsKvLatestCompositeKey compositeKey =  
208 - new TsKvLatestCompositeKey(  
209 - entityId.getId(),  
210 - getOrSaveKeyId(key));  
211 - Optional<TsKvLatestEntity> entry = tsKvLatestRepository.findById(compositeKey);  
212 - TsKvEntry result;  
213 - if (entry.isPresent()) {  
214 - TsKvLatestEntity tsKvLatestEntity = entry.get();  
215 - tsKvLatestEntity.setStrKey(key);  
216 - result = DaoUtil.getData(tsKvLatestEntity);  
217 - } else {  
218 - result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));  
219 - }  
220 - return Futures.immediateFuture(result);  
221 - }  
222 -  
223 - protected ListenableFuture<Void> getRemoveLatestFuture(EntityId entityId, DeleteTsKvQuery query) {  
224 - ListenableFuture<TsKvEntry> latestFuture = getFindLatestFuture(entityId, query.getKey());  
225 -  
226 - ListenableFuture<Boolean> booleanFuture = Futures.transform(latestFuture, tsKvEntry -> {  
227 - long ts = tsKvEntry.getTs();  
228 - return ts > query.getStartTs() && ts <= query.getEndTs();  
229 - }, service);  
230 -  
231 - ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {  
232 - if (isRemove) {  
233 - TsKvLatestEntity latestEntity = new TsKvLatestEntity();  
234 - latestEntity.setEntityId(entityId.getId());  
235 - latestEntity.setKey(getOrSaveKeyId(query.getKey()));  
236 - return service.submit(() -> {  
237 - tsKvLatestRepository.delete(latestEntity);  
238 - return null;  
239 - });  
240 - }  
241 - return Futures.immediateFuture(null);  
242 - }, service);  
243 -  
244 - final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();  
245 - Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {  
246 - @Override  
247 - public void onSuccess(@Nullable Void result) {  
248 - if (query.getRewriteLatestIfDeleted()) {  
249 - ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {  
250 - if (isRemove) {  
251 - return getNewLatestEntryFuture(entityId, query);  
252 - }  
253 - return Futures.immediateFuture(null);  
254 - }, service);  
255 -  
256 - try {  
257 - resultFuture.set(savedLatestFuture.get());  
258 - } catch (InterruptedException | ExecutionException e) {  
259 - log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);  
260 - }  
261 - } else {  
262 - resultFuture.set(null);  
263 - }  
264 - }  
265 -  
266 - @Override  
267 - public void onFailure(Throwable t) {  
268 - log.warn("[{}] Failed to process remove of the latest value", entityId, t);  
269 - }  
270 - }, MoreExecutors.directExecutor());  
271 - return resultFuture;  
272 - }  
273 -  
274 - protected ListenableFuture<List<TsKvEntry>> getFindAllLatestFuture(EntityId entityId) {  
275 - return Futures.immediateFuture(  
276 - DaoUtil.convertDataList(Lists.newArrayList(  
277 - searchTsKvLatestRepository.findAllByEntityId(entityId.getId()))));  
278 - }  
279 -  
280 - protected ListenableFuture<Void> getSaveLatestFuture(EntityId entityId, TsKvEntry tsKvEntry) {  
281 - TsKvLatestEntity latestEntity = new TsKvLatestEntity();  
282 - latestEntity.setEntityId(entityId.getId());  
283 - latestEntity.setTs(tsKvEntry.getTs());  
284 - latestEntity.setKey(getOrSaveKeyId(tsKvEntry.getKey()));  
285 - latestEntity.setStrValue(tsKvEntry.getStrValue().orElse(null));  
286 - latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));  
287 - latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null));  
288 - latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));  
289 - latestEntity.setJsonValue(tsKvEntry.getJsonValue().orElse(null));  
290 -  
291 - return tsLatestQueue.add(latestEntity);  
292 - }  
293 -  
294 - protected Integer getOrSaveKeyId(String strKey) {  
295 - Integer keyId = tsKvDictionaryMap.get(strKey);  
296 - if (keyId == null) {  
297 - Optional<TsKvDictionary> tsKvDictionaryOptional;  
298 - tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));  
299 - if (!tsKvDictionaryOptional.isPresent()) {  
300 - tsCreationLock.lock();  
301 - try {  
302 - tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));  
303 - if (!tsKvDictionaryOptional.isPresent()) {  
304 - TsKvDictionary tsKvDictionary = new TsKvDictionary();  
305 - tsKvDictionary.setKey(strKey);  
306 - try {  
307 - TsKvDictionary saved = dictionaryRepository.save(tsKvDictionary);  
308 - tsKvDictionaryMap.put(saved.getKey(), saved.getKeyId());  
309 - keyId = saved.getKeyId();  
310 - } catch (ConstraintViolationException e) {  
311 - tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));  
312 - TsKvDictionary dictionary = tsKvDictionaryOptional.orElseThrow(() -> new RuntimeException("Failed to get TsKvDictionary entity from DB!"));  
313 - tsKvDictionaryMap.put(dictionary.getKey(), dictionary.getKeyId());  
314 - keyId = dictionary.getKeyId();  
315 - }  
316 - } else {  
317 - keyId = tsKvDictionaryOptional.get().getKeyId();  
318 - }  
319 - } finally {  
320 - tsCreationLock.unlock();  
321 - }  
322 - } else {  
323 - keyId = tsKvDictionaryOptional.get().getKeyId();  
324 - tsKvDictionaryMap.put(strKey, keyId);  
325 - }  
326 - }  
327 - return keyId;  
328 - }  
329 -  
330 - private ListenableFuture<Void> getNewLatestEntryFuture(EntityId entityId, DeleteTsKvQuery query) {  
331 - ListenableFuture<List<TsKvEntry>> future = findNewLatestEntryFuture(entityId, query);  
332 - return Futures.transformAsync(future, entryList -> {  
333 - if (entryList.size() == 1) {  
334 - return getSaveLatestFuture(entityId, entryList.get(0));  
335 - } else {  
336 - log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());  
337 - }  
338 - return Futures.immediateFuture(null);  
339 - }, service);  
340 - }  
341 } 75 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
  22 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  23 +
  24 +import java.util.List;
  25 +
  26 +public interface AggregationTimeseriesDao {
  27 +
  28 + ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query);
  29 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts;
  17 +
  18 +import com.google.common.base.Function;
  19 +import com.google.common.util.concurrent.Futures;
  20 +import com.google.common.util.concurrent.ListenableFuture;
  21 +import lombok.extern.slf4j.Slf4j;
  22 +import org.hibernate.exception.ConstraintViolationException;
  23 +import org.springframework.beans.factory.annotation.Autowired;
  24 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  25 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;
  26 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;
  27 +import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
  28 +import org.thingsboard.server.dao.sqlts.dictionary.TsKvDictionaryRepository;
  29 +
  30 +import javax.annotation.Nullable;
  31 +import java.util.List;
  32 +import java.util.Optional;
  33 +import java.util.concurrent.ConcurrentHashMap;
  34 +import java.util.concurrent.ConcurrentMap;
  35 +import java.util.concurrent.locks.ReentrantLock;
  36 +import java.util.stream.Collectors;
  37 +
  38 +@Slf4j
  39 +public abstract class BaseAbstractSqlTimeseriesDao extends JpaAbstractDaoListeningExecutorService {
  40 +
  41 + private final ConcurrentMap<String, Integer> tsKvDictionaryMap = new ConcurrentHashMap<>();
  42 +
  43 + protected static final ReentrantLock tsCreationLock = new ReentrantLock();
  44 +
  45 + @Autowired
  46 + protected TsKvDictionaryRepository dictionaryRepository;
  47 +
  48 + protected Integer getOrSaveKeyId(String strKey) {
  49 + Integer keyId = tsKvDictionaryMap.get(strKey);
  50 + if (keyId == null) {
  51 + Optional<TsKvDictionary> tsKvDictionaryOptional;
  52 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  53 + if (!tsKvDictionaryOptional.isPresent()) {
  54 + tsCreationLock.lock();
  55 + try {
  56 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  57 + if (!tsKvDictionaryOptional.isPresent()) {
  58 + TsKvDictionary tsKvDictionary = new TsKvDictionary();
  59 + tsKvDictionary.setKey(strKey);
  60 + try {
  61 + TsKvDictionary saved = dictionaryRepository.save(tsKvDictionary);
  62 + tsKvDictionaryMap.put(saved.getKey(), saved.getKeyId());
  63 + keyId = saved.getKeyId();
  64 + } catch (ConstraintViolationException e) {
  65 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  66 + TsKvDictionary dictionary = tsKvDictionaryOptional.orElseThrow(() -> new RuntimeException("Failed to get TsKvDictionary entity from DB!"));
  67 + tsKvDictionaryMap.put(dictionary.getKey(), dictionary.getKeyId());
  68 + keyId = dictionary.getKeyId();
  69 + }
  70 + } else {
  71 + keyId = tsKvDictionaryOptional.get().getKeyId();
  72 + }
  73 + } finally {
  74 + tsCreationLock.unlock();
  75 + }
  76 + } else {
  77 + keyId = tsKvDictionaryOptional.get().getKeyId();
  78 + tsKvDictionaryMap.put(strKey, keyId);
  79 + }
  80 + }
  81 + return keyId;
  82 + }
  83 +
  84 + protected ListenableFuture<List<TsKvEntry>> getTskvEntriesFuture(ListenableFuture<List<Optional<TsKvEntry>>> future) {
  85 + return Futures.transform(future, new Function<List<Optional<TsKvEntry>>, List<TsKvEntry>>() {
  86 + @Nullable
  87 + @Override
  88 + public List<TsKvEntry> apply(@Nullable List<Optional<TsKvEntry>> results) {
  89 + if (results == null || results.isEmpty()) {
  90 + return null;
  91 + }
  92 + return results.stream()
  93 + .filter(Optional::isPresent)
  94 + .map(Optional::get)
  95 + .collect(Collectors.toList());
  96 + }
  97 + }, service);
  98 + }
  99 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts;
  17 +
  18 +import com.google.common.collect.Lists;
  19 +import com.google.common.util.concurrent.FutureCallback;
  20 +import com.google.common.util.concurrent.Futures;
  21 +import com.google.common.util.concurrent.ListenableFuture;
  22 +import com.google.common.util.concurrent.MoreExecutors;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.beans.factory.annotation.Autowired;
  25 +import org.springframework.beans.factory.annotation.Value;
  26 +import org.springframework.stereotype.Component;
  27 +import org.thingsboard.server.common.data.id.EntityId;
  28 +import org.thingsboard.server.common.data.id.TenantId;
  29 +import org.thingsboard.server.common.data.kv.Aggregation;
  30 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
  31 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  32 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  33 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
  34 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  35 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  36 +import org.thingsboard.server.common.stats.StatsFactory;
  37 +import org.thingsboard.server.dao.DaoUtil;
  38 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey;
  39 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
  40 +import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;
  41 +import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
  42 +import org.thingsboard.server.dao.sql.TbSqlBlockingQueueWrapper;
  43 +import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository;
  44 +import org.thingsboard.server.dao.sqlts.latest.SearchTsKvLatestRepository;
  45 +import org.thingsboard.server.dao.sqlts.latest.TsKvLatestRepository;
  46 +import org.thingsboard.server.dao.timeseries.SimpleListenableFuture;
  47 +import org.thingsboard.server.dao.timeseries.TimeseriesLatestDao;
  48 +import org.thingsboard.server.dao.util.SqlTsLatestAnyDao;
  49 +
  50 +import javax.annotation.Nullable;
  51 +import javax.annotation.PostConstruct;
  52 +import javax.annotation.PreDestroy;
  53 +import java.util.ArrayList;
  54 +import java.util.HashMap;
  55 +import java.util.List;
  56 +import java.util.Map;
  57 +import java.util.Optional;
  58 +import java.util.concurrent.ExecutionException;
  59 +
  60 +@Slf4j
  61 +@Component
  62 +@SqlTsLatestAnyDao
  63 +public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao implements TimeseriesLatestDao {
  64 +
  65 + private static final String DESC_ORDER = "DESC";
  66 +
  67 + @Autowired
  68 + private TsKvLatestRepository tsKvLatestRepository;
  69 +
  70 + @Autowired
  71 + protected AggregationTimeseriesDao aggregationTimeseriesDao;
  72 +
  73 + @Autowired
  74 + private SearchTsKvLatestRepository searchTsKvLatestRepository;
  75 +
  76 + @Autowired
  77 + private InsertLatestTsRepository insertLatestTsRepository;
  78 +
  79 + private TbSqlBlockingQueueWrapper<TsKvLatestEntity> tsLatestQueue;
  80 +
  81 + @Value("${sql.ts_latest.batch_size:1000}")
  82 + private int tsLatestBatchSize;
  83 +
  84 + @Value("${sql.ts_latest.batch_max_delay:100}")
  85 + private long tsLatestMaxDelay;
  86 +
  87 + @Value("${sql.ts_latest.stats_print_interval_ms:1000}")
  88 + private long tsLatestStatsPrintIntervalMs;
  89 +
  90 + @Value("${sql.ts_latest.batch_threads:4}")
  91 + private int tsLatestBatchThreads;
  92 +
  93 + @Autowired
  94 + protected ScheduledLogExecutorComponent logExecutor;
  95 +
  96 + @Autowired
  97 + private StatsFactory statsFactory;
  98 +
  99 + @PostConstruct
  100 + protected void init() {
  101 + TbSqlBlockingQueueParams tsLatestParams = TbSqlBlockingQueueParams.builder()
  102 + .logName("TS Latest")
  103 + .batchSize(tsLatestBatchSize)
  104 + .maxDelay(tsLatestMaxDelay)
  105 + .statsPrintIntervalMs(tsLatestStatsPrintIntervalMs)
  106 + .statsNamePrefix("ts.latest")
  107 + .build();
  108 +
  109 + java.util.function.Function<TsKvLatestEntity, Integer> hashcodeFunction = entity -> entity.getEntityId().hashCode();
  110 + tsLatestQueue = new TbSqlBlockingQueueWrapper<>(tsLatestParams, hashcodeFunction, tsLatestBatchThreads, statsFactory);
  111 +
  112 + tsLatestQueue.init(logExecutor, v -> {
  113 + Map<TsKey, TsKvLatestEntity> trueLatest = new HashMap<>();
  114 + v.forEach(ts -> {
  115 + TsKey key = new TsKey(ts.getEntityId(), ts.getKey());
  116 + TsKvLatestEntity old = trueLatest.get(key);
  117 + if (old == null || old.getTs() < ts.getTs()) {
  118 + trueLatest.put(key, ts);
  119 + }
  120 + });
  121 + List<TsKvLatestEntity> latestEntities = new ArrayList<>(trueLatest.values());
  122 + insertLatestTsRepository.saveOrUpdate(latestEntities);
  123 + });
  124 + }
  125 +
  126 + @PreDestroy
  127 + protected void destroy() {
  128 + if (tsLatestQueue != null) {
  129 + tsLatestQueue.destroy();
  130 + }
  131 + }
  132 +
  133 + @Override
  134 + public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
  135 + return getSaveLatestFuture(entityId, tsKvEntry);
  136 + }
  137 +
  138 + @Override
  139 + public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  140 + return getRemoveLatestFuture(tenantId, entityId, query);
  141 + }
  142 +
  143 + @Override
  144 + public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
  145 + return getFindLatestFuture(entityId, key);
  146 + }
  147 +
  148 + @Override
  149 + public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
  150 + return getFindAllLatestFuture(entityId);
  151 + }
  152 +
  153 + private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  154 + ListenableFuture<List<TsKvEntry>> future = findNewLatestEntryFuture(tenantId, entityId, query);
  155 + return Futures.transformAsync(future, entryList -> {
  156 + if (entryList.size() == 1) {
  157 + return getSaveLatestFuture(entityId, entryList.get(0));
  158 + } else {
  159 + log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
  160 + }
  161 + return Futures.immediateFuture(null);
  162 + }, service);
  163 + }
  164 +
  165 + private ListenableFuture<List<TsKvEntry>> findNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  166 + long startTs = 0;
  167 + long endTs = query.getStartTs() - 1;
  168 + ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
  169 + Aggregation.NONE, DESC_ORDER);
  170 + return aggregationTimeseriesDao.findAllAsync(tenantId, entityId, findNewLatestQuery);
  171 + }
  172 +
  173 + protected ListenableFuture<TsKvEntry> getFindLatestFuture(EntityId entityId, String key) {
  174 + TsKvLatestCompositeKey compositeKey =
  175 + new TsKvLatestCompositeKey(
  176 + entityId.getId(),
  177 + getOrSaveKeyId(key));
  178 + Optional<TsKvLatestEntity> entry = tsKvLatestRepository.findById(compositeKey);
  179 + TsKvEntry result;
  180 + if (entry.isPresent()) {
  181 + TsKvLatestEntity tsKvLatestEntity = entry.get();
  182 + tsKvLatestEntity.setStrKey(key);
  183 + result = DaoUtil.getData(tsKvLatestEntity);
  184 + } else {
  185 + result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));
  186 + }
  187 + return Futures.immediateFuture(result);
  188 + }
  189 +
  190 + protected ListenableFuture<Void> getRemoveLatestFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  191 + ListenableFuture<TsKvEntry> latestFuture = getFindLatestFuture(entityId, query.getKey());
  192 +
  193 + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestFuture, tsKvEntry -> {
  194 + long ts = tsKvEntry.getTs();
  195 + return ts > query.getStartTs() && ts <= query.getEndTs();
  196 + }, service);
  197 +
  198 + ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  199 + if (isRemove) {
  200 + TsKvLatestEntity latestEntity = new TsKvLatestEntity();
  201 + latestEntity.setEntityId(entityId.getId());
  202 + latestEntity.setKey(getOrSaveKeyId(query.getKey()));
  203 + return service.submit(() -> {
  204 + tsKvLatestRepository.delete(latestEntity);
  205 + return null;
  206 + });
  207 + }
  208 + return Futures.immediateFuture(null);
  209 + }, service);
  210 +
  211 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  212 + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {
  213 + @Override
  214 + public void onSuccess(@Nullable Void result) {
  215 + if (query.getRewriteLatestIfDeleted()) {
  216 + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  217 + if (isRemove) {
  218 + return getNewLatestEntryFuture(tenantId, entityId, query);
  219 + }
  220 + return Futures.immediateFuture(null);
  221 + }, service);
  222 +
  223 + try {
  224 + resultFuture.set(savedLatestFuture.get());
  225 + } catch (InterruptedException | ExecutionException e) {
  226 + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);
  227 + }
  228 + } else {
  229 + resultFuture.set(null);
  230 + }
  231 + }
  232 +
  233 + @Override
  234 + public void onFailure(Throwable t) {
  235 + log.warn("[{}] Failed to process remove of the latest value", entityId, t);
  236 + }
  237 + }, MoreExecutors.directExecutor());
  238 + return resultFuture;
  239 + }
  240 +
  241 + protected ListenableFuture<List<TsKvEntry>> getFindAllLatestFuture(EntityId entityId) {
  242 + return Futures.immediateFuture(
  243 + DaoUtil.convertDataList(Lists.newArrayList(
  244 + searchTsKvLatestRepository.findAllByEntityId(entityId.getId()))));
  245 + }
  246 +
  247 + protected ListenableFuture<Void> getSaveLatestFuture(EntityId entityId, TsKvEntry tsKvEntry) {
  248 + TsKvLatestEntity latestEntity = new TsKvLatestEntity();
  249 + latestEntity.setEntityId(entityId.getId());
  250 + latestEntity.setTs(tsKvEntry.getTs());
  251 + latestEntity.setKey(getOrSaveKeyId(tsKvEntry.getKey()));
  252 + latestEntity.setStrValue(tsKvEntry.getStrValue().orElse(null));
  253 + latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
  254 + latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null));
  255 + latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
  256 + latestEntity.setJsonValue(tsKvEntry.getJsonValue().orElse(null));
  257 +
  258 + return tsLatestQueue.add(latestEntity);
  259 + }
  260 +
  261 +}
@@ -18,11 +18,11 @@ package org.thingsboard.server.dao.sqlts.dictionary; @@ -18,11 +18,11 @@ package org.thingsboard.server.dao.sqlts.dictionary;
18 import org.springframework.data.repository.CrudRepository; 18 import org.springframework.data.repository.CrudRepository;
19 import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary; 19 import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;
20 import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey; 20 import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;
21 -import org.thingsboard.server.dao.util.SqlTsAnyDao; 21 +import org.thingsboard.server.dao.util.SqlTsOrTsLatestAnyDao;
22 22
23 import java.util.Optional; 23 import java.util.Optional;
24 24
25 -@SqlTsAnyDao 25 +@SqlTsOrTsLatestAnyDao
26 public interface TsKvDictionaryRepository extends CrudRepository<TsKvDictionary, TsKvDictionaryCompositeKey> { 26 public interface TsKvDictionaryRepository extends CrudRepository<TsKvDictionary, TsKvDictionaryCompositeKey> {
27 27
28 Optional<TsKvDictionary> findByKeyId(int keyId); 28 Optional<TsKvDictionary> findByKeyId(int keyId);
@@ -22,14 +22,14 @@ import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; @@ -22,14 +22,14 @@ import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
22 import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository; 22 import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository;
23 import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository; 23 import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository;
24 import org.thingsboard.server.dao.util.HsqlDao; 24 import org.thingsboard.server.dao.util.HsqlDao;
25 -import org.thingsboard.server.dao.util.SqlTsDao; 25 +import org.thingsboard.server.dao.util.SqlTsLatestDao;
26 26
27 import java.sql.PreparedStatement; 27 import java.sql.PreparedStatement;
28 import java.sql.SQLException; 28 import java.sql.SQLException;
29 import java.sql.Types; 29 import java.sql.Types;
30 import java.util.List; 30 import java.util.List;
31 31
32 -@SqlTsDao 32 +@SqlTsLatestDao
33 @HsqlDao 33 @HsqlDao
34 @Repository 34 @Repository
35 @Transactional 35 @Transactional
@@ -23,7 +23,7 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult; @@ -23,7 +23,7 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult;
23 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; 23 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
24 import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository; 24 import org.thingsboard.server.dao.sqlts.insert.AbstractInsertRepository;
25 import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository; 25 import org.thingsboard.server.dao.sqlts.insert.latest.InsertLatestTsRepository;
26 -import org.thingsboard.server.dao.util.PsqlTsAnyDao; 26 +import org.thingsboard.server.dao.util.PsqlTsLatestAnyDao;
27 27
28 import java.sql.PreparedStatement; 28 import java.sql.PreparedStatement;
29 import java.sql.SQLException; 29 import java.sql.SQLException;
@@ -32,7 +32,7 @@ import java.util.ArrayList; @@ -32,7 +32,7 @@ import java.util.ArrayList;
32 import java.util.List; 32 import java.util.List;
33 33
34 34
35 -@PsqlTsAnyDao 35 +@PsqlTsLatestAnyDao
36 @Repository 36 @Repository
37 @Transactional 37 @Transactional
38 public class PsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository { 38 public class PsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository {
@@ -17,14 +17,14 @@ package org.thingsboard.server.dao.sqlts.latest; @@ -17,14 +17,14 @@ package org.thingsboard.server.dao.sqlts.latest;
17 17
18 import org.springframework.stereotype.Repository; 18 import org.springframework.stereotype.Repository;
19 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; 19 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
20 -import org.thingsboard.server.dao.util.SqlTsAnyDao; 20 +import org.thingsboard.server.dao.util.SqlTsLatestAnyDao;
21 21
22 import javax.persistence.EntityManager; 22 import javax.persistence.EntityManager;
23 import javax.persistence.PersistenceContext; 23 import javax.persistence.PersistenceContext;
24 import java.util.List; 24 import java.util.List;
25 import java.util.UUID; 25 import java.util.UUID;
26 26
27 -@SqlTsAnyDao 27 +@SqlTsLatestAnyDao
28 @Repository 28 @Repository
29 public class SearchTsKvLatestRepository { 29 public class SearchTsKvLatestRepository {
30 30
@@ -18,9 +18,7 @@ package org.thingsboard.server.dao.sqlts.latest; @@ -18,9 +18,7 @@ package org.thingsboard.server.dao.sqlts.latest;
18 import org.springframework.data.repository.CrudRepository; 18 import org.springframework.data.repository.CrudRepository;
19 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey; 19 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey;
20 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; 20 import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
21 -import org.thingsboard.server.dao.util.SqlDao;  
22 21
23 -@SqlDao  
24 public interface TsKvLatestRepository extends CrudRepository<TsKvLatestEntity, TsKvLatestCompositeKey> { 22 public interface TsKvLatestRepository extends CrudRepository<TsKvLatestEntity, TsKvLatestCompositeKey> {
25 23
26 } 24 }
@@ -41,11 +41,10 @@ import java.util.Optional; @@ -41,11 +41,10 @@ import java.util.Optional;
41 import java.util.concurrent.ConcurrentHashMap; 41 import java.util.concurrent.ConcurrentHashMap;
42 import java.util.concurrent.locks.ReentrantLock; 42 import java.util.concurrent.locks.ReentrantLock;
43 43
44 -  
45 @Component 44 @Component
46 @Slf4j 45 @Slf4j
47 -@SqlTsDao  
48 @PsqlDao 46 @PsqlDao
  47 +@SqlTsDao
49 public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDao { 48 public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDao {
50 49
51 private final Map<Long, PsqlPartition> partitions = new ConcurrentHashMap<>(); 50 private final Map<Long, PsqlPartition> partitions = new ConcurrentHashMap<>();
@@ -18,7 +18,7 @@ package org.thingsboard.server.dao.sqlts.timescale; @@ -18,7 +18,7 @@ package org.thingsboard.server.dao.sqlts.timescale;
18 import org.springframework.scheduling.annotation.Async; 18 import org.springframework.scheduling.annotation.Async;
19 import org.springframework.stereotype.Repository; 19 import org.springframework.stereotype.Repository;
20 import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvEntity; 20 import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvEntity;
21 -import org.thingsboard.server.dao.util.TimescaleDBTsDao; 21 +import org.thingsboard.server.dao.util.TimescaleDBTsOrTsLatestDao;
22 22
23 import javax.persistence.EntityManager; 23 import javax.persistence.EntityManager;
24 import javax.persistence.PersistenceContext; 24 import javax.persistence.PersistenceContext;
@@ -27,7 +27,7 @@ import java.util.UUID; @@ -27,7 +27,7 @@ import java.util.UUID;
27 import java.util.concurrent.CompletableFuture; 27 import java.util.concurrent.CompletableFuture;
28 28
29 @Repository 29 @Repository
30 -@TimescaleDBTsDao 30 +@TimescaleDBTsOrTsLatestDao
31 public class AggregationRepository { 31 public class AggregationRepository {
32 32
33 public static final String FIND_AVG = "findAvg"; 33 public static final String FIND_AVG = "findAvg";
@@ -72,7 +72,6 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -72,7 +72,6 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
72 72
73 @PostConstruct 73 @PostConstruct
74 protected void init() { 74 protected void init() {
75 - super.init();  
76 TbSqlBlockingQueueParams tsParams = TbSqlBlockingQueueParams.builder() 75 TbSqlBlockingQueueParams tsParams = TbSqlBlockingQueueParams.builder()
77 .logName("TS Timescale") 76 .logName("TS Timescale")
78 .batchSize(tsBatchSize) 77 .batchSize(tsBatchSize)
@@ -89,14 +88,60 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -89,14 +88,60 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
89 88
90 @PreDestroy 89 @PreDestroy
91 protected void destroy() { 90 protected void destroy() {
92 - super.destroy();  
93 if (tsQueue != null) { 91 if (tsQueue != null) {
94 tsQueue.destroy(); 92 tsQueue.destroy();
95 } 93 }
96 } 94 }
97 95
98 @Override 96 @Override
99 - protected ListenableFuture<List<TsKvEntry>> findAllAsync(EntityId entityId, ReadTsKvQuery query) { 97 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
  98 + return processFindAllAsync(tenantId, entityId, queries);
  99 + }
  100 +
  101 + @Override
  102 + public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
  103 + String strKey = tsKvEntry.getKey();
  104 + Integer keyId = getOrSaveKeyId(strKey);
  105 + TimescaleTsKvEntity entity = new TimescaleTsKvEntity();
  106 + entity.setEntityId(entityId.getId());
  107 + entity.setTs(tsKvEntry.getTs());
  108 + entity.setKey(keyId);
  109 + entity.setStrValue(tsKvEntry.getStrValue().orElse(null));
  110 + entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
  111 + entity.setLongValue(tsKvEntry.getLongValue().orElse(null));
  112 + entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
  113 + entity.setJsonValue(tsKvEntry.getJsonValue().orElse(null));
  114 +
  115 + log.trace("Saving entity to timescale db: {}", entity);
  116 + return tsQueue.add(entity);
  117 + }
  118 +
  119 + @Override
  120 + public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
  121 + return Futures.immediateFuture(null);
  122 + }
  123 +
  124 + @Override
  125 + public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  126 + String strKey = query.getKey();
  127 + Integer keyId = getOrSaveKeyId(strKey);
  128 + return service.submit(() -> {
  129 + tsKvRepository.delete(
  130 + entityId.getId(),
  131 + keyId,
  132 + query.getStartTs(),
  133 + query.getEndTs());
  134 + return null;
  135 + });
  136 + }
  137 +
  138 + @Override
  139 + public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  140 + return service.submit(() -> null);
  141 + }
  142 +
  143 + @Override
  144 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
100 if (query.getAggregation() == Aggregation.NONE) { 145 if (query.getAggregation() == Aggregation.NONE) {
101 return findAllAsyncWithLimit(entityId, query); 146 return findAllAsyncWithLimit(entityId, query);
102 } else { 147 } else {
@@ -108,8 +153,7 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -108,8 +153,7 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
108 } 153 }
109 } 154 }
110 155
111 - @Override  
112 - protected ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) { 156 + private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
113 String strKey = query.getKey(); 157 String strKey = query.getKey();
114 Integer keyId = getOrSaveKeyId(strKey); 158 Integer keyId = getOrSaveKeyId(strKey);
115 List<TimescaleTsKvEntity> timescaleTsKvEntities = tsKvRepository.findAllWithLimit( 159 List<TimescaleTsKvEntity> timescaleTsKvEntities = tsKvRepository.findAllWithLimit(
@@ -153,73 +197,6 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -153,73 +197,6 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
153 }, MoreExecutors.directExecutor()); 197 }, MoreExecutors.directExecutor());
154 } 198 }
155 199
156 - @Override  
157 - public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {  
158 - return processFindAllAsync(tenantId, entityId, queries);  
159 - }  
160 -  
161 - @Override  
162 - public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {  
163 - return getFindLatestFuture(entityId, key);  
164 - }  
165 -  
166 - @Override  
167 - public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {  
168 - return getFindAllLatestFuture(entityId);  
169 - }  
170 -  
171 - @Override  
172 - public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {  
173 - String strKey = tsKvEntry.getKey();  
174 - Integer keyId = getOrSaveKeyId(strKey);  
175 - TimescaleTsKvEntity entity = new TimescaleTsKvEntity();  
176 - entity.setEntityId(entityId.getId());  
177 - entity.setTs(tsKvEntry.getTs());  
178 - entity.setKey(keyId);  
179 - entity.setStrValue(tsKvEntry.getStrValue().orElse(null));  
180 - entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));  
181 - entity.setLongValue(tsKvEntry.getLongValue().orElse(null));  
182 - entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));  
183 - entity.setJsonValue(tsKvEntry.getJsonValue().orElse(null));  
184 -  
185 - log.trace("Saving entity to timescale db: {}", entity);  
186 - return tsQueue.add(entity);  
187 - }  
188 -  
189 - @Override  
190 - public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {  
191 - return Futures.immediateFuture(null);  
192 - }  
193 -  
194 - @Override  
195 - public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {  
196 - return getSaveLatestFuture(entityId, tsKvEntry);  
197 - }  
198 -  
199 - @Override  
200 - public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
201 - String strKey = query.getKey();  
202 - Integer keyId = getOrSaveKeyId(strKey);  
203 - return service.submit(() -> {  
204 - tsKvRepository.delete(  
205 - entityId.getId(),  
206 - keyId,  
207 - query.getStartTs(),  
208 - query.getEndTs());  
209 - return null;  
210 - });  
211 - }  
212 -  
213 - @Override  
214 - public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
215 - return getRemoveLatestFuture(entityId, query);  
216 - }  
217 -  
218 - @Override  
219 - public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
220 - return service.submit(() -> null);  
221 - }  
222 -  
223 private CompletableFuture<List<TimescaleTsKvEntity>> switchAggregation(String key, long startTs, long endTs, long timeBucket, Aggregation aggregation, UUID entityId) { 200 private CompletableFuture<List<TimescaleTsKvEntity>> switchAggregation(String key, long startTs, long endTs, long timeBucket, Aggregation aggregation, UUID entityId) {
224 switch (aggregation) { 201 switch (aggregation) {
225 case AVG: 202 case AVG:
@@ -23,12 +23,12 @@ import org.springframework.data.repository.query.Param; @@ -23,12 +23,12 @@ import org.springframework.data.repository.query.Param;
23 import org.springframework.transaction.annotation.Transactional; 23 import org.springframework.transaction.annotation.Transactional;
24 import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvCompositeKey; 24 import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvCompositeKey;
25 import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvEntity; 25 import org.thingsboard.server.dao.model.sqlts.timescale.ts.TimescaleTsKvEntity;
26 -import org.thingsboard.server.dao.util.TimescaleDBTsDao; 26 +import org.thingsboard.server.dao.util.TimescaleDBTsOrTsLatestDao;
27 27
28 import java.util.List; 28 import java.util.List;
29 import java.util.UUID; 29 import java.util.UUID;
30 30
31 -@TimescaleDBTsDao 31 +@TimescaleDBTsOrTsLatestDao
32 public interface TsKvTimescaleRepository extends CrudRepository<TimescaleTsKvEntity, TimescaleTsKvCompositeKey> { 32 public interface TsKvTimescaleRepository extends CrudRepository<TimescaleTsKvEntity, TimescaleTsKvCompositeKey> {
33 33
34 @Query("SELECT tskv FROM TimescaleTsKvEntity tskv WHERE tskv.entityId = :entityId " + 34 @Query("SELECT tskv FROM TimescaleTsKvEntity tskv WHERE tskv.entityId = :entityId " +
@@ -24,13 +24,11 @@ import org.springframework.scheduling.annotation.Async; @@ -24,13 +24,11 @@ import org.springframework.scheduling.annotation.Async;
24 import org.springframework.transaction.annotation.Transactional; 24 import org.springframework.transaction.annotation.Transactional;
25 import org.thingsboard.server.dao.model.sqlts.ts.TsKvCompositeKey; 25 import org.thingsboard.server.dao.model.sqlts.ts.TsKvCompositeKey;
26 import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; 26 import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity;
27 -import org.thingsboard.server.dao.util.SqlDao;  
28 27
29 import java.util.List; 28 import java.util.List;
30 import java.util.UUID; 29 import java.util.UUID;
31 import java.util.concurrent.CompletableFuture; 30 import java.util.concurrent.CompletableFuture;
32 31
33 -@SqlDao  
34 public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvCompositeKey> { 32 public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvCompositeKey> {
35 33
36 @Query("SELECT tskv FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " + 34 @Query("SELECT tskv FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " +
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.timeseries;
  17 +
  18 +import com.datastax.oss.driver.api.core.cql.Row;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.apache.commons.lang3.StringUtils;
  21 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  22 +import org.thingsboard.server.common.data.kv.BooleanDataEntry;
  23 +import org.thingsboard.server.common.data.kv.DoubleDataEntry;
  24 +import org.thingsboard.server.common.data.kv.JsonDataEntry;
  25 +import org.thingsboard.server.common.data.kv.KvEntry;
  26 +import org.thingsboard.server.common.data.kv.LongDataEntry;
  27 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  28 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  29 +import org.thingsboard.server.dao.model.ModelConstants;
  30 +import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao;
  31 +
  32 +import java.util.ArrayList;
  33 +import java.util.List;
  34 +
  35 +@Slf4j
  36 +public abstract class AbstractCassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao {
  37 + public static final String DESC_ORDER = "DESC";
  38 + public static final String GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID = "Generated query [{}] for entityType {} and entityId {}";
  39 + public static final String INSERT_INTO = "INSERT INTO ";
  40 + public static final String SELECT_PREFIX = "SELECT ";
  41 + public static final String EQUALS_PARAM = " = ? ";
  42 +
  43 + public static KvEntry toKvEntry(Row row, String key) {
  44 + KvEntry kvEntry = null;
  45 + String strV = row.get(ModelConstants.STRING_VALUE_COLUMN, String.class);
  46 + if (strV != null) {
  47 + kvEntry = new StringDataEntry(key, strV);
  48 + } else {
  49 + Long longV = row.get(ModelConstants.LONG_VALUE_COLUMN, Long.class);
  50 + if (longV != null) {
  51 + kvEntry = new LongDataEntry(key, longV);
  52 + } else {
  53 + Double doubleV = row.get(ModelConstants.DOUBLE_VALUE_COLUMN, Double.class);
  54 + if (doubleV != null) {
  55 + kvEntry = new DoubleDataEntry(key, doubleV);
  56 + } else {
  57 + Boolean boolV = row.get(ModelConstants.BOOLEAN_VALUE_COLUMN, Boolean.class);
  58 + if (boolV != null) {
  59 + kvEntry = new BooleanDataEntry(key, boolV);
  60 + } else {
  61 + String jsonV = row.get(ModelConstants.JSON_VALUE_COLUMN, String.class);
  62 + if (StringUtils.isNoneEmpty(jsonV)) {
  63 + kvEntry = new JsonDataEntry(key, jsonV);
  64 + } else {
  65 + log.warn("All values in key-value row are nullable ");
  66 + }
  67 + }
  68 + }
  69 + }
  70 + }
  71 + return kvEntry;
  72 + }
  73 +
  74 + protected List<TsKvEntry> convertResultToTsKvEntryList(List<Row> rows) {
  75 + List<TsKvEntry> entries = new ArrayList<>(rows.size());
  76 + if (!rows.isEmpty()) {
  77 + rows.forEach(row -> entries.add(convertResultToTsKvEntry(row)));
  78 + }
  79 + return entries;
  80 + }
  81 +
  82 + private TsKvEntry convertResultToTsKvEntry(Row row) {
  83 + String key = row.getString(ModelConstants.KEY_COLUMN);
  84 + long ts = row.getLong(ModelConstants.TS_COLUMN);
  85 + return new BasicTsKvEntry(ts, toKvEntry(row, key));
  86 + }
  87 +
  88 +}
@@ -60,6 +60,9 @@ public class BaseTimeseriesService implements TimeseriesService { @@ -60,6 +60,9 @@ public class BaseTimeseriesService implements TimeseriesService {
60 private TimeseriesDao timeseriesDao; 60 private TimeseriesDao timeseriesDao;
61 61
62 @Autowired 62 @Autowired
  63 + private TimeseriesLatestDao timeseriesLatestDao;
  64 +
  65 + @Autowired
63 private EntityViewService entityViewService; 66 private EntityViewService entityViewService;
64 67
65 @Override 68 @Override
@@ -103,7 +106,7 @@ public class BaseTimeseriesService implements TimeseriesService { @@ -103,7 +106,7 @@ public class BaseTimeseriesService implements TimeseriesService {
103 return Futures.immediateFuture(new ArrayList<>()); 106 return Futures.immediateFuture(new ArrayList<>());
104 } 107 }
105 } 108 }
106 - keys.forEach(key -> futures.add(timeseriesDao.findLatest(tenantId, entityId, key))); 109 + keys.forEach(key -> futures.add(timeseriesLatestDao.findLatest(tenantId, entityId, key)));
107 return Futures.allAsList(futures); 110 return Futures.allAsList(futures);
108 } 111 }
109 112
@@ -119,7 +122,7 @@ public class BaseTimeseriesService implements TimeseriesService { @@ -119,7 +122,7 @@ public class BaseTimeseriesService implements TimeseriesService {
119 return Futures.immediateFuture(new ArrayList<>()); 122 return Futures.immediateFuture(new ArrayList<>());
120 } 123 }
121 } else { 124 } else {
122 - return timeseriesDao.findAllLatest(tenantId, entityId); 125 + return timeseriesLatestDao.findAllLatest(tenantId, entityId);
123 } 126 }
124 } 127 }
125 128
@@ -151,7 +154,7 @@ public class BaseTimeseriesService implements TimeseriesService { @@ -151,7 +154,7 @@ public class BaseTimeseriesService implements TimeseriesService {
151 throw new IncorrectParameterException("Telemetry data can't be stored for entity view. Read only"); 154 throw new IncorrectParameterException("Telemetry data can't be stored for entity view. Read only");
152 } 155 }
153 futures.add(timeseriesDao.savePartition(tenantId, entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl)); 156 futures.add(timeseriesDao.savePartition(tenantId, entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl));
154 - futures.add(timeseriesDao.saveLatest(tenantId, entityId, tsKvEntry)); 157 + futures.add(timeseriesLatestDao.saveLatest(tenantId, entityId, tsKvEntry));
155 futures.add(timeseriesDao.save(tenantId, entityId, tsKvEntry, ttl)); 158 futures.add(timeseriesDao.save(tenantId, entityId, tsKvEntry, ttl));
156 } 159 }
157 160
@@ -187,7 +190,7 @@ public class BaseTimeseriesService implements TimeseriesService { @@ -187,7 +190,7 @@ public class BaseTimeseriesService implements TimeseriesService {
187 190
188 private void deleteAndRegisterFutures(TenantId tenantId, List<ListenableFuture<Void>> futures, EntityId entityId, DeleteTsKvQuery query) { 191 private void deleteAndRegisterFutures(TenantId tenantId, List<ListenableFuture<Void>> futures, EntityId entityId, DeleteTsKvQuery query) {
189 futures.add(timeseriesDao.remove(tenantId, entityId, query)); 192 futures.add(timeseriesDao.remove(tenantId, entityId, query));
190 - futures.add(timeseriesDao.removeLatest(tenantId, entityId, query)); 193 + futures.add(timeseriesLatestDao.removeLatest(tenantId, entityId, query));
191 futures.add(timeseriesDao.removePartition(tenantId, entityId, query)); 194 futures.add(timeseriesDao.removePartition(tenantId, entityId, query));
192 } 195 }
193 196
@@ -20,7 +20,6 @@ import com.datastax.oss.driver.api.core.cql.BoundStatement; @@ -20,7 +20,6 @@ import com.datastax.oss.driver.api.core.cql.BoundStatement;
20 import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder; 20 import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder;
21 import com.datastax.oss.driver.api.core.cql.PreparedStatement; 21 import com.datastax.oss.driver.api.core.cql.PreparedStatement;
22 import com.datastax.oss.driver.api.core.cql.Row; 22 import com.datastax.oss.driver.api.core.cql.Row;
23 -import com.datastax.oss.driver.api.core.cql.Statement;  
24 import com.datastax.oss.driver.api.querybuilder.QueryBuilder; 23 import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
25 import com.datastax.oss.driver.api.querybuilder.select.Select; 24 import com.datastax.oss.driver.api.querybuilder.select.Select;
26 import com.google.common.base.Function; 25 import com.google.common.base.Function;
@@ -30,7 +29,6 @@ import com.google.common.util.concurrent.Futures; @@ -30,7 +29,6 @@ import com.google.common.util.concurrent.Futures;
30 import com.google.common.util.concurrent.ListenableFuture; 29 import com.google.common.util.concurrent.ListenableFuture;
31 import com.google.common.util.concurrent.MoreExecutors; 30 import com.google.common.util.concurrent.MoreExecutors;
32 import lombok.extern.slf4j.Slf4j; 31 import lombok.extern.slf4j.Slf4j;
33 -import org.apache.commons.lang3.StringUtils;  
34 import org.springframework.beans.factory.annotation.Autowired; 32 import org.springframework.beans.factory.annotation.Autowired;
35 import org.springframework.beans.factory.annotation.Value; 33 import org.springframework.beans.factory.annotation.Value;
36 import org.springframework.core.env.Environment; 34 import org.springframework.core.env.Environment;
@@ -39,21 +37,15 @@ import org.thingsboard.server.common.data.id.EntityId; @@ -39,21 +37,15 @@ import org.thingsboard.server.common.data.id.EntityId;
39 import org.thingsboard.server.common.data.id.TenantId; 37 import org.thingsboard.server.common.data.id.TenantId;
40 import org.thingsboard.server.common.data.kv.Aggregation; 38 import org.thingsboard.server.common.data.kv.Aggregation;
41 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; 39 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
42 -import org.thingsboard.server.common.data.kv.BasicTsKvEntry;  
43 -import org.thingsboard.server.common.data.kv.BooleanDataEntry;  
44 import org.thingsboard.server.common.data.kv.DataType; 40 import org.thingsboard.server.common.data.kv.DataType;
45 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; 41 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
46 -import org.thingsboard.server.common.data.kv.DoubleDataEntry;  
47 -import org.thingsboard.server.common.data.kv.JsonDataEntry;  
48 import org.thingsboard.server.common.data.kv.KvEntry; 42 import org.thingsboard.server.common.data.kv.KvEntry;
49 -import org.thingsboard.server.common.data.kv.LongDataEntry;  
50 import org.thingsboard.server.common.data.kv.ReadTsKvQuery; 43 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
51 -import org.thingsboard.server.common.data.kv.StringDataEntry;  
52 import org.thingsboard.server.common.data.kv.TsKvEntry; 44 import org.thingsboard.server.common.data.kv.TsKvEntry;
53 import org.thingsboard.server.dao.model.ModelConstants; 45 import org.thingsboard.server.dao.model.ModelConstants;
54 -import org.thingsboard.server.dao.nosql.CassandraAbstractAsyncDao;  
55 import org.thingsboard.server.dao.nosql.TbResultSet; 46 import org.thingsboard.server.dao.nosql.TbResultSet;
56 import org.thingsboard.server.dao.nosql.TbResultSetFuture; 47 import org.thingsboard.server.dao.nosql.TbResultSetFuture;
  48 +import org.thingsboard.server.dao.sqlts.AggregationTimeseriesDao;
57 import org.thingsboard.server.dao.util.NoSqlTsDao; 49 import org.thingsboard.server.dao.util.NoSqlTsDao;
58 50
59 import javax.annotation.Nullable; 51 import javax.annotation.Nullable;
@@ -68,7 +60,6 @@ import java.util.Arrays; @@ -68,7 +60,6 @@ import java.util.Arrays;
68 import java.util.Collections; 60 import java.util.Collections;
69 import java.util.List; 61 import java.util.List;
70 import java.util.Optional; 62 import java.util.Optional;
71 -import java.util.concurrent.ExecutionException;  
72 import java.util.stream.Collectors; 63 import java.util.stream.Collectors;
73 64
74 import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal; 65 import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal;
@@ -79,16 +70,11 @@ import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal; @@ -79,16 +70,11 @@ import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal;
79 @Component 70 @Component
80 @Slf4j 71 @Slf4j
81 @NoSqlTsDao 72 @NoSqlTsDao
82 -public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implements TimeseriesDao { 73 +public class CassandraBaseTimeseriesDao extends AbstractCassandraBaseTimeseriesDao implements TimeseriesDao, AggregationTimeseriesDao {
83 74
84 - private static final int MIN_AGGREGATION_STEP_MS = 1000;  
85 - public static final String INSERT_INTO = "INSERT INTO ";  
86 - public static final String GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID = "Generated query [{}] for entityType {} and entityId {}";  
87 - public static final String SELECT_PREFIX = "SELECT ";  
88 - public static final String EQUALS_PARAM = " = ? "; 75 + protected static final int MIN_AGGREGATION_STEP_MS = 1000;
89 public static final String ASC_ORDER = "ASC"; 76 public static final String ASC_ORDER = "ASC";
90 - public static final String DESC_ORDER = "DESC";  
91 - private static List<Long> FIXED_PARTITION = Arrays.asList(new Long[]{0L}); 77 + protected static List<Long> FIXED_PARTITION = Arrays.asList(new Long[]{0L});
92 78
93 @Autowired 79 @Autowired
94 private Environment environment; 80 private Environment environment;
@@ -106,13 +92,10 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -106,13 +92,10 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
106 92
107 private PreparedStatement partitionInsertStmt; 93 private PreparedStatement partitionInsertStmt;
108 private PreparedStatement partitionInsertTtlStmt; 94 private PreparedStatement partitionInsertTtlStmt;
109 - private PreparedStatement latestInsertStmt;  
110 private PreparedStatement[] saveStmts; 95 private PreparedStatement[] saveStmts;
111 private PreparedStatement[] saveTtlStmts; 96 private PreparedStatement[] saveTtlStmts;
112 private PreparedStatement[] fetchStmtsAsc; 97 private PreparedStatement[] fetchStmtsAsc;
113 private PreparedStatement[] fetchStmtsDesc; 98 private PreparedStatement[] fetchStmtsDesc;
114 - private PreparedStatement findLatestStmt;  
115 - private PreparedStatement findAllLatestStmt;  
116 private PreparedStatement deleteStmt; 99 private PreparedStatement deleteStmt;
117 private PreparedStatement deletePartitionStmt; 100 private PreparedStatement deletePartitionStmt;
118 101
@@ -157,8 +140,113 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -157,8 +140,113 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
157 }, readResultsProcessingExecutor); 140 }, readResultsProcessingExecutor);
158 } 141 }
159 142
  143 + @Override
  144 + public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
  145 + List<ListenableFuture<Void>> futures = new ArrayList<>();
  146 + ttl = computeTtl(ttl);
  147 + long partition = toPartitionTs(tsKvEntry.getTs());
  148 + DataType type = tsKvEntry.getDataType();
  149 + if (setNullValuesEnabled) {
  150 + processSetNullValues(tenantId, entityId, tsKvEntry, ttl, futures, partition, type);
  151 + }
  152 + BoundStatementBuilder stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getSaveStmt(type) : getSaveTtlStmt(type)).bind());
  153 + stmtBuilder.setString(0, entityId.getEntityType().name())
  154 + .setUuid(1, entityId.getId())
  155 + .setString(2, tsKvEntry.getKey())
  156 + .setLong(3, partition)
  157 + .setLong(4, tsKvEntry.getTs());
  158 + addValue(tsKvEntry, stmtBuilder, 5);
  159 + if (ttl > 0) {
  160 + stmtBuilder.setInt(6, (int) ttl);
  161 + }
  162 + BoundStatement stmt = stmtBuilder.build();
  163 + futures.add(getFuture(executeAsyncWrite(tenantId, stmt), rs -> null));
  164 + return Futures.transform(Futures.allAsList(futures), result -> null, MoreExecutors.directExecutor());
  165 + }
  166 +
  167 + @Override
  168 + public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
  169 + if (isFixedPartitioning()) {
  170 + return Futures.immediateFuture(null);
  171 + }
  172 + ttl = computeTtl(ttl);
  173 + long partition = toPartitionTs(tsKvEntryTs);
  174 + log.debug("Saving partition {} for the entity [{}-{}] and key {}", partition, entityId.getEntityType(), entityId.getId(), key);
  175 + BoundStatementBuilder stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getPartitionInsertStmt() : getPartitionInsertTtlStmt()).bind());
  176 + stmtBuilder.setString(0, entityId.getEntityType().name())
  177 + .setUuid(1, entityId.getId())
  178 + .setLong(2, partition)
  179 + .setString(3, key);
  180 + if (ttl > 0) {
  181 + stmtBuilder.setInt(4, (int) ttl);
  182 + }
  183 + BoundStatement stmt = stmtBuilder.build();
  184 + return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
  185 + }
160 186
161 - private ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) { 187 + @Override
  188 + public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  189 + long minPartition = toPartitionTs(query.getStartTs());
  190 + long maxPartition = toPartitionTs(query.getEndTs());
  191 +
  192 + TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
  193 +
  194 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  195 + final ListenableFuture<List<Long>> partitionsListFuture = Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
  196 +
  197 + Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
  198 + @Override
  199 + public void onSuccess(@Nullable List<Long> partitions) {
  200 + QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitions);
  201 + deleteAsync(tenantId, cursor, resultFuture);
  202 + }
  203 +
  204 + @Override
  205 + public void onFailure(Throwable t) {
  206 + log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);
  207 + }
  208 + }, readResultsProcessingExecutor);
  209 + return resultFuture;
  210 + }
  211 +
  212 + @Override
  213 + public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  214 + long minPartition = toPartitionTs(query.getStartTs());
  215 + long maxPartition = toPartitionTs(query.getEndTs());
  216 + if (minPartition == maxPartition) {
  217 + return Futures.immediateFuture(null);
  218 + } else {
  219 + TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
  220 +
  221 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  222 + final ListenableFuture<List<Long>> partitionsListFuture = Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
  223 +
  224 + Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {
  225 + @Override
  226 + public void onSuccess(@Nullable List<Long> partitions) {
  227 + int index = 0;
  228 + if (minPartition != query.getStartTs()) {
  229 + index = 1;
  230 + }
  231 + List<Long> partitionsToDelete = new ArrayList<>();
  232 + for (int i = index; i < partitions.size() - 1; i++) {
  233 + partitionsToDelete.add(partitions.get(i));
  234 + }
  235 + QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitionsToDelete);
  236 + deletePartitionAsync(tenantId, cursor, resultFuture);
  237 + }
  238 +
  239 + @Override
  240 + public void onFailure(Throwable t) {
  241 + log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);
  242 + }
  243 + }, readResultsProcessingExecutor);
  244 + return resultFuture;
  245 + }
  246 + }
  247 +
  248 + @Override
  249 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
162 if (query.getAggregation() == Aggregation.NONE) { 250 if (query.getAggregation() == Aggregation.NONE) {
163 return findAllAsyncWithLimit(tenantId, entityId, query); 251 return findAllAsyncWithLimit(tenantId, entityId, query);
164 } else { 252 } else {
@@ -183,18 +271,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -183,18 +271,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
183 } 271 }
184 } 272 }
185 273
186 - public boolean isFixedPartitioning() {  
187 - return tsFormat.getTruncateUnit().equals(ChronoUnit.FOREVER);  
188 - }  
189 -  
190 - private ListenableFuture<List<Long>> getPartitionsFuture(TenantId tenantId, ReadTsKvQuery query, EntityId entityId, long minPartition, long maxPartition) {  
191 - if (isFixedPartitioning()) { //no need to fetch partitions from DB  
192 - return Futures.immediateFuture(FIXED_PARTITION);  
193 - }  
194 - TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);  
195 - return Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);  
196 - }  
197 -  
198 private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) { 274 private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
199 long minPartition = toPartitionTs(query.getStartTs()); 275 long minPartition = toPartitionTs(query.getStartTs());
200 long maxPartition = toPartitionTs(query.getEndTs()); 276 long maxPartition = toPartitionTs(query.getEndTs());
@@ -287,10 +363,18 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -287,10 +363,18 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
287 363
288 private AsyncFunction<TbResultSet, List<Long>> getPartitionsArrayFunction() { 364 private AsyncFunction<TbResultSet, List<Long>> getPartitionsArrayFunction() {
289 return rs -> 365 return rs ->
290 - Futures.transform(rs.allRows(readResultsProcessingExecutor), rows ->  
291 - rows.stream()  
292 - .map(row -> row.getLong(ModelConstants.PARTITION_COLUMN)).collect(Collectors.toList()),  
293 - readResultsProcessingExecutor); 366 + Futures.transform(rs.allRows(readResultsProcessingExecutor), rows ->
  367 + rows.stream()
  368 + .map(row -> row.getLong(ModelConstants.PARTITION_COLUMN)).collect(Collectors.toList()),
  369 + readResultsProcessingExecutor);
  370 + }
  371 +
  372 + private ListenableFuture<List<Long>> getPartitionsFuture(TenantId tenantId, ReadTsKvQuery query, EntityId entityId, long minPartition, long maxPartition) {
  373 + if (isFixedPartitioning()) { //no need to fetch partitions from DB
  374 + return Futures.immediateFuture(FIXED_PARTITION);
  375 + }
  376 + TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);
  377 + return Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);
294 } 378 }
295 379
296 private AsyncFunction<List<Long>, List<TbResultSet>> getFetchChunksAsyncFunction(TenantId tenantId, EntityId entityId, String key, Aggregation aggregation, long startTs, long endTs) { 380 private AsyncFunction<List<Long>, List<TbResultSet>> getFetchChunksAsyncFunction(TenantId tenantId, EntityId entityId, String key, Aggregation aggregation, long startTs, long endTs) {
@@ -319,49 +403,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -319,49 +403,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
319 }; 403 };
320 } 404 }
321 405
322 - @Override  
323 - public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {  
324 - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getFindLatestStmt().bind());  
325 - stmtBuilder.setString(0, entityId.getEntityType().name());  
326 - stmtBuilder.setUuid(1, entityId.getId());  
327 - stmtBuilder.setString(2, key);  
328 - BoundStatement stmt = stmtBuilder.build();  
329 - log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());  
330 - return getFuture(executeAsyncRead(tenantId, stmt), rs -> convertResultToTsKvEntry(key, rs.one()));  
331 - }  
332 -  
333 - @Override  
334 - public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {  
335 - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getFindAllLatestStmt().bind());  
336 - stmtBuilder.setString(0, entityId.getEntityType().name());  
337 - stmtBuilder.setUuid(1, entityId.getId());  
338 - BoundStatement stmt = stmtBuilder.build();  
339 - log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());  
340 - return getFutureAsync(executeAsyncRead(tenantId, stmt), rs -> convertAsyncResultSetToTsKvEntryList(rs));  
341 - }  
342 -  
343 - @Override  
344 - public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {  
345 - List<ListenableFuture<Void>> futures = new ArrayList<>();  
346 - ttl = computeTtl(ttl);  
347 - long partition = toPartitionTs(tsKvEntry.getTs());  
348 - DataType type = tsKvEntry.getDataType();  
349 - if (setNullValuesEnabled) {  
350 - processSetNullValues(tenantId, entityId, tsKvEntry, ttl, futures, partition, type);  
351 - }  
352 - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getSaveStmt(type) : getSaveTtlStmt(type)).bind());  
353 - stmtBuilder.setString(0, entityId.getEntityType().name())  
354 - .setUuid(1, entityId.getId())  
355 - .setString(2, tsKvEntry.getKey())  
356 - .setLong(3, partition)  
357 - .setLong(4, tsKvEntry.getTs());  
358 - addValue(tsKvEntry, stmtBuilder, 5);  
359 - if (ttl > 0) {  
360 - stmtBuilder.setInt(6, (int) ttl);  
361 - }  
362 - BoundStatement stmt = stmtBuilder.build();  
363 - futures.add(getFuture(executeAsyncWrite(tenantId, stmt), rs -> null));  
364 - return Futures.transform(Futures.allAsList(futures), result -> null, MoreExecutors.directExecutor()); 406 + private boolean isFixedPartitioning() {
  407 + return tsFormat.getTruncateUnit().equals(ChronoUnit.FOREVER);
365 } 408 }
366 409
367 private void processSetNullValues(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl, List<ListenableFuture<Void>> futures, long partition, DataType type) { 410 private void processSetNullValues(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl, List<ListenableFuture<Void>> futures, long partition, DataType type) {
@@ -414,26 +457,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -414,26 +457,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
414 return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null); 457 return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
415 } 458 }
416 459
417 - @Override  
418 - public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {  
419 - if (isFixedPartitioning()) {  
420 - return Futures.immediateFuture(null);  
421 - }  
422 - ttl = computeTtl(ttl);  
423 - long partition = toPartitionTs(tsKvEntryTs);  
424 - log.debug("Saving partition {} for the entity [{}-{}] and key {}", partition, entityId.getEntityType(), entityId.getId(), key);  
425 - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder((ttl == 0 ? getPartitionInsertStmt() : getPartitionInsertTtlStmt()).bind());  
426 - stmtBuilder.setString(0, entityId.getEntityType().name())  
427 - .setUuid(1, entityId.getId())  
428 - .setLong(2, partition)  
429 - .setString(3, key);  
430 - if (ttl > 0) {  
431 - stmtBuilder.setInt(4, (int) ttl);  
432 - }  
433 - BoundStatement stmt = stmtBuilder.build();  
434 - return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);  
435 - }  
436 -  
437 private long computeTtl(long ttl) { 460 private long computeTtl(long ttl) {
438 if (systemTtl > 0) { 461 if (systemTtl > 0) {
439 if (ttl == 0) { 462 if (ttl == 0) {
@@ -445,53 +468,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -445,53 +468,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
445 return ttl; 468 return ttl;
446 } 469 }
447 470
448 - @Override  
449 - public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {  
450 - BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getLatestStmt().bind());  
451 - stmtBuilder.setString(0, entityId.getEntityType().name())  
452 - .setUuid(1, entityId.getId())  
453 - .setString(2, tsKvEntry.getKey())  
454 - .setLong(3, tsKvEntry.getTs())  
455 - .set(4, tsKvEntry.getBooleanValue().orElse(null), Boolean.class)  
456 - .set(5, tsKvEntry.getStrValue().orElse(null), String.class)  
457 - .set(6, tsKvEntry.getLongValue().orElse(null), Long.class)  
458 - .set(7, tsKvEntry.getDoubleValue().orElse(null), Double.class);  
459 - Optional<String> jsonV = tsKvEntry.getJsonValue();  
460 - if (jsonV.isPresent()) {  
461 - stmtBuilder.setString(8, tsKvEntry.getJsonValue().get());  
462 - } else {  
463 - stmtBuilder.setToNull(8);  
464 - }  
465 - BoundStatement stmt = stmtBuilder.build();  
466 -  
467 - return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);  
468 - }  
469 -  
470 - @Override  
471 - public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
472 - long minPartition = toPartitionTs(query.getStartTs());  
473 - long maxPartition = toPartitionTs(query.getEndTs());  
474 -  
475 - TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);  
476 -  
477 - final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();  
478 - final ListenableFuture<List<Long>> partitionsListFuture = Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);  
479 -  
480 - Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {  
481 - @Override  
482 - public void onSuccess(@Nullable List<Long> partitions) {  
483 - QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitions);  
484 - deleteAsync(tenantId, cursor, resultFuture);  
485 - }  
486 -  
487 - @Override  
488 - public void onFailure(Throwable t) {  
489 - log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);  
490 - }  
491 - }, readResultsProcessingExecutor);  
492 - return resultFuture;  
493 - }  
494 -  
495 private void deleteAsync(TenantId tenantId, final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) { 471 private void deleteAsync(TenantId tenantId, final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
496 if (!cursor.hasNextPartition()) { 472 if (!cursor.hasNextPartition()) {
497 resultFuture.set(null); 473 resultFuture.set(null);
@@ -534,119 +510,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -534,119 +510,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
534 return deleteStmt; 510 return deleteStmt;
535 } 511 }
536 512
537 - @Override  
538 - public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
539 - ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(tenantId, entityId, query.getKey());  
540 -  
541 - ListenableFuture<Boolean> booleanFuture = Futures.transform(latestEntryFuture, latestEntry -> {  
542 - long ts = latestEntry.getTs();  
543 - if (ts > query.getStartTs() && ts <= query.getEndTs()) {  
544 - return true;  
545 - } else {  
546 - log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey());  
547 - }  
548 - return false;  
549 - }, readResultsProcessingExecutor);  
550 -  
551 - ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {  
552 - if (isRemove) {  
553 - return deleteLatest(tenantId, entityId, query.getKey());  
554 - }  
555 - return Futures.immediateFuture(null);  
556 - }, readResultsProcessingExecutor);  
557 -  
558 - final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();  
559 - Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {  
560 - @Override  
561 - public void onSuccess(@Nullable Void result) {  
562 - if (query.getRewriteLatestIfDeleted()) {  
563 - ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {  
564 - if (isRemove) {  
565 - return getNewLatestEntryFuture(tenantId, entityId, query);  
566 - }  
567 - return Futures.immediateFuture(null);  
568 - }, readResultsProcessingExecutor);  
569 -  
570 - try {  
571 - resultFuture.set(savedLatestFuture.get());  
572 - } catch (InterruptedException | ExecutionException e) {  
573 - log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);  
574 - }  
575 - } else {  
576 - resultFuture.set(null);  
577 - }  
578 - }  
579 -  
580 - @Override  
581 - public void onFailure(Throwable t) {  
582 - log.warn("[{}] Failed to process remove of the latest value", entityId, t);  
583 - }  
584 - }, MoreExecutors.directExecutor());  
585 - return resultFuture;  
586 - }  
587 -  
588 - private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
589 - long startTs = 0;  
590 - long endTs = query.getStartTs() - 1;  
591 - ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,  
592 - Aggregation.NONE, DESC_ORDER);  
593 - ListenableFuture<List<TsKvEntry>> future = findAllAsync(tenantId, entityId, findNewLatestQuery);  
594 -  
595 - return Futures.transformAsync(future, entryList -> {  
596 - if (entryList.size() == 1) {  
597 - return saveLatest(tenantId, entityId, entryList.get(0));  
598 - } else {  
599 - log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());  
600 - }  
601 - return Futures.immediateFuture(null);  
602 - }, readResultsProcessingExecutor);  
603 - }  
604 -  
605 - private ListenableFuture<Void> deleteLatest(TenantId tenantId, EntityId entityId, String key) {  
606 - Statement delete = QueryBuilder.deleteFrom(ModelConstants.TS_KV_LATEST_CF)  
607 - .whereColumn(ModelConstants.ENTITY_TYPE_COLUMN).isEqualTo(literal(entityId.getEntityType().name()))  
608 - .whereColumn(ModelConstants.ENTITY_ID_COLUMN).isEqualTo(literal(entityId.getId()))  
609 - .whereColumn(ModelConstants.KEY_COLUMN).isEqualTo(literal(key)).build();  
610 - log.debug("Remove request: {}", delete.toString());  
611 - return getFuture(executeAsyncWrite(tenantId, delete), rs -> null);  
612 - }  
613 -  
614 - @Override  
615 - public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
616 - long minPartition = toPartitionTs(query.getStartTs());  
617 - long maxPartition = toPartitionTs(query.getEndTs());  
618 - if (minPartition == maxPartition) {  
619 - return Futures.immediateFuture(null);  
620 - } else {  
621 - TbResultSetFuture partitionsFuture = fetchPartitions(tenantId, entityId, query.getKey(), minPartition, maxPartition);  
622 -  
623 - final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();  
624 - final ListenableFuture<List<Long>> partitionsListFuture = Futures.transformAsync(partitionsFuture, getPartitionsArrayFunction(), readResultsProcessingExecutor);  
625 -  
626 - Futures.addCallback(partitionsListFuture, new FutureCallback<List<Long>>() {  
627 - @Override  
628 - public void onSuccess(@Nullable List<Long> partitions) {  
629 - int index = 0;  
630 - if (minPartition != query.getStartTs()) {  
631 - index = 1;  
632 - }  
633 - List<Long> partitionsToDelete = new ArrayList<>();  
634 - for (int i = index; i < partitions.size() - 1; i++) {  
635 - partitionsToDelete.add(partitions.get(i));  
636 - }  
637 - QueryCursor cursor = new QueryCursor(entityId.getEntityType().name(), entityId.getId(), query, partitionsToDelete);  
638 - deletePartitionAsync(tenantId, cursor, resultFuture);  
639 - }  
640 -  
641 - @Override  
642 - public void onFailure(Throwable t) {  
643 - log.error("[{}][{}] Failed to fetch partitions for interval {}-{}", entityId.getEntityType().name(), entityId.getId(), minPartition, maxPartition, t);  
644 - }  
645 - }, readResultsProcessingExecutor);  
646 - return resultFuture;  
647 - }  
648 - }  
649 -  
650 private void deletePartitionAsync(TenantId tenantId, final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) { 513 private void deletePartitionAsync(TenantId tenantId, final QueryCursor cursor, final SimpleListenableFuture<Void> resultFuture) {
651 if (!cursor.hasNextPartition()) { 514 if (!cursor.hasNextPartition()) {
652 resultFuture.set(null); 515 resultFuture.set(null);
@@ -685,79 +548,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -685,79 +548,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
685 return deletePartitionStmt; 548 return deletePartitionStmt;
686 } 549 }
687 550
688 - private ListenableFuture<List<TsKvEntry>> convertAsyncResultSetToTsKvEntryList(TbResultSet rs) {  
689 - return Futures.transform(rs.allRows(readResultsProcessingExecutor),  
690 - rows -> this.convertResultToTsKvEntryList(rows), readResultsProcessingExecutor);  
691 - }  
692 -  
693 - private List<TsKvEntry> convertResultToTsKvEntryList(List<Row> rows) {  
694 - List<TsKvEntry> entries = new ArrayList<>(rows.size());  
695 - if (!rows.isEmpty()) {  
696 - rows.forEach(row -> entries.add(convertResultToTsKvEntry(row)));  
697 - }  
698 - return entries;  
699 - }  
700 -  
701 - private TsKvEntry convertResultToTsKvEntry(String key, Row row) {  
702 - if (row != null) {  
703 - long ts = row.getLong(ModelConstants.TS_COLUMN);  
704 - return new BasicTsKvEntry(ts, toKvEntry(row, key));  
705 - } else {  
706 - return new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));  
707 - }  
708 - }  
709 -  
710 - private TsKvEntry convertResultToTsKvEntry(Row row) {  
711 - String key = row.getString(ModelConstants.KEY_COLUMN);  
712 - long ts = row.getLong(ModelConstants.TS_COLUMN);  
713 - return new BasicTsKvEntry(ts, toKvEntry(row, key));  
714 - }  
715 -  
716 - public static KvEntry toKvEntry(Row row, String key) {  
717 - KvEntry kvEntry = null;  
718 - String strV = row.get(ModelConstants.STRING_VALUE_COLUMN, String.class);  
719 - if (strV != null) {  
720 - kvEntry = new StringDataEntry(key, strV);  
721 - } else {  
722 - Long longV = row.get(ModelConstants.LONG_VALUE_COLUMN, Long.class);  
723 - if (longV != null) {  
724 - kvEntry = new LongDataEntry(key, longV);  
725 - } else {  
726 - Double doubleV = row.get(ModelConstants.DOUBLE_VALUE_COLUMN, Double.class);  
727 - if (doubleV != null) {  
728 - kvEntry = new DoubleDataEntry(key, doubleV);  
729 - } else {  
730 - Boolean boolV = row.get(ModelConstants.BOOLEAN_VALUE_COLUMN, Boolean.class);  
731 - if (boolV != null) {  
732 - kvEntry = new BooleanDataEntry(key, boolV);  
733 - } else {  
734 - String jsonV = row.get(ModelConstants.JSON_VALUE_COLUMN, String.class);  
735 - if (StringUtils.isNoneEmpty(jsonV)) {  
736 - kvEntry = new JsonDataEntry(key, jsonV);  
737 - } else {  
738 - log.warn("All values in key-value row are nullable ");  
739 - }  
740 - }  
741 - }  
742 - }  
743 - }  
744 - return kvEntry;  
745 - }  
746 -  
747 - /**  
748 - * Select existing partitions from the table  
749 - * <code>{@link ModelConstants#TS_KV_PARTITIONS_CF}</code> for the given entity  
750 - */  
751 - private TbResultSetFuture fetchPartitions(TenantId tenantId, EntityId entityId, String key, long minPartition, long maxPartition) {  
752 - Select select = QueryBuilder.selectFrom(ModelConstants.TS_KV_PARTITIONS_CF).column(ModelConstants.PARTITION_COLUMN)  
753 - .whereColumn(ModelConstants.ENTITY_TYPE_COLUMN).isEqualTo(literal(entityId.getEntityType().name()))  
754 - .whereColumn(ModelConstants.ENTITY_ID_COLUMN).isEqualTo(literal(entityId.getId()))  
755 - .whereColumn(ModelConstants.KEY_COLUMN).isEqualTo(literal(key))  
756 - .whereColumn(ModelConstants.PARTITION_COLUMN).isGreaterThanOrEqualTo(literal(minPartition))  
757 - .whereColumn(ModelConstants.PARTITION_COLUMN).isLessThanOrEqualTo(literal(maxPartition));  
758 - return executeAsyncRead(tenantId, select.build());  
759 - }  
760 -  
761 private PreparedStatement getSaveStmt(DataType dataType) { 551 private PreparedStatement getSaveStmt(DataType dataType) {
762 if (saveStmts == null) { 552 if (saveStmts == null) {
763 saveStmts = new PreparedStatement[DataType.values().length]; 553 saveStmts = new PreparedStatement[DataType.values().length];
@@ -792,63 +582,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -792,63 +582,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
792 return saveTtlStmts[dataType.ordinal()]; 582 return saveTtlStmts[dataType.ordinal()];
793 } 583 }
794 584
795 - private PreparedStatement getFetchStmt(Aggregation aggType, String orderBy) {  
796 - switch (orderBy) {  
797 - case ASC_ORDER:  
798 - if (fetchStmtsAsc == null) {  
799 - fetchStmtsAsc = initFetchStmt(orderBy);  
800 - }  
801 - return fetchStmtsAsc[aggType.ordinal()];  
802 - case DESC_ORDER:  
803 - if (fetchStmtsDesc == null) {  
804 - fetchStmtsDesc = initFetchStmt(orderBy);  
805 - }  
806 - return fetchStmtsDesc[aggType.ordinal()];  
807 - default:  
808 - throw new RuntimeException("Not supported" + orderBy + "order!");  
809 - }  
810 - }  
811 -  
812 - private PreparedStatement[] initFetchStmt(String orderBy) {  
813 - PreparedStatement[] fetchStmts = new PreparedStatement[Aggregation.values().length];  
814 - for (Aggregation type : Aggregation.values()) {  
815 - if (type == Aggregation.SUM && fetchStmts[Aggregation.AVG.ordinal()] != null) {  
816 - fetchStmts[type.ordinal()] = fetchStmts[Aggregation.AVG.ordinal()];  
817 - } else if (type == Aggregation.AVG && fetchStmts[Aggregation.SUM.ordinal()] != null) {  
818 - fetchStmts[type.ordinal()] = fetchStmts[Aggregation.SUM.ordinal()];  
819 - } else {  
820 - fetchStmts[type.ordinal()] = prepare(SELECT_PREFIX +  
821 - String.join(", ", ModelConstants.getFetchColumnNames(type)) + " FROM " + ModelConstants.TS_KV_CF  
822 - + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM  
823 - + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM  
824 - + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM  
825 - + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM  
826 - + "AND " + ModelConstants.TS_COLUMN + " > ? "  
827 - + "AND " + ModelConstants.TS_COLUMN + " <= ?"  
828 - + (type == Aggregation.NONE ? " ORDER BY " + ModelConstants.TS_COLUMN + " " + orderBy + " LIMIT ?" : ""));  
829 - }  
830 - }  
831 - return fetchStmts;  
832 - }  
833 -  
834 - private PreparedStatement getLatestStmt() {  
835 - if (latestInsertStmt == null) {  
836 - latestInsertStmt = prepare(INSERT_INTO + ModelConstants.TS_KV_LATEST_CF +  
837 - "(" + ModelConstants.ENTITY_TYPE_COLUMN +  
838 - "," + ModelConstants.ENTITY_ID_COLUMN +  
839 - "," + ModelConstants.KEY_COLUMN +  
840 - "," + ModelConstants.TS_COLUMN +  
841 - "," + ModelConstants.BOOLEAN_VALUE_COLUMN +  
842 - "," + ModelConstants.STRING_VALUE_COLUMN +  
843 - "," + ModelConstants.LONG_VALUE_COLUMN +  
844 - "," + ModelConstants.DOUBLE_VALUE_COLUMN +  
845 - "," + ModelConstants.JSON_VALUE_COLUMN + ")" +  
846 - " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)");  
847 - }  
848 - return latestInsertStmt;  
849 - }  
850 -  
851 -  
852 private PreparedStatement getPartitionInsertStmt() { 585 private PreparedStatement getPartitionInsertStmt() {
853 if (partitionInsertStmt == null) { 586 if (partitionInsertStmt == null) {
854 partitionInsertStmt = prepare(INSERT_INTO + ModelConstants.TS_KV_PARTITIONS_CF + 587 partitionInsertStmt = prepare(INSERT_INTO + ModelConstants.TS_KV_PARTITIONS_CF +
@@ -873,42 +606,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -873,42 +606,6 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
873 return partitionInsertTtlStmt; 606 return partitionInsertTtlStmt;
874 } 607 }
875 608
876 -  
877 - private PreparedStatement getFindLatestStmt() {  
878 - if (findLatestStmt == null) {  
879 - findLatestStmt = prepare(SELECT_PREFIX +  
880 - ModelConstants.KEY_COLUMN + "," +  
881 - ModelConstants.TS_COLUMN + "," +  
882 - ModelConstants.STRING_VALUE_COLUMN + "," +  
883 - ModelConstants.BOOLEAN_VALUE_COLUMN + "," +  
884 - ModelConstants.LONG_VALUE_COLUMN + "," +  
885 - ModelConstants.DOUBLE_VALUE_COLUMN + "," +  
886 - ModelConstants.JSON_VALUE_COLUMN + " " +  
887 - "FROM " + ModelConstants.TS_KV_LATEST_CF + " " +  
888 - "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM +  
889 - "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM +  
890 - "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM);  
891 - }  
892 - return findLatestStmt;  
893 - }  
894 -  
895 - private PreparedStatement getFindAllLatestStmt() {  
896 - if (findAllLatestStmt == null) {  
897 - findAllLatestStmt = prepare(SELECT_PREFIX +  
898 - ModelConstants.KEY_COLUMN + "," +  
899 - ModelConstants.TS_COLUMN + "," +  
900 - ModelConstants.STRING_VALUE_COLUMN + "," +  
901 - ModelConstants.BOOLEAN_VALUE_COLUMN + "," +  
902 - ModelConstants.LONG_VALUE_COLUMN + "," +  
903 - ModelConstants.DOUBLE_VALUE_COLUMN + "," +  
904 - ModelConstants.JSON_VALUE_COLUMN + " " +  
905 - "FROM " + ModelConstants.TS_KV_LATEST_CF + " " +  
906 - "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM +  
907 - "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM);  
908 - }  
909 - return findAllLatestStmt;  
910 - }  
911 -  
912 private static String getColumnName(DataType type) { 609 private static String getColumnName(DataType type) {
913 switch (type) { 610 switch (type) {
914 case BOOLEAN: 611 case BOOLEAN:
@@ -951,4 +648,56 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -951,4 +648,56 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
951 } 648 }
952 } 649 }
953 650
  651 + /**
  652 + // * Select existing partitions from the table
  653 + // * <code>{@link ModelConstants#TS_KV_PARTITIONS_CF}</code> for the given entity
  654 + // */
  655 + private TbResultSetFuture fetchPartitions(TenantId tenantId, EntityId entityId, String key, long minPartition, long maxPartition) {
  656 + Select select = QueryBuilder.selectFrom(ModelConstants.TS_KV_PARTITIONS_CF).column(ModelConstants.PARTITION_COLUMN)
  657 + .whereColumn(ModelConstants.ENTITY_TYPE_COLUMN).isEqualTo(literal(entityId.getEntityType().name()))
  658 + .whereColumn(ModelConstants.ENTITY_ID_COLUMN).isEqualTo(literal(entityId.getId()))
  659 + .whereColumn(ModelConstants.KEY_COLUMN).isEqualTo(literal(key))
  660 + .whereColumn(ModelConstants.PARTITION_COLUMN).isGreaterThanOrEqualTo(literal(minPartition))
  661 + .whereColumn(ModelConstants.PARTITION_COLUMN).isLessThanOrEqualTo(literal(maxPartition));
  662 + return executeAsyncRead(tenantId, select.build());
  663 + }
  664 +
  665 + private PreparedStatement getFetchStmt(Aggregation aggType, String orderBy) {
  666 + switch (orderBy) {
  667 + case ASC_ORDER:
  668 + if (fetchStmtsAsc == null) {
  669 + fetchStmtsAsc = initFetchStmt(orderBy);
  670 + }
  671 + return fetchStmtsAsc[aggType.ordinal()];
  672 + case DESC_ORDER:
  673 + if (fetchStmtsDesc == null) {
  674 + fetchStmtsDesc = initFetchStmt(orderBy);
  675 + }
  676 + return fetchStmtsDesc[aggType.ordinal()];
  677 + default:
  678 + throw new RuntimeException("Not supported" + orderBy + "order!");
  679 + }
  680 + }
  681 +
  682 + private PreparedStatement[] initFetchStmt(String orderBy) {
  683 + PreparedStatement[] fetchStmts = new PreparedStatement[Aggregation.values().length];
  684 + for (Aggregation type : Aggregation.values()) {
  685 + if (type == Aggregation.SUM && fetchStmts[Aggregation.AVG.ordinal()] != null) {
  686 + fetchStmts[type.ordinal()] = fetchStmts[Aggregation.AVG.ordinal()];
  687 + } else if (type == Aggregation.AVG && fetchStmts[Aggregation.SUM.ordinal()] != null) {
  688 + fetchStmts[type.ordinal()] = fetchStmts[Aggregation.SUM.ordinal()];
  689 + } else {
  690 + fetchStmts[type.ordinal()] = prepare(SELECT_PREFIX +
  691 + String.join(", ", ModelConstants.getFetchColumnNames(type)) + " FROM " + ModelConstants.TS_KV_CF
  692 + + " WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM
  693 + + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM
  694 + + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM
  695 + + "AND " + ModelConstants.PARTITION_COLUMN + EQUALS_PARAM
  696 + + "AND " + ModelConstants.TS_COLUMN + " > ? "
  697 + + "AND " + ModelConstants.TS_COLUMN + " <= ?"
  698 + + (type == Aggregation.NONE ? " ORDER BY " + ModelConstants.TS_COLUMN + " " + orderBy + " LIMIT ?" : ""));
  699 + }
  700 + }
  701 + return fetchStmts;
  702 + }
954 } 703 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.timeseries;
  17 +
  18 +import com.datastax.oss.driver.api.core.cql.BoundStatement;
  19 +import com.datastax.oss.driver.api.core.cql.BoundStatementBuilder;
  20 +import com.datastax.oss.driver.api.core.cql.PreparedStatement;
  21 +import com.datastax.oss.driver.api.core.cql.Row;
  22 +import com.datastax.oss.driver.api.core.cql.Statement;
  23 +import com.datastax.oss.driver.api.querybuilder.QueryBuilder;
  24 +import com.google.common.util.concurrent.FutureCallback;
  25 +import com.google.common.util.concurrent.Futures;
  26 +import com.google.common.util.concurrent.ListenableFuture;
  27 +import com.google.common.util.concurrent.MoreExecutors;
  28 +import lombok.extern.slf4j.Slf4j;
  29 +import org.springframework.beans.factory.annotation.Autowired;
  30 +import org.springframework.stereotype.Component;
  31 +import org.thingsboard.server.common.data.id.EntityId;
  32 +import org.thingsboard.server.common.data.id.TenantId;
  33 +import org.thingsboard.server.common.data.kv.Aggregation;
  34 +import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
  35 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  36 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  37 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
  38 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  39 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  40 +import org.thingsboard.server.dao.model.ModelConstants;
  41 +import org.thingsboard.server.dao.nosql.TbResultSet;
  42 +import org.thingsboard.server.dao.sqlts.AggregationTimeseriesDao;
  43 +import org.thingsboard.server.dao.util.NoSqlTsLatestDao;
  44 +
  45 +import javax.annotation.Nullable;
  46 +import java.util.List;
  47 +import java.util.Optional;
  48 +import java.util.concurrent.ExecutionException;
  49 +
  50 +import static com.datastax.oss.driver.api.querybuilder.QueryBuilder.literal;
  51 +
  52 +@Component
  53 +@Slf4j
  54 +@NoSqlTsLatestDao
  55 +public class CassandraBaseTimeseriesLatestDao extends AbstractCassandraBaseTimeseriesDao implements TimeseriesLatestDao {
  56 +
  57 + @Autowired
  58 + protected AggregationTimeseriesDao aggregationTimeseriesDao;
  59 +
  60 + private PreparedStatement latestInsertStmt;
  61 + private PreparedStatement findLatestStmt;
  62 + private PreparedStatement findAllLatestStmt;
  63 +
  64 + @Override
  65 + public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
  66 + BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getFindLatestStmt().bind());
  67 + stmtBuilder.setString(0, entityId.getEntityType().name());
  68 + stmtBuilder.setUuid(1, entityId.getId());
  69 + stmtBuilder.setString(2, key);
  70 + BoundStatement stmt = stmtBuilder.build();
  71 + log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());
  72 + return getFuture(executeAsyncRead(tenantId, stmt), rs -> convertResultToTsKvEntry(key, rs.one()));
  73 + }
  74 +
  75 + @Override
  76 + public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
  77 + BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getFindAllLatestStmt().bind());
  78 + stmtBuilder.setString(0, entityId.getEntityType().name());
  79 + stmtBuilder.setUuid(1, entityId.getId());
  80 + BoundStatement stmt = stmtBuilder.build();
  81 + log.debug(GENERATED_QUERY_FOR_ENTITY_TYPE_AND_ENTITY_ID, stmt, entityId.getEntityType(), entityId.getId());
  82 + return getFutureAsync(executeAsyncRead(tenantId, stmt), rs -> convertAsyncResultSetToTsKvEntryList(rs));
  83 + }
  84 +
  85 + @Override
  86 + public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
  87 + BoundStatementBuilder stmtBuilder = new BoundStatementBuilder(getLatestStmt().bind());
  88 + stmtBuilder.setString(0, entityId.getEntityType().name())
  89 + .setUuid(1, entityId.getId())
  90 + .setString(2, tsKvEntry.getKey())
  91 + .setLong(3, tsKvEntry.getTs())
  92 + .set(4, tsKvEntry.getBooleanValue().orElse(null), Boolean.class)
  93 + .set(5, tsKvEntry.getStrValue().orElse(null), String.class)
  94 + .set(6, tsKvEntry.getLongValue().orElse(null), Long.class)
  95 + .set(7, tsKvEntry.getDoubleValue().orElse(null), Double.class);
  96 + Optional<String> jsonV = tsKvEntry.getJsonValue();
  97 + if (jsonV.isPresent()) {
  98 + stmtBuilder.setString(8, tsKvEntry.getJsonValue().get());
  99 + } else {
  100 + stmtBuilder.setToNull(8);
  101 + }
  102 + BoundStatement stmt = stmtBuilder.build();
  103 +
  104 + return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null);
  105 + }
  106 +
  107 + @Override
  108 + public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  109 + ListenableFuture<TsKvEntry> latestEntryFuture = findLatest(tenantId, entityId, query.getKey());
  110 +
  111 + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestEntryFuture, latestEntry -> {
  112 + long ts = latestEntry.getTs();
  113 + if (ts > query.getStartTs() && ts <= query.getEndTs()) {
  114 + return true;
  115 + } else {
  116 + log.trace("Won't be deleted latest value for [{}], key - {}", entityId, query.getKey());
  117 + }
  118 + return false;
  119 + }, readResultsProcessingExecutor);
  120 +
  121 + ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  122 + if (isRemove) {
  123 + return deleteLatest(tenantId, entityId, query.getKey());
  124 + }
  125 + return Futures.immediateFuture(null);
  126 + }, readResultsProcessingExecutor);
  127 +
  128 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  129 + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {
  130 + @Override
  131 + public void onSuccess(@Nullable Void result) {
  132 + if (query.getRewriteLatestIfDeleted()) {
  133 + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  134 + if (isRemove) {
  135 + return getNewLatestEntryFuture(tenantId, entityId, query);
  136 + }
  137 + return Futures.immediateFuture(null);
  138 + }, readResultsProcessingExecutor);
  139 +
  140 + try {
  141 + resultFuture.set(savedLatestFuture.get());
  142 + } catch (InterruptedException | ExecutionException e) {
  143 + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);
  144 + }
  145 + } else {
  146 + resultFuture.set(null);
  147 + }
  148 + }
  149 +
  150 + @Override
  151 + public void onFailure(Throwable t) {
  152 + log.warn("[{}] Failed to process remove of the latest value", entityId, t);
  153 + }
  154 + }, MoreExecutors.directExecutor());
  155 + return resultFuture;
  156 + }
  157 +
  158 + private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  159 + long startTs = 0;
  160 + long endTs = query.getStartTs() - 1;
  161 + ReadTsKvQuery findNewLatestQuery = new BaseReadTsKvQuery(query.getKey(), startTs, endTs, endTs - startTs, 1,
  162 + Aggregation.NONE, DESC_ORDER);
  163 + ListenableFuture<List<TsKvEntry>> future = aggregationTimeseriesDao.findAllAsync(tenantId, entityId, findNewLatestQuery);
  164 +
  165 + return Futures.transformAsync(future, entryList -> {
  166 + if (entryList.size() == 1) {
  167 + return saveLatest(tenantId, entityId, entryList.get(0));
  168 + } else {
  169 + log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
  170 + }
  171 + return Futures.immediateFuture(null);
  172 + }, readResultsProcessingExecutor);
  173 + }
  174 +
  175 + private ListenableFuture<Void> deleteLatest(TenantId tenantId, EntityId entityId, String key) {
  176 + Statement delete = QueryBuilder.deleteFrom(ModelConstants.TS_KV_LATEST_CF)
  177 + .whereColumn(ModelConstants.ENTITY_TYPE_COLUMN).isEqualTo(literal(entityId.getEntityType().name()))
  178 + .whereColumn(ModelConstants.ENTITY_ID_COLUMN).isEqualTo(literal(entityId.getId()))
  179 + .whereColumn(ModelConstants.KEY_COLUMN).isEqualTo(literal(key)).build();
  180 + log.debug("Remove request: {}", delete.toString());
  181 + return getFuture(executeAsyncWrite(tenantId, delete), rs -> null);
  182 + }
  183 +
  184 + private ListenableFuture<List<TsKvEntry>> convertAsyncResultSetToTsKvEntryList(TbResultSet rs) {
  185 + return Futures.transform(rs.allRows(readResultsProcessingExecutor),
  186 + rows -> this.convertResultToTsKvEntryList(rows), readResultsProcessingExecutor);
  187 + }
  188 +
  189 + private TsKvEntry convertResultToTsKvEntry(String key, Row row) {
  190 + if (row != null) {
  191 + long ts = row.getLong(ModelConstants.TS_COLUMN);
  192 + return new BasicTsKvEntry(ts, toKvEntry(row, key));
  193 + } else {
  194 + return new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));
  195 + }
  196 + }
  197 +
  198 + private PreparedStatement getLatestStmt() {
  199 + if (latestInsertStmt == null) {
  200 + latestInsertStmt = prepare(INSERT_INTO + ModelConstants.TS_KV_LATEST_CF +
  201 + "(" + ModelConstants.ENTITY_TYPE_COLUMN +
  202 + "," + ModelConstants.ENTITY_ID_COLUMN +
  203 + "," + ModelConstants.KEY_COLUMN +
  204 + "," + ModelConstants.TS_COLUMN +
  205 + "," + ModelConstants.BOOLEAN_VALUE_COLUMN +
  206 + "," + ModelConstants.STRING_VALUE_COLUMN +
  207 + "," + ModelConstants.LONG_VALUE_COLUMN +
  208 + "," + ModelConstants.DOUBLE_VALUE_COLUMN +
  209 + "," + ModelConstants.JSON_VALUE_COLUMN + ")" +
  210 + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)");
  211 + }
  212 + return latestInsertStmt;
  213 + }
  214 +
  215 + private PreparedStatement getFindLatestStmt() {
  216 + if (findLatestStmt == null) {
  217 + findLatestStmt = prepare(SELECT_PREFIX +
  218 + ModelConstants.KEY_COLUMN + "," +
  219 + ModelConstants.TS_COLUMN + "," +
  220 + ModelConstants.STRING_VALUE_COLUMN + "," +
  221 + ModelConstants.BOOLEAN_VALUE_COLUMN + "," +
  222 + ModelConstants.LONG_VALUE_COLUMN + "," +
  223 + ModelConstants.DOUBLE_VALUE_COLUMN + "," +
  224 + ModelConstants.JSON_VALUE_COLUMN + " " +
  225 + "FROM " + ModelConstants.TS_KV_LATEST_CF + " " +
  226 + "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM +
  227 + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM +
  228 + "AND " + ModelConstants.KEY_COLUMN + EQUALS_PARAM);
  229 + }
  230 + return findLatestStmt;
  231 + }
  232 +
  233 + private PreparedStatement getFindAllLatestStmt() {
  234 + if (findAllLatestStmt == null) {
  235 + findAllLatestStmt = prepare(SELECT_PREFIX +
  236 + ModelConstants.KEY_COLUMN + "," +
  237 + ModelConstants.TS_COLUMN + "," +
  238 + ModelConstants.STRING_VALUE_COLUMN + "," +
  239 + ModelConstants.BOOLEAN_VALUE_COLUMN + "," +
  240 + ModelConstants.LONG_VALUE_COLUMN + "," +
  241 + ModelConstants.DOUBLE_VALUE_COLUMN + "," +
  242 + ModelConstants.JSON_VALUE_COLUMN + " " +
  243 + "FROM " + ModelConstants.TS_KV_LATEST_CF + " " +
  244 + "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM +
  245 + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM);
  246 + }
  247 + return findAllLatestStmt;
  248 + }
  249 +}
@@ -31,19 +31,11 @@ public interface TimeseriesDao { @@ -31,19 +31,11 @@ public interface TimeseriesDao {
31 31
32 ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries); 32 ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries);
33 33
34 - ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key);  
35 -  
36 - ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId);  
37 -  
38 ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl); 34 ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl);
39 35
40 ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl); 36 ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl);
41 37
42 - ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry);  
43 -  
44 ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query); 38 ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);
45 39
46 - ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);  
47 -  
48 ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query); 40 ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);
49 } 41 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.timeseries;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import org.thingsboard.server.common.data.id.EntityId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
  21 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  22 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  23 +
  24 +import java.util.List;
  25 +
  26 +public interface TimeseriesLatestDao {
  27 +
  28 + ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key);
  29 +
  30 + ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId);
  31 +
  32 + ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry);
  33 +
  34 + ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query);
  35 +
  36 +}
@@ -113,7 +113,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic @@ -113,7 +113,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
113 public User saveUser(User user) { 113 public User saveUser(User user) {
114 log.trace("Executing saveUser [{}]", user); 114 log.trace("Executing saveUser [{}]", user);
115 userValidator.validate(user, User::getTenantId); 115 userValidator.validate(user, User::getTenantId);
116 - if (user.getId() == null && !userLoginCaseSensitive) { 116 + if (!userLoginCaseSensitive) {
117 user.setEmail(user.getEmail().toLowerCase()); 117 user.setEmail(user.getEmail().toLowerCase());
118 } 118 }
119 User savedUser = userDao.save(user.getTenantId(), user); 119 User savedUser = userDao.save(user.getTenantId(), user);
  1 +--
  2 +-- Copyright © 2016-2020 The Thingsboard Authors
  3 +--
  4 +-- Licensed under the Apache License, Version 2.0 (the "License");
  5 +-- you may not use this file except in compliance with the License.
  6 +-- You may obtain a copy of the License at
  7 +--
  8 +-- http://www.apache.org/licenses/LICENSE-2.0
  9 +--
  10 +-- Unless required by applicable law or agreed to in writing, software
  11 +-- distributed under the License is distributed on an "AS IS" BASIS,
  12 +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +-- See the License for the specific language governing permissions and
  14 +-- limitations under the License.
  15 +--
  16 +
  17 +CREATE KEYSPACE IF NOT EXISTS thingsboard
  18 +WITH replication = {
  19 + 'class' : 'SimpleStrategy',
  20 + 'replication_factor' : 1
  21 +};
  22 +
  23 +CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_latest_cf (
  24 + entity_type text, // (DEVICE, CUSTOMER, TENANT)
  25 + entity_id timeuuid,
  26 + key text,
  27 + ts bigint,
  28 + bool_v boolean,
  29 + str_v text,
  30 + long_v bigint,
  31 + dbl_v double,
  32 + json_v text,
  33 + PRIMARY KEY (( entity_type, entity_id ), key)
  34 +) WITH compaction = { 'class' : 'LeveledCompactionStrategy' };
@@ -42,16 +42,3 @@ CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_partitions_cf ( @@ -42,16 +42,3 @@ CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_partitions_cf (
42 PRIMARY KEY (( entity_type, entity_id, key ), partition) 42 PRIMARY KEY (( entity_type, entity_id, key ), partition)
43 ) WITH CLUSTERING ORDER BY ( partition ASC ) 43 ) WITH CLUSTERING ORDER BY ( partition ASC )
44 AND compaction = { 'class' : 'LeveledCompactionStrategy' }; 44 AND compaction = { 'class' : 'LeveledCompactionStrategy' };
45 -  
46 -CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_latest_cf (  
47 - entity_type text, // (DEVICE, CUSTOMER, TENANT)  
48 - entity_id timeuuid,  
49 - key text,  
50 - ts bigint,  
51 - bool_v boolean,  
52 - str_v text,  
53 - long_v bigint,  
54 - dbl_v double,  
55 - json_v text,  
56 - PRIMARY KEY (( entity_type, entity_id ), key)  
57 -) WITH compaction = { 'class' : 'LeveledCompactionStrategy' };  
@@ -272,3 +272,20 @@ CREATE TABLE IF NOT EXISTS entity_view ( @@ -272,3 +272,20 @@ CREATE TABLE IF NOT EXISTS entity_view (
272 additional_info varchar 272 additional_info varchar
273 ); 273 );
274 274
  275 +CREATE TABLE IF NOT EXISTS ts_kv_latest (
  276 + entity_id uuid NOT NULL,
  277 + key int NOT NULL,
  278 + ts bigint NOT NULL,
  279 + bool_v boolean,
  280 + str_v varchar(10000000),
  281 + long_v bigint,
  282 + dbl_v double precision,
  283 + json_v varchar(10000000),
  284 + CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
  285 +);
  286 +
  287 +CREATE TABLE IF NOT EXISTS ts_kv_dictionary (
  288 + key varchar(255) NOT NULL,
  289 + key_id int GENERATED BY DEFAULT AS IDENTITY(start with 0 increment by 1) UNIQUE,
  290 + CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
  291 +);
@@ -14,6 +14,13 @@ @@ -14,6 +14,13 @@
14 -- limitations under the License. 14 -- limitations under the License.
15 -- 15 --
16 16
  17 +CREATE TABLE IF NOT EXISTS tb_schema_settings
  18 +(
  19 + schema_version bigint NOT NULL,
  20 + CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)
  21 +);
  22 +
  23 +INSERT INTO tb_schema_settings (schema_version) VALUES (3001000) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 3001000;
17 24
18 CREATE TABLE IF NOT EXISTS admin_settings ( 25 CREATE TABLE IF NOT EXISTS admin_settings (
19 id uuid NOT NULL CONSTRAINT admin_settings_pkey PRIMARY KEY, 26 id uuid NOT NULL CONSTRAINT admin_settings_pkey PRIMARY KEY,
@@ -331,3 +338,4 @@ BEGIN @@ -331,3 +338,4 @@ BEGIN
331 '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12); 338 '-' || substring(entity_id, 16, 4) || '-' || substring(entity_id, 20, 12);
332 END; 339 END;
333 $$ LANGUAGE plpgsql; 340 $$ LANGUAGE plpgsql;
  341 +
@@ -46,14 +46,6 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( @@ -46,14 +46,6 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest (
46 CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) 46 CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)
47 ); 47 );
48 48
49 -CREATE TABLE IF NOT EXISTS tb_schema_settings  
50 -(  
51 - schema_version bigint NOT NULL,  
52 - CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)  
53 -);  
54 -  
55 -INSERT INTO tb_schema_settings (schema_version) VALUES (2005001) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 2005001;  
56 -  
57 CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS 49 CREATE OR REPLACE FUNCTION to_uuid(IN entity_id varchar, OUT uuid_id uuid) AS
58 $$ 50 $$
59 BEGIN 51 BEGIN
@@ -28,18 +28,6 @@ CREATE TABLE IF NOT EXISTS ts_kv ( @@ -28,18 +28,6 @@ CREATE TABLE IF NOT EXISTS ts_kv (
28 CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts) 28 CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts)
29 ); 29 );
30 30
31 -CREATE TABLE IF NOT EXISTS ts_kv_latest (  
32 - entity_id uuid NOT NULL,  
33 - key int NOT NULL,  
34 - ts bigint NOT NULL,  
35 - bool_v boolean,  
36 - str_v varchar(10000000),  
37 - long_v bigint,  
38 - dbl_v double precision,  
39 - json_v varchar(10000000),  
40 - CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)  
41 -);  
42 -  
43 CREATE TABLE IF NOT EXISTS ts_kv_dictionary ( 31 CREATE TABLE IF NOT EXISTS ts_kv_dictionary (
44 key varchar(255) NOT NULL, 32 key varchar(255) NOT NULL,
45 key_id int GENERATED BY DEFAULT AS IDENTITY(start with 0 increment by 1) UNIQUE, 33 key_id int GENERATED BY DEFAULT AS IDENTITY(start with 0 increment by 1) UNIQUE,
@@ -27,19 +27,6 @@ CREATE TABLE IF NOT EXISTS ts_kv @@ -27,19 +27,6 @@ CREATE TABLE IF NOT EXISTS ts_kv
27 CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts) 27 CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts)
28 ) PARTITION BY RANGE (ts); 28 ) PARTITION BY RANGE (ts);
29 29
30 -CREATE TABLE IF NOT EXISTS ts_kv_latest  
31 -(  
32 - entity_id uuid NOT NULL,  
33 - key int NOT NULL,  
34 - ts bigint NOT NULL,  
35 - bool_v boolean,  
36 - str_v varchar(10000000),  
37 - long_v bigint,  
38 - dbl_v double precision,  
39 - json_v json,  
40 - CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key)  
41 -);  
42 -  
43 CREATE TABLE IF NOT EXISTS ts_kv_dictionary 30 CREATE TABLE IF NOT EXISTS ts_kv_dictionary
44 ( 31 (
45 key varchar(255) NOT NULL, 32 key varchar(255) NOT NULL,
@@ -47,14 +34,6 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary @@ -47,14 +34,6 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary
47 CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) 34 CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
48 ); 35 );
49 36
50 -CREATE TABLE IF NOT EXISTS tb_schema_settings  
51 -(  
52 - schema_version bigint NOT NULL,  
53 - CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)  
54 -);  
55 -  
56 -INSERT INTO tb_schema_settings (schema_version) VALUES (2005001) ON CONFLICT (schema_version) DO UPDATE SET schema_version = 2005001;  
57 -  
58 CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar, IN system_ttl bigint, INOUT deleted bigint) 37 CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar, IN system_ttl bigint, INOUT deleted bigint)
59 LANGUAGE plpgsql AS 38 LANGUAGE plpgsql AS
60 $$ 39 $$
@@ -22,7 +22,6 @@ import org.dbunit.ext.hsqldb.HsqldbDataTypeFactory; @@ -22,7 +22,6 @@ import org.dbunit.ext.hsqldb.HsqldbDataTypeFactory;
22 import org.springframework.beans.factory.annotation.Autowired; 22 import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.context.annotation.Bean; 23 import org.springframework.context.annotation.Bean;
24 import org.springframework.context.annotation.Configuration; 24 import org.springframework.context.annotation.Configuration;
25 -import org.thingsboard.server.dao.util.SqlDao;  
26 25
27 import javax.sql.DataSource; 26 import javax.sql.DataSource;
28 import java.io.IOException; 27 import java.io.IOException;
@@ -32,7 +31,6 @@ import java.sql.SQLException; @@ -32,7 +31,6 @@ import java.sql.SQLException;
32 * Created by Valerii Sosliuk on 5/6/2017. 31 * Created by Valerii Sosliuk on 5/6/2017.
33 */ 32 */
34 @Configuration 33 @Configuration
35 -@SqlDao  
36 public class JpaDbunitTestConfig { 34 public class JpaDbunitTestConfig {
37 35
38 @Autowired 36 @Autowired
@@ -40,7 +40,8 @@ public class NoSqlDaoServiceTestSuite { @@ -40,7 +40,8 @@ public class NoSqlDaoServiceTestSuite {
40 public static CustomCassandraCQLUnit cassandraUnit = 40 public static CustomCassandraCQLUnit cassandraUnit =
41 new CustomCassandraCQLUnit( 41 new CustomCassandraCQLUnit(
42 Arrays.asList( 42 Arrays.asList(
43 - new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false) 43 + new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
  44 + new ClassPathCQLDataSet("cassandra/schema-ts-latest.cql", false, false)
44 ), 45 ),
45 "cassandra-test.yaml", 30000L); 46 "cassandra-test.yaml", 30000L);
46 47
1 database.ts.type=cassandra 1 database.ts.type=cassandra
  2 +database.ts_latest.type=cassandra
2 3
3 sql.ts_inserts_executor_type=fixed 4 sql.ts_inserts_executor_type=fixed
4 sql.ts_inserts_fixed_thread_pool_size=10 5 sql.ts_inserts_fixed_thread_pool_size=10
1 database.ts.type=sql 1 database.ts.type=sql
  2 +database.ts_latest.type=sql
2 3
3 sql.ts_inserts_executor_type=fixed 4 sql.ts_inserts_executor_type=fixed
4 sql.ts_inserts_fixed_thread_pool_size=200 5 sql.ts_inserts_fixed_thread_pool_size=200
@@ -25,8 +25,10 @@ import org.thingsboard.rule.engine.api.TbContext; @@ -25,8 +25,10 @@ import org.thingsboard.rule.engine.api.TbContext;
25 import org.thingsboard.rule.engine.api.TbNodeConfiguration; 25 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
26 import org.thingsboard.rule.engine.api.TbNodeException; 26 import org.thingsboard.rule.engine.api.TbNodeException;
27 import org.thingsboard.rule.engine.api.util.TbNodeUtils; 27 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  28 +import org.thingsboard.server.common.data.EntityType;
28 import org.thingsboard.server.common.data.alarm.Alarm; 29 import org.thingsboard.server.common.data.alarm.Alarm;
29 import org.thingsboard.server.common.data.alarm.AlarmStatus; 30 import org.thingsboard.server.common.data.alarm.AlarmStatus;
  31 +import org.thingsboard.server.common.data.id.AlarmId;
30 import org.thingsboard.server.common.data.plugin.ComponentType; 32 import org.thingsboard.server.common.data.plugin.ComponentType;
31 import org.thingsboard.server.common.msg.TbMsg; 33 import org.thingsboard.server.common.msg.TbMsg;
32 34
@@ -56,8 +58,13 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig @@ -56,8 +58,13 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig
56 @Override 58 @Override
57 protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) { 59 protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) {
58 String alarmType = TbNodeUtils.processPattern(this.config.getAlarmType(), msg.getMetaData()); 60 String alarmType = TbNodeUtils.processPattern(this.config.getAlarmType(), msg.getMetaData());
59 - ListenableFuture<Alarm> latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType);  
60 - return Futures.transformAsync(latest, a -> { 61 + ListenableFuture<Alarm> alarmFuture;
  62 + if(msg.getOriginator().getEntityType().equals(EntityType.ALARM)){
  63 + alarmFuture = ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), new AlarmId(msg.getOriginator().getId()));
  64 + } else {
  65 + alarmFuture = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType);
  66 + }
  67 + return Futures.transformAsync(alarmFuture, a -> {
61 if (a != null && !a.getStatus().isCleared()) { 68 if (a != null && !a.getStatus().isCleared()) {
62 return clearAlarm(ctx, msg, a); 69 return clearAlarm(ctx, msg, a);
63 } 70 }
@@ -16,8 +16,13 @@ @@ -16,8 +16,13 @@
16 package org.thingsboard.rule.engine.filter; 16 package org.thingsboard.rule.engine.filter;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
  20 +import org.thingsboard.rule.engine.api.RuleNode;
  21 +import org.thingsboard.rule.engine.api.TbContext;
  22 +import org.thingsboard.rule.engine.api.TbNode;
  23 +import org.thingsboard.rule.engine.api.TbNodeConfiguration;
  24 +import org.thingsboard.rule.engine.api.TbNodeException;
19 import org.thingsboard.rule.engine.api.util.TbNodeUtils; 25 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
20 -import org.thingsboard.rule.engine.api.*;  
21 import org.thingsboard.server.common.data.DataConstants; 26 import org.thingsboard.server.common.data.DataConstants;
22 import org.thingsboard.server.common.data.plugin.ComponentType; 27 import org.thingsboard.server.common.data.plugin.ComponentType;
23 import org.thingsboard.server.common.msg.TbMsg; 28 import org.thingsboard.server.common.msg.TbMsg;
@@ -30,7 +35,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -30,7 +35,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType;
30 configClazz = EmptyNodeConfiguration.class, 35 configClazz = EmptyNodeConfiguration.class,
31 relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event", 36 relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event",
32 "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned", 37 "Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned",
33 - "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other"}, 38 + "Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other", "Entity Assigned From Tenant", "Entity Assigned To Tenant"},
34 nodeDescription = "Route incoming messages by Message Type", 39 nodeDescription = "Route incoming messages by Message Type",
35 nodeDetails = "Sends messages with message types <b>\"Post attributes\", \"Post telemetry\", \"RPC Request\"</b> etc. via corresponding chain, otherwise <b>Other</b> chain is used.", 40 nodeDetails = "Sends messages with message types <b>\"Post attributes\", \"Post telemetry\", \"RPC Request\"</b> etc. via corresponding chain, otherwise <b>Other</b> chain is used.",
36 uiResources = {"static/rulenode/rulenode-core-config.js"}, 41 uiResources = {"static/rulenode/rulenode-core-config.js"},
@@ -81,6 +86,10 @@ public class TbMsgTypeSwitchNode implements TbNode { @@ -81,6 +86,10 @@ public class TbMsgTypeSwitchNode implements TbNode {
81 relationType = "Alarm Cleared"; 86 relationType = "Alarm Cleared";
82 } else if (msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE)) { 87 } else if (msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE)) {
83 relationType = "RPC Request to Device"; 88 relationType = "RPC Request to Device";
  89 + } else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED_FROM_TENANT)) {
  90 + relationType = "Entity Assigned From Tenant";
  91 + } else if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED_TO_TENANT)) {
  92 + relationType = "Entity Assigned To Tenant";
84 } else { 93 } else {
85 relationType = "Other"; 94 relationType = "Other";
86 } 95 }
@@ -74,6 +74,7 @@ export interface EntityDataSubscriptionOptions { @@ -74,6 +74,7 @@ export interface EntityDataSubscriptionOptions {
74 74
75 export class EntityDataSubscription { 75 export class EntityDataSubscription {
76 76
  77 + private entityDataSubscriptionOptions = this.listener.subscriptionOptions;
77 private datasourceType: DatasourceType = this.entityDataSubscriptionOptions.datasourceType; 78 private datasourceType: DatasourceType = this.entityDataSubscriptionOptions.datasourceType;
78 private history: boolean; 79 private history: boolean;
79 private realtime: boolean; 80 private realtime: boolean;
@@ -103,8 +104,7 @@ export class EntityDataSubscription { @@ -103,8 +104,7 @@ export class EntityDataSubscription {
103 private dataResolved = false; 104 private dataResolved = false;
104 private started = false; 105 private started = false;
105 106
106 - constructor(public entityDataSubscriptionOptions: EntityDataSubscriptionOptions,  
107 - private listener: EntityDataListener, 107 + constructor(private listener: EntityDataListener,
108 private telemetryService: TelemetryService, 108 private telemetryService: TelemetryService,
109 private utils: UtilsService) { 109 private utils: UtilsService) {
110 this.initializeSubscription(); 110 this.initializeSubscription();
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 import { DataSetHolder, Datasource, DatasourceType, widgetType } from '@shared/models/widget.models'; 17 import { DataSetHolder, Datasource, DatasourceType, widgetType } from '@shared/models/widget.models';
18 import { SubscriptionTimewindow } from '@shared/models/time/time.models'; 18 import { SubscriptionTimewindow } from '@shared/models/time/time.models';
19 import { EntityData, EntityDataPageLink, KeyFilter } from '@shared/models/query/query.models'; 19 import { EntityData, EntityDataPageLink, KeyFilter } from '@shared/models/query/query.models';
20 -import { PageData } from '@shared/models/page/page-data'; 20 +import { emptyPageData, PageData } from '@shared/models/page/page-data';
21 import { Injectable } from '@angular/core'; 21 import { Injectable } from '@angular/core';
22 import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service'; 22 import { TelemetryWebsocketService } from '@core/ws/telemetry-websocket.service';
23 import { UtilsService } from '@core/services/utils.service'; 23 import { UtilsService } from '@core/services/utils.service';
@@ -41,6 +41,7 @@ export interface EntityDataListener { @@ -41,6 +41,7 @@ export interface EntityDataListener {
41 initialPageDataChanged?: (nextPageData: PageData<EntityData>) => void; 41 initialPageDataChanged?: (nextPageData: PageData<EntityData>) => void;
42 updateRealtimeSubscription?: () => SubscriptionTimewindow; 42 updateRealtimeSubscription?: () => SubscriptionTimewindow;
43 setRealtimeSubscription?: (subscriptionTimewindow: SubscriptionTimewindow) => void; 43 setRealtimeSubscription?: (subscriptionTimewindow: SubscriptionTimewindow) => void;
  44 + subscriptionOptions?: EntityDataSubscriptionOptions;
44 subscription?: EntityDataSubscription; 45 subscription?: EntityDataSubscription;
45 } 46 }
46 47
@@ -61,19 +62,24 @@ export class EntityDataService { @@ -61,19 +62,24 @@ export class EntityDataService {
61 62
62 public prepareSubscription(listener: EntityDataListener): Observable<EntityDataLoadResult> { 63 public prepareSubscription(listener: EntityDataListener): Observable<EntityDataLoadResult> {
63 const datasource = listener.configDatasource; 64 const datasource = listener.configDatasource;
  65 + listener.subscriptionOptions = this.createSubscriptionOptions(
  66 + datasource,
  67 + listener.subscriptionType,
  68 + datasource.pageLink,
  69 + datasource.keyFilters,
  70 + null,
  71 + false);
64 if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !datasource.pageLink)) { 72 if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !datasource.pageLink)) {
65 return of(null); 73 return of(null);
66 } 74 }
67 - listener.subscription = this.createSubscription(listener,  
68 - datasource.pageLink, datasource.keyFilters, null,  
69 - false); 75 + listener.subscription = new EntityDataSubscription(listener, this.telemetryService, this.utils);
70 return listener.subscription.subscribe(); 76 return listener.subscription.subscribe();
71 } 77 }
72 78
73 public startSubscription(listener: EntityDataListener) { 79 public startSubscription(listener: EntityDataListener) {
74 if (listener.subscription) { 80 if (listener.subscription) {
75 if (listener.subscriptionType === widgetType.timeseries) { 81 if (listener.subscriptionType === widgetType.timeseries) {
76 - listener.subscription.entityDataSubscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow); 82 + listener.subscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow);
77 } 83 }
78 listener.subscription.start(); 84 listener.subscription.start();
79 } 85 }
@@ -83,13 +89,21 @@ export class EntityDataService { @@ -83,13 +89,21 @@ export class EntityDataService {
83 pageLink: EntityDataPageLink, 89 pageLink: EntityDataPageLink,
84 keyFilters: KeyFilter[]): Observable<EntityDataLoadResult> { 90 keyFilters: KeyFilter[]): Observable<EntityDataLoadResult> {
85 const datasource = listener.configDatasource; 91 const datasource = listener.configDatasource;
  92 + listener.subscriptionOptions = this.createSubscriptionOptions(
  93 + datasource,
  94 + listener.subscriptionType,
  95 + pageLink,
  96 + datasource.keyFilters,
  97 + keyFilters,
  98 + true);
86 if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !pageLink)) { 99 if (datasource.type === DatasourceType.entity && (!datasource.entityFilter || !pageLink)) {
  100 + listener.dataLoaded(emptyPageData<EntityData>(), [],
  101 + listener.configDatasourceIndex, listener.subscriptionOptions.pageLink);
87 return of(null); 102 return of(null);
88 } 103 }
89 - listener.subscription = this.createSubscription(listener,  
90 - pageLink, datasource.keyFilters, keyFilters,true); 104 + listener.subscription = new EntityDataSubscription(listener, this.telemetryService, this.utils);
91 if (listener.subscriptionType === widgetType.timeseries) { 105 if (listener.subscriptionType === widgetType.timeseries) {
92 - listener.subscription.entityDataSubscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow); 106 + listener.subscriptionOptions.subscriptionTimewindow = deepClone(listener.subscriptionTimewindow);
93 } 107 }
94 return listener.subscription.subscribe(); 108 return listener.subscription.subscribe();
95 } 109 }
@@ -100,12 +114,12 @@ export class EntityDataService { @@ -100,12 +114,12 @@ export class EntityDataService {
100 } 114 }
101 } 115 }
102 116
103 - private createSubscription(listener: EntityDataListener,  
104 - pageLink: EntityDataPageLink,  
105 - keyFilters: KeyFilter[],  
106 - additionalKeyFilters: KeyFilter[],  
107 - isPaginatedDataSubscription: boolean): EntityDataSubscription {  
108 - const datasource = listener.configDatasource; 117 + private createSubscriptionOptions(datasource: Datasource,
  118 + subscriptionType: widgetType,
  119 + pageLink: EntityDataPageLink,
  120 + keyFilters: KeyFilter[],
  121 + additionalKeyFilters: KeyFilter[],
  122 + isPaginatedDataSubscription: boolean): EntityDataSubscriptionOptions {
109 const subscriptionDataKeys: Array<SubscriptionDataKey> = []; 123 const subscriptionDataKeys: Array<SubscriptionDataKey> = [];
110 datasource.dataKeys.forEach((dataKey) => { 124 datasource.dataKeys.forEach((dataKey) => {
111 const subscriptionDataKey: SubscriptionDataKey = { 125 const subscriptionDataKey: SubscriptionDataKey = {
@@ -119,7 +133,7 @@ export class EntityDataService { @@ -119,7 +133,7 @@ export class EntityDataService {
119 const entityDataSubscriptionOptions: EntityDataSubscriptionOptions = { 133 const entityDataSubscriptionOptions: EntityDataSubscriptionOptions = {
120 datasourceType: datasource.type, 134 datasourceType: datasource.type,
121 dataKeys: subscriptionDataKeys, 135 dataKeys: subscriptionDataKeys,
122 - type: listener.subscriptionType 136 + type: subscriptionType
123 }; 137 };
124 if (entityDataSubscriptionOptions.datasourceType === DatasourceType.entity) { 138 if (entityDataSubscriptionOptions.datasourceType === DatasourceType.entity) {
125 entityDataSubscriptionOptions.entityFilter = datasource.entityFilter; 139 entityDataSubscriptionOptions.entityFilter = datasource.entityFilter;
@@ -128,8 +142,6 @@ export class EntityDataService { @@ -128,8 +142,6 @@ export class EntityDataService {
128 entityDataSubscriptionOptions.additionalKeyFilters = additionalKeyFilters; 142 entityDataSubscriptionOptions.additionalKeyFilters = additionalKeyFilters;
129 } 143 }
130 entityDataSubscriptionOptions.isPaginatedDataSubscription = isPaginatedDataSubscription; 144 entityDataSubscriptionOptions.isPaginatedDataSubscription = isPaginatedDataSubscription;
131 - return new EntityDataSubscription(entityDataSubscriptionOptions,  
132 - listener, this.telemetryService, this.utils); 145 + return entityDataSubscriptionOptions;
133 } 146 }
134 -  
135 } 147 }
@@ -735,8 +735,12 @@ export class WidgetSubscription implements IWidgetSubscription { @@ -735,8 +735,12 @@ export class WidgetSubscription implements IWidgetSubscription {
735 if (this.type === widgetType.alarm) { 735 if (this.type === widgetType.alarm) {
736 this.updateAlarmDataSubscription(); 736 this.updateAlarmDataSubscription();
737 } else { 737 } else {
738 - this.notifyDataLoading();  
739 - this.dataSubscribe(); 738 + if (this.hasDataPageLink) {
  739 + this.updateDataSubscriptions();
  740 + } else {
  741 + this.notifyDataLoading();
  742 + this.dataSubscribe();
  743 + }
740 } 744 }
741 } 745 }
742 } 746 }
@@ -1017,8 +1021,8 @@ export class WidgetSubscription implements IWidgetSubscription { @@ -1017,8 +1021,8 @@ export class WidgetSubscription implements IWidgetSubscription {
1017 for (let datasourceIndex = 0; datasourceIndex < this.entityDataListeners.length; datasourceIndex++) { 1021 for (let datasourceIndex = 0; datasourceIndex < this.entityDataListeners.length; datasourceIndex++) {
1018 const entityDataListener = this.entityDataListeners[datasourceIndex]; 1022 const entityDataListener = this.entityDataListeners[datasourceIndex];
1019 if (entityDataListener) { 1023 if (entityDataListener) {
1020 - const pageLink = entityDataListener.subscription.entityDataSubscriptionOptions.pageLink;  
1021 - const keyFilters = entityDataListener.subscription.entityDataSubscriptionOptions.additionalKeyFilters; 1024 + const pageLink = entityDataListener.subscriptionOptions.pageLink;
  1025 + const keyFilters = entityDataListener.subscriptionOptions.additionalKeyFilters;
1022 this.subscribeForPaginatedData(datasourceIndex, pageLink, keyFilters); 1026 this.subscribeForPaginatedData(datasourceIndex, pageLink, keyFilters);
1023 } 1027 }
1024 } 1028 }
@@ -376,6 +376,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -376,6 +376,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
376 } 376 }
377 this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings); 377 this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings);
378 this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx'); 378 this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx');
  379 + this.contentsInfo[dataKey.def].units = dataKey.units;
  380 + this.contentsInfo[dataKey.def].decimals = dataKey.decimals;
379 this.columnWidth[dataKey.def] = getColumnWidth(keySettings); 381 this.columnWidth[dataKey.def] = getColumnWidth(keySettings);
380 this.columns.push(dataKey); 382 this.columns.push(dataKey);
381 383
@@ -932,7 +934,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> { @@ -932,7 +934,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> {
932 } 934 }
933 } 935 }
934 } 936 }
935 - alarm[dataKey.label] = value; 937 + alarm[dataKey.name] = value;
936 }); 938 });
937 return alarm; 939 return alarm;
938 } 940 }
@@ -87,7 +87,6 @@ import { @@ -87,7 +87,6 @@ import {
87 import { sortItems } from '@shared/models/page/page-link'; 87 import { sortItems } from '@shared/models/page/page-link';
88 import { entityFields } from '@shared/models/entity.models'; 88 import { entityFields } from '@shared/models/entity.models';
89 import { DatePipe } from '@angular/common'; 89 import { DatePipe } from '@angular/common';
90 -import { alarmFields } from '@shared/models/alarm.models';  
91 90
92 interface EntitiesTableWidgetSettings extends TableWidgetSettings { 91 interface EntitiesTableWidgetSettings extends TableWidgetSettings {
93 entitiesTitle: string; 92 entitiesTitle: string;
@@ -659,7 +658,9 @@ class EntityDatasource implements DataSource<EntityData> { @@ -659,7 +658,9 @@ class EntityDatasource implements DataSource<EntityData> {
659 this.dataKeys.forEach((dataKey, index) => { 658 this.dataKeys.forEach((dataKey, index) => {
660 const keyData = data[index].data; 659 const keyData = data[index].data;
661 if (keyData && keyData.length && keyData[0].length > 1) { 660 if (keyData && keyData.length && keyData[0].length > 1) {
662 - entity[dataKey.label] = keyData[0][1]; 661 + if (data[index].dataKey.type !== DataKeyType.entityField || !entity.hasOwnProperty(dataKey.label)) {
  662 + entity[dataKey.label] = keyData[0][1];
  663 + }
663 } else { 664 } else {
664 entity[dataKey.label] = ''; 665 entity[dataKey.label] = '';
665 } 666 }
@@ -14,8 +14,6 @@ @@ -14,8 +14,6 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import { DEFAULT_MAP_PAGE_SIZE } from '@home/components/widget/lib/maps/map-models';  
18 -  
19 export const googleMapSettingsSchema = 17 export const googleMapSettingsSchema =
20 { 18 {
21 schema: { 19 schema: {
@@ -247,7 +245,7 @@ export const commonMapSettingsSchema = @@ -247,7 +245,7 @@ export const commonMapSettingsSchema =
247 mapPageSize: { 245 mapPageSize: {
248 title: 'Map page size load entities', 246 title: 'Map page size load entities',
249 type: 'number', 247 type: 'number',
250 - default: DEFAULT_MAP_PAGE_SIZE 248 + default: 16384
251 }, 249 },
252 defaultCenterPosition: { 250 defaultCenterPosition: {
253 title: 'Default map center position (0,0)', 251 title: 'Default map center position (0,0)',
@@ -189,7 +189,7 @@ export function getAlarmValue(alarm: AlarmDataInfo, key: EntityColumn) { @@ -189,7 +189,7 @@ export function getAlarmValue(alarm: AlarmDataInfo, key: EntityColumn) {
189 if (alarmField) { 189 if (alarmField) {
190 return getDescendantProp(alarm, alarmField.value); 190 return getDescendantProp(alarm, alarmField.value);
191 } else { 191 } else {
192 - return getDescendantProp(alarm, key.label); 192 + return getDescendantProp(alarm, key.name);
193 } 193 }
194 } 194 }
195 195
@@ -49,7 +49,9 @@ export enum ActionType { @@ -49,7 +49,9 @@ export enum ActionType {
49 ALARM_CLEAR = 'ALARM_CLEAR', 49 ALARM_CLEAR = 'ALARM_CLEAR',
50 LOGIN = 'LOGIN', 50 LOGIN = 'LOGIN',
51 LOGOUT = 'LOGOUT', 51 LOGOUT = 'LOGOUT',
52 - LOCKOUT = 'LOCKOUT' 52 + LOCKOUT = 'LOCKOUT',
  53 + ASSIGNED_FROM_TENANT = 'ASSIGNED_FROM_TENANT',
  54 + ASSIGNED_TO_TENANT = 'ASSIGNED_TO_TENANT'
53 } 55 }
54 56
55 export enum ActionStatus { 57 export enum ActionStatus {
@@ -79,7 +81,9 @@ export const actionTypeTranslations = new Map<ActionType, string>( @@ -79,7 +81,9 @@ export const actionTypeTranslations = new Map<ActionType, string>(
79 [ActionType.ALARM_CLEAR, 'audit-log.type-alarm-clear'], 81 [ActionType.ALARM_CLEAR, 'audit-log.type-alarm-clear'],
80 [ActionType.LOGIN, 'audit-log.type-login'], 82 [ActionType.LOGIN, 'audit-log.type-login'],
81 [ActionType.LOGOUT, 'audit-log.type-logout'], 83 [ActionType.LOGOUT, 'audit-log.type-logout'],
82 - [ActionType.LOCKOUT, 'audit-log.type-lockout'] 84 + [ActionType.LOCKOUT, 'audit-log.type-lockout'],
  85 + [ActionType.ASSIGNED_FROM_TENANT, 'audit-log.type-assigned-from-tenant'],
  86 + [ActionType.ASSIGNED_TO_TENANT, 'audit-log.type-assigned-to-tenant']
83 ] 87 ]
84 ); 88 );
85 89
@@ -100,6 +100,7 @@ export const HelpLinks = { @@ -100,6 +100,7 @@ export const HelpLinks = {
100 ruleNodeAwsSqs: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#aws-sqs-node', 100 ruleNodeAwsSqs: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#aws-sqs-node',
101 ruleNodeKafka: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#kafka-node', 101 ruleNodeKafka: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#kafka-node',
102 ruleNodeMqtt: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#mqtt-node', 102 ruleNodeMqtt: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#mqtt-node',
  103 + ruleNodeAzureIotHub: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#azure-iot-hub-node',
103 ruleNodeRabbitMq: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#rabbitmq-node', 104 ruleNodeRabbitMq: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#rabbitmq-node',
104 ruleNodeRestApiCall: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#rest-api-call-node', 105 ruleNodeRestApiCall: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#rest-api-call-node',
105 ruleNodeSendEmail: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#send-email-node', 106 ruleNodeSendEmail: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/external-nodes/#send-email-node',
@@ -412,6 +412,7 @@ const ruleNodeClazzHelpLinkMap = { @@ -412,6 +412,7 @@ const ruleNodeClazzHelpLinkMap = {
412 'org.thingsboard.rule.engine.aws.sqs.TbSqsNode': 'ruleNodeAwsSqs', 412 'org.thingsboard.rule.engine.aws.sqs.TbSqsNode': 'ruleNodeAwsSqs',
413 'org.thingsboard.rule.engine.kafka.TbKafkaNode': 'ruleNodeKafka', 413 'org.thingsboard.rule.engine.kafka.TbKafkaNode': 'ruleNodeKafka',
414 'org.thingsboard.rule.engine.mqtt.TbMqttNode': 'ruleNodeMqtt', 414 'org.thingsboard.rule.engine.mqtt.TbMqttNode': 'ruleNodeMqtt',
  415 + 'org.thingsboard.rule.engine.mqtt.azure.TbAzureIotHubNode': 'ruleNodeAzureIotHub',
415 'org.thingsboard.rule.engine.rabbitmq.TbRabbitMqNode': 'ruleNodeRabbitMq', 416 'org.thingsboard.rule.engine.rabbitmq.TbRabbitMqNode': 'ruleNodeRabbitMq',
416 'org.thingsboard.rule.engine.rest.TbRestApiCallNode': 'ruleNodeRestApiCall', 417 'org.thingsboard.rule.engine.rest.TbRestApiCallNode': 'ruleNodeRestApiCall',
417 'org.thingsboard.rule.engine.mail.TbSendEmailNode': 'ruleNodeSendEmail' 418 'org.thingsboard.rule.engine.mail.TbSendEmailNode': 'ruleNodeSendEmail'
@@ -371,7 +371,9 @@ @@ -371,7 +371,9 @@
371 "action-data": "Action data", 371 "action-data": "Action data",
372 "failure-details": "Failure details", 372 "failure-details": "Failure details",
373 "search": "Search audit logs", 373 "search": "Search audit logs",
374 - "clear-search": "Clear search" 374 + "clear-search": "Clear search",
  375 + "type-assigned-from-tenant": "Assigned from Tenant",
  376 + "type-assigned-to-tenant": "Assigned to Tenant"
375 }, 377 },
376 "confirm-on-exit": { 378 "confirm-on-exit": {
377 "message": "You have unsaved changes. Are you sure you want to leave this page?", 379 "message": "You have unsaved changes. Are you sure you want to leave this page?",