Browse Source

device swap feature init

pull/3213/head
Dima Landiak 6 years ago
committed by Andrew Shvayka
parent
commit
ac007bea24
  1. 28
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  2. 56
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  3. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java
  4. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  5. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java
  6. 3
      common/dao-api/src/main/java/org/thingsboard/server/dao/relation/RelationService.java
  7. 2
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  8. 4
      common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java
  9. 3
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java
  10. 39
      dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java
  11. 5
      dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java
  12. 10
      dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java
  13. 17
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  14. 49
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  15. 19
      dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java
  16. 16
      dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationService.java
  17. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
  18. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
  19. 13
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java
  20. 6
      ui/src/app/common/types.constant.js
  21. 4
      ui/src/app/locale/locale.constant-en_US.json

28
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
@ -26,7 +27,6 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
@ -39,12 +39,12 @@ import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DashboardId;
@ -595,6 +595,12 @@ public abstract class BaseController {
case ALARM_CLEAR:
msgType = DataConstants.ALARM_CLEAR;
break;
case SWAPPED_FROM_TENANT:
msgType = DataConstants.ENTITY_SWAPPED_FROM;
break;
case SWAPPED_TO_TENANT:
msgType = DataConstants.ENTITY_SWAPPED_TO;
break;
}
if (!StringUtils.isEmpty(msgType)) {
try {
@ -614,6 +620,16 @@ public abstract class BaseController {
String strCustomerName = extractParameter(String.class, 2, additionalInfo);
metaData.putValue("unassignedCustomerId", strCustomerId);
metaData.putValue("unassignedCustomerName", strCustomerName);
} else if (actionType == ActionType.SWAPPED_FROM_TENANT) {
String strTenantId = extractParameter(String.class, 0, additionalInfo);
String strTenantName = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("swappedFromTenantId", strTenantId);
metaData.putValue("swappedFromTenantName", strTenantName);
} else if (actionType == ActionType.SWAPPED_TO_TENANT) {
String strTenantId = extractParameter(String.class, 0, additionalInfo);
String strTenantName = extractParameter(String.class, 1, additionalInfo);
metaData.putValue("swappedToTenantId", strTenantId);
metaData.putValue("swappedToTenantName", strTenantName);
}
ObjectNode entityNode;
if (entity != null) {
@ -677,5 +693,13 @@ public abstract class BaseController {
return result;
}
protected <E extends HasName> String entityToStr(E entity) {
try {
return json.writeValueAsString(json.valueToTree(entity));
} catch (JsonProcessingException e) {
log.warn("[{}] Failed to convert entity to string!", entity, e);
}
return null;
}
}

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

@ -39,8 +39,10 @@ import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
@ -48,6 +50,9 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TextPageData;
import org.thingsboard.server.common.data.page.TextPageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.device.claim.ClaimResponse;
import org.thingsboard.server.dao.device.claim.ClaimResult;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
@ -70,6 +75,7 @@ public class DeviceController extends BaseController {
private static final String DEVICE_ID = "deviceId";
private static final String DEVICE_NAME = "deviceName";
private static final String TENANT_ID = "tenantId";
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET)
@ -481,4 +487,54 @@ public class DeviceController extends BaseController {
}
return DataConstants.DEFAULT_SECRET_KEY;
}
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/{tenantId}/device/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public Device swapDevice(@PathVariable(TENANT_ID) String strTenantId,
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(TENANT_ID, strTenantId);
checkParameter(DEVICE_ID, strDeviceId);
try {
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
Device device = checkDeviceId(deviceId, Operation.WRITE);
TenantId newTenantId = new TenantId(toUUID(strTenantId));
Tenant newTenant = tenantService.findTenantById(newTenantId);
if (newTenant == null) {
throw new ThingsboardException("Could not find the specified Tenant!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
Device swappedDevice = deviceService.swapDevice(newTenantId, device);
logEntityAction(getCurrentUser(), deviceId, swappedDevice,
swappedDevice.getCustomerId(),
ActionType.SWAPPED_TO_TENANT, null, strTenantId, newTenant.getName());
Tenant currentTenant = tenantService.findTenantById(getTenantId());
pushSwappedFromNotification(currentTenant, newTenantId, swappedDevice);
return swappedDevice;
} catch (Exception e) {
logEntityAction(getCurrentUser(), emptyId(EntityType.DEVICE), null,
null,
ActionType.SWAPPED_TO_TENANT, e, strTenantId);
throw handleException(e);
}
}
private void pushSwappedFromNotification(Tenant currentTenant, TenantId newTenantId, Device swappedDevice) {
String data = entityToStr(swappedDevice);
if (data != null) {
TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_SWAPPED_FROM, swappedDevice.getId(), getMetaDataForSwappedFrom(currentTenant), TbMsgDataType.JSON, data);
tbClusterService.pushMsgToRuleEngine(newTenantId, swappedDevice.getId(), tbMsg, null);
}
}
private TbMsgMetaData getMetaDataForSwappedFrom(Tenant tenant) {
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("swappedFromTenantId", tenant.getId().getId().toString());
metaData.putValue("swappedFromTenantName", tenant.getName());
return metaData;
}
}

3
common/dao-api/src/main/java/org/thingsboard/server/dao/audit/AuditLogService.java

@ -16,14 +16,12 @@
package org.thingsboard.server.dao.audit;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.audit.AuditLog;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
@ -50,4 +48,5 @@ public interface AuditLogService {
ActionType actionType,
Exception e, Object... additionalInfo);
void removeAuditLogs(TenantId tenantId, EntityId entityId);
}

4
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -28,7 +28,7 @@ import org.thingsboard.server.common.data.page.TextPageLink;
import java.util.List;
public interface DeviceService {
Device findDeviceById(TenantId tenantId, DeviceId deviceId);
ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId);
@ -65,4 +65,6 @@ public interface DeviceService {
ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId);
Device swapDevice(TenantId tenantId, Device device);
}

2
common/dao-api/src/main/java/org/thingsboard/server/dao/event/EventService.java

@ -41,4 +41,6 @@ public interface EventService {
List<Event> findLatestEvents(TenantId tenantId, EntityId entityId, String eventType, int limit);
void removeEvents(TenantId tenantId, EntityId entityId);
}

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

@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
* Created by ashvayka on 27.04.17.
@ -77,6 +76,8 @@ public interface RelationService {
ListenableFuture<List<EntityRelationInfo>> findInfoByQuery(TenantId tenantId, EntityRelationsQuery query);
void removeRelations(TenantId tenantId, EntityId entityId);
// TODO: This method may be useful for some validations in the future
// ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to);

2
common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java

@ -57,6 +57,8 @@ public class DataConstants {
public static final String ATTRIBUTES_DELETED = "ATTRIBUTES_DELETED";
public static final String ALARM_ACK = "ALARM_ACK";
public static final String ALARM_CLEAR = "ALARM_CLEAR";
public static final String ENTITY_SWAPPED_FROM = "ENTITY_SWAPPED_FROM";
public static final String ENTITY_SWAPPED_TO = "ENTITY_SWAPPED_TO";
public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE";

4
common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java

@ -40,7 +40,9 @@ public enum ActionType {
ALARM_CLEAR(false),
LOGIN(false),
LOGOUT(false),
LOCKOUT(false);
LOCKOUT(false),
SWAPPED_FROM_TENANT(false),
SWAPPED_TO_TENANT(false);
private final boolean isRead;

3
dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogDao.java

@ -22,11 +22,12 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.Dao;
import java.util.List;
import java.util.UUID;
public interface AuditLogDao {
public interface AuditLogDao extends Dao<AuditLog> {
ListenableFuture<Void> saveByTenantId(AuditLog auditLog);

39
dao/src/main/java/org/thingsboard/server/dao/audit/AuditLogServiceImpl.java

@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.audit.ActionStatus;
@ -38,7 +37,6 @@ import org.thingsboard.server.common.data.id.AuditLogId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.page.TimePageData;
@ -53,6 +51,8 @@ import org.thingsboard.server.dao.service.DataValidator;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.thingsboard.server.dao.service.Validator.validateEntityId;
@ -117,8 +117,8 @@ public class AuditLogServiceImpl implements AuditLogService {
@Override
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) {
logEntityAction(TenantId tenantId, CustomerId customerId, UserId userId, String userName, I entityId, E entity,
ActionType actionType, Exception e, Object... additionalInfo) {
if (canLog(entityId.getEntityType(), actionType)) {
JsonNode actionData = constructActionData(entityId, entity, actionType, additionalInfo);
ActionStatus actionStatus = ActionStatus.SUCCESS;
@ -129,7 +129,8 @@ public class AuditLogServiceImpl implements AuditLogService {
} else {
try {
entityName = entityService.fetchEntityNameAsync(tenantId, entityId).get();
} catch (Exception ex) {}
} catch (Exception ex) {
}
}
if (e != null) {
actionStatus = ActionStatus.FAILURE;
@ -157,16 +158,36 @@ public class AuditLogServiceImpl implements AuditLogService {
}
}
@Override
public void removeAuditLogs(TenantId tenantId, EntityId entityId) {
List<AuditLog> auditLogs = new ArrayList<>();
TimePageData<AuditLog> auditLogPageData;
TimePageLink auditLogPageLink = new TimePageLink(1000);
do {
auditLogPageData = findAuditLogsByTenantIdAndEntityId(tenantId, entityId,
new ArrayList<>(Arrays.asList(ActionType.values())), auditLogPageLink);
auditLogs.addAll(auditLogPageData.getData());
if (auditLogPageData.hasNext()) {
auditLogPageLink = auditLogPageData.getNextPageLink();
}
} while (auditLogPageData.hasNext());
for (AuditLog auditLog : auditLogs) {
auditLogDao.removeById(tenantId, auditLog.getUuidId());
}
}
private <E extends HasName, I extends EntityId> JsonNode constructActionData(I entityId, E entity,
ActionType actionType,
Object... additionalInfo) {
ActionType actionType,
Object... additionalInfo) {
ObjectNode actionData = objectMapper.createObjectNode();
switch(actionType) {
switch (actionType) {
case ADDED:
case UPDATED:
case ALARM_ACK:
case ALARM_CLEAR:
case RELATIONS_DELETED:
case SWAPPED_TO_TENANT:
if (entity != null) {
ObjectNode entityNode = objectMapper.valueToTree(entity);
if (entityId.getEntityType() == EntityType.DASHBOARD) {
@ -208,7 +229,7 @@ public class AuditLogServiceImpl implements AuditLogService {
scope = extractParameter(String.class, 0, additionalInfo);
actionData.put("scope", scope);
List<String> keys = extractParameter(List.class, 1, additionalInfo);
ArrayNode attrsArrayNode = actionData.putArray("attributes");
ArrayNode attrsArrayNode = actionData.putArray("attributes");
if (keys != null) {
keys.forEach(attrsArrayNode::add);
}

5
dao/src/main/java/org/thingsboard/server/dao/audit/DummyAuditLogServiceImpl.java

@ -18,14 +18,12 @@ package org.thingsboard.server.dao.audit;
import com.google.common.util.concurrent.ListenableFuture;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.audit.AuditLog;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink;
@ -61,4 +59,7 @@ public class DummyAuditLogServiceImpl implements AuditLogService {
return null;
}
@Override
public void removeAuditLogs(TenantId tenantId, EntityId entityId) {
}
}

10
dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java

@ -189,4 +189,14 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
}, MoreExecutors.directExecutor());
}
@Override
public Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id) {
return findById(tenantId, id);
}
@Override
public ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id) {
return findByIdAsync(tenantId, id);
}
}

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

@ -115,4 +115,21 @@ public interface DeviceDao extends Dao<Device> {
* @return the list of tenant device type objects
*/
ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId);
/**
* Find devices by tenantId and device id.
* @param tenantId the tenant Id
* @param id the device Id
* @return the device object
*/
Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id);
/**
* Find devices by tenantId and device id.
* @param tenantId tenantId the tenantId
* @param id the deviceId
* @return the device object
*/
ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id);
}

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

@ -28,6 +28,8 @@ import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
@ -46,9 +48,11 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.customer.CustomerDao;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
@ -97,18 +101,32 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Autowired
private CacheManager cacheManager;
@Autowired
private EventService eventService;
@Autowired
private AuditLogService auditLogService;
@Override
public Device findDeviceById(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
return deviceDao.findById(tenantId, deviceId.getId());
if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
return deviceDao.findById(tenantId, deviceId.getId());
} else {
return deviceDao.findDeviceByTenantIdAndId(tenantId, deviceId.getId());
}
}
@Override
public ListenableFuture<Device> findDeviceByIdAsync(TenantId tenantId, DeviceId deviceId) {
log.trace("Executing findDeviceById [{}]", deviceId);
validateId(deviceId, INCORRECT_DEVICE_ID + deviceId);
return deviceDao.findByIdAsync(tenantId, deviceId.getId());
if (TenantId.SYS_TENANT_ID.equals(tenantId)) {
return deviceDao.findByIdAsync(tenantId, deviceId.getId());
} else {
return deviceDao.findDeviceByTenantIdAndIdAsync(tenantId, deviceId.getId());
}
}
@Cacheable(cacheNames = DEVICE_CACHE, key = "{#tenantId, #name}")
@ -317,6 +335,33 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
}, MoreExecutors.directExecutor());
}
@Transactional
@CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}")
@Override
public Device swapDevice(TenantId tenantId, Device device) {
log.trace("Executing swapDevice [{}]", device);
try {
List<EntityView> entityViews = entityViewService.findEntityViewsByTenantIdAndEntityIdAsync(device.getTenantId(), device.getId()).get();
if (!CollectionUtils.isEmpty(entityViews)) {
throw new DataValidationException("Can't swap device that is assigned to entity views!");
}
} catch (ExecutionException | InterruptedException e) {
log.error("Exception while finding entity views for deviceId [{}]", device.getId(), e);
throw new RuntimeException("Exception while finding entity views for deviceId [" + device.getId() + "]", e);
}
eventService.removeEvents(device.getTenantId(), device.getId());
relationService.removeRelations(device.getTenantId(), device.getId());
auditLogService.removeAuditLogs(device.getTenantId(), device.getId());
device.setTenantId(tenantId);
device.setCustomerId(null);
return doSaveDevice(device, null);
}
private DataValidator<Device> deviceValidator =
new DataValidator<Device>() {

19
dao/src/main/java/org/thingsboard/server/dao/event/BaseEventService.java

@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@ -94,6 +95,24 @@ public class BaseEventService implements EventService {
return eventDao.findLatestEvents(tenantId.getId(), entityId, eventType, limit);
}
@Override
public void removeEvents(TenantId tenantId, EntityId entityId) {
List<Event> events = new ArrayList<>();
TimePageData<Event> eventPageData;
TimePageLink eventPageLink = new TimePageLink(1000);
do {
eventPageData = findEvents(tenantId, entityId, eventPageLink);
events.addAll(eventPageData.getData());
if (eventPageData.hasNext()) {
eventPageLink = eventPageData.getNextPageLink();
}
} while (eventPageData.hasNext());
for (Event event : events) {
eventDao.removeById(tenantId, event.getUuidId());
}
}
private DataValidator<Event> eventValidator =
new DataValidator<Event>() {
@Override

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

@ -506,6 +506,22 @@ public class BaseRelationService implements RelationService {
}, MoreExecutors.directExecutor());
}
@Override
public void removeRelations(TenantId tenantId, EntityId entityId) {
Cache cache = cacheManager.getCache(RELATIONS_CACHE);
List<EntityRelation> relations = new ArrayList<>();
for (RelationTypeGroup relationTypeGroup : RelationTypeGroup.values()) {
relations.addAll(findByFrom(tenantId, entityId, relationTypeGroup));
relations.addAll(findByTo(tenantId, entityId, relationTypeGroup));
}
for (EntityRelation relation : relations) {
cacheEviction(relation, cache);
deleteRelation(tenantId, relation);
}
}
protected void validate(EntityRelation relation) {
if (relation == null) {
throw new DataValidationException("Relation type should be specified!");

3
dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java

@ -85,4 +85,7 @@ public interface DeviceRepository extends CrudRepository<DeviceEntity, String> {
List<DeviceEntity> findDevicesByTenantIdAndCustomerIdAndIdIn(String tenantId, String customerId, List<String> deviceIds);
List<DeviceEntity> findDevicesByTenantIdAndIdIn(String tenantId, List<String> deviceIds);
DeviceEntity findByTenantIdAndId(String tenantId, String id);
}

10
dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java

@ -138,6 +138,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(fromTimeUUID(tenantId))));
}
@Override
public Device findDeviceByTenantIdAndId(TenantId tenantId, UUID id) {
return DaoUtil.getData(deviceRepository.findByTenantIdAndId(fromTimeUUID(tenantId.getId()), fromTimeUUID(id)));
}
@Override
public ListenableFuture<Device> findDeviceByTenantIdAndIdAsync(TenantId tenantId, UUID id) {
return service.submit(() -> DaoUtil.getData(deviceRepository.findByTenantIdAndId(fromTimeUUID(tenantId.getId()), fromTimeUUID(id))));
}
private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) {
List<EntitySubtype> list = Collections.emptyList();
if (types != null && !types.isEmpty()) {

13
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/filter/TbMsgTypeSwitchNode.java

@ -16,8 +16,13 @@
package org.thingsboard.rule.engine.filter;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.rule.engine.api.*;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
@ -30,7 +35,7 @@ import org.thingsboard.server.common.msg.session.SessionMsgType;
configClazz = EmptyNodeConfiguration.class,
relationTypes = {"Post attributes", "Post telemetry", "RPC Request from Device", "RPC Request to Device", "Activity Event", "Inactivity Event",
"Connect Event", "Disconnect Event", "Entity Created", "Entity Updated", "Entity Deleted", "Entity Assigned",
"Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other"},
"Entity Unassigned", "Attributes Updated", "Attributes Deleted", "Alarm Acknowledged", "Alarm Cleared", "Other", "Entity Swapped From", "Entity Swapped To"},
nodeDescription = "Route incoming messages by Message Type",
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.",
uiResources = {"static/rulenode/rulenode-core-config.js"},
@ -81,6 +86,10 @@ public class TbMsgTypeSwitchNode implements TbNode {
relationType = "Alarm Cleared";
} else if (msg.getType().equals(DataConstants.RPC_CALL_FROM_SERVER_TO_DEVICE)) {
relationType = "RPC Request to Device";
} else if (msg.getType().equals(DataConstants.ENTITY_SWAPPED_FROM)) {
relationType = "Entity Swapped From";
} else if (msg.getType().equals(DataConstants.ENTITY_SWAPPED_TO)) {
relationType = "Entity Swapped To";
} else {
relationType = "Other";
}

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

@ -222,6 +222,12 @@ export default angular.module('thingsboard.types', [])
},
"LOCKOUT": {
name: "audit-log.type-lockout"
},
"SWAPPED_FROM_TENANT": {
name: "audit-log.type-swapped-from-tenant"
},
"SWAPPED_TO_TENANT": {
name: "audit-log.type-swapped-to-tenant"
}
},
auditLogActionStatus: {

4
ui/src/app/locale/locale.constant-en_US.json

@ -356,7 +356,9 @@
"action-data": "Action data",
"failure-details": "Failure details",
"search": "Search audit logs",
"clear-search": "Clear search"
"clear-search": "Clear search",
"type-swapped-from-tenant": "Swapped from Tenant",
"type-swapped-to-tenant": "Swapped to Tenant"
},
"confirm-on-exit": {
"message": "You have unsaved changes. Are you sure you want to leave this page?",

Loading…
Cancel
Save