Entities export/import API refactoring: permission checks and entity lifecycle events

This commit is contained in:
Viacheslav Klimov 2022-03-16 17:35:34 +02:00
parent 4062dc70fb
commit 849513541e
26 changed files with 239 additions and 141 deletions

View File

@ -156,7 +156,7 @@ public class AssetController extends BaseController {
Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
onAssetCreatedOrUpdated(savedAsset, asset.getId() != null, getCurrentUser()); onEntityUpdatedOrCreated(getCurrentUser(), savedAsset, null, asset.getId() == null);
return savedAsset; return savedAsset;
} catch (Exception e) { } catch (Exception e) {
@ -166,20 +166,6 @@ public class AssetController extends BaseController {
} }
} }
private void onAssetCreatedOrUpdated(Asset asset, boolean updated, SecurityUser user) {
try {
logEntityAction(user, asset.getId(), asset,
asset.getCustomerId(),
updated ? ActionType.UPDATED : ActionType.ADDED, null);
} catch (ThingsboardException e) {
log.error("Failed to log entity action", e);
}
if (updated) {
sendEntityNotificationMsg(asset.getTenantId(), asset.getId(), EdgeEventActionType.UPDATED);
}
}
@ApiOperation(value = "Delete asset (deleteAsset)", @ApiOperation(value = "Delete asset (deleteAsset)",
notes = "Deletes the asset and all the relations (from and to the asset). Referencing non-existing asset Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) notes = "Deletes the asset and all the relations (from and to the asset). Referencing non-existing asset Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@ -681,7 +667,7 @@ public class AssetController extends BaseController {
public BulkImportResult<Asset> processAssetsBulkImport(@RequestBody BulkImportRequest request) throws Exception { public BulkImportResult<Asset> processAssetsBulkImport(@RequestBody BulkImportRequest request) throws Exception {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return assetBulkImportService.processBulkImport(request, user, importedAssetInfo -> { return assetBulkImportService.processBulkImport(request, user, importedAssetInfo -> {
onAssetCreatedOrUpdated(importedAssetInfo.getEntity(), importedAssetInfo.isUpdated(), user); onEntityUpdatedOrCreated(user, importedAssetInfo.getEntity(), importedAssetInfo.getOldEntity(), !importedAssetInfo.isUpdated());
}); });
} }

View File

@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.EntityViewInfo;
import org.thingsboard.server.common.data.HasCustomerId;
import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackage;
@ -68,6 +69,7 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.OtaPackageId;
import org.thingsboard.server.common.data.id.RpcId; import org.thingsboard.server.common.data.id.RpcId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
@ -83,6 +85,7 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.rpc.Rpc; import org.thingsboard.server.common.data.rpc.Rpc;
@ -141,6 +144,7 @@ import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -919,4 +923,62 @@ public abstract class BaseController {
return MediaType.APPLICATION_OCTET_STREAM; return MediaType.APPLICATION_OCTET_STREAM;
} }
} }
public <E extends HasName & HasId<I> & HasTenantId, I extends EntityId> void onEntityUpdatedOrCreated(User user, E savedEntity, E oldEntity, boolean isNewEntity) {
boolean notifyEdge = false;
EntityType entityType = savedEntity.getId().getEntityType();
switch (entityType) {
case DEVICE:
tbClusterService.onDeviceUpdated((Device) savedEntity, (Device) oldEntity);
break;
case DEVICE_PROFILE:
DeviceProfile deviceProfile = (DeviceProfile) savedEntity;
DeviceProfile oldDeviceProfile = (DeviceProfile) oldEntity;
boolean isFirmwareChanged = false;
boolean isSoftwareChanged = false;
if (!isNewEntity) {
if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
isFirmwareChanged = true;
}
if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) {
isSoftwareChanged = true;
}
}
tbClusterService.onDeviceProfileChange(deviceProfile, null);
tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), deviceProfile.getId(),
isNewEntity ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
otaPackageStateService.update(deviceProfile, isFirmwareChanged, isSoftwareChanged);
notifyEdge = true;
break;
case RULE_CHAIN: // FIXME: events for rule chain metadata
RuleChainType ruleChainType = ((RuleChain) savedEntity).getType();
if (RuleChainType.CORE.equals(ruleChainType)) {
tbClusterService.broadcastEntityStateChangeEvent(savedEntity.getTenantId(), savedEntity.getId(),
isNewEntity ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
}
if (RuleChainType.EDGE.equals(ruleChainType)) {
if (!isNewEntity) {
notifyEdge = true;
}
}
break;
case ASSET:
case CUSTOMER:
case DASHBOARD:
if (!isNewEntity) {
notifyEdge = true;
}
break;
default:
throw new UnsupportedOperationException();
}
entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : null,
isNewEntity ? ActionType.ADDED : ActionType.UPDATED, null);
if (notifyEdge) {
sendEntityNotificationMsg(savedEntity.getTenantId(), savedEntity.getId(), isNewEntity ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
}
}
} }

View File

@ -33,7 +33,6 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
@ -151,14 +150,7 @@ public class CustomerController extends BaseController {
checkEntity(customer.getId(), customer, Resource.CUSTOMER); checkEntity(customer.getId(), customer, Resource.CUSTOMER);
Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer)); Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer));
onEntityUpdatedOrCreated(getCurrentUser(), savedCustomer, null, customer.getId() == null);
logEntityAction(savedCustomer.getId(), savedCustomer,
savedCustomer.getId(),
customer.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
if (customer.getId() != null) {
sendEntityNotificationMsg(savedCustomer.getTenantId(), savedCustomer.getId(), EdgeEventActionType.UPDATED);
}
return savedCustomer; return savedCustomer;
} catch (Exception e) { } catch (Exception e) {

View File

@ -187,13 +187,7 @@ public class DashboardController extends BaseController {
Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard));
logEntityAction(savedDashboard.getId(), savedDashboard, onEntityUpdatedOrCreated(getCurrentUser(), savedDashboard, null, dashboard.getId() == null);
null,
dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
if (dashboard.getId() != null) {
sendEntityNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), EdgeEventActionType.UPDATED);
}
return savedDashboard; return savedDashboard;
} catch (Exception e) { } catch (Exception e) {

View File

@ -195,7 +195,7 @@ public class DeviceController extends BaseController {
Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken));
onDeviceCreatedOrUpdated(savedDevice, oldDevice, !created, getCurrentUser()); onEntityUpdatedOrCreated(getCurrentUser(), savedDevice, oldDevice, created);
return savedDevice; return savedDevice;
} catch (Exception e) { } catch (Exception e) {
@ -224,10 +224,8 @@ public class DeviceController extends BaseController {
checkEntity(device.getId(), device, Resource.DEVICE); checkEntity(device.getId(), device, Resource.DEVICE);
Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials); Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials);
checkNotNull(savedDevice); checkNotNull(savedDevice);
tbClusterService.onDeviceUpdated(savedDevice, device);
logEntityAction(savedDevice.getId(), savedDevice, onEntityUpdatedOrCreated(getCurrentUser(), savedDevice, device, device.getId() == null);
savedDevice.getCustomerId(),
device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
return savedDevice; return savedDevice;
} catch (Exception e) { } catch (Exception e) {
@ -237,18 +235,6 @@ public class DeviceController extends BaseController {
} }
} }
private void onDeviceCreatedOrUpdated(Device savedDevice, Device oldDevice, boolean updated, SecurityUser user) {
tbClusterService.onDeviceUpdated(savedDevice, oldDevice);
try {
logEntityAction(user, savedDevice.getId(), savedDevice,
savedDevice.getCustomerId(),
updated ? ActionType.UPDATED : ActionType.ADDED, null);
} catch (ThingsboardException e) {
log.error("Failed to log entity action", e);
}
}
@ApiOperation(value = "Delete device (deleteDevice)", @ApiOperation(value = "Delete device (deleteDevice)",
notes = "Deletes the device, it's credentials and all the relations (from and to the device). Referencing non-existing device Id will cause an error." + TENANT_AUTHORITY_PARAGRAPH) notes = "Deletes the device, it's credentials and all the relations (from and to the device). Referencing non-existing device Id will cause an error." + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
@ -1015,7 +1001,7 @@ public class DeviceController extends BaseController {
public BulkImportResult<Device> processDevicesBulkImport(@RequestBody BulkImportRequest request) throws Exception { public BulkImportResult<Device> processDevicesBulkImport(@RequestBody BulkImportRequest request) throws Exception {
SecurityUser user = getCurrentUser(); SecurityUser user = getCurrentUser();
return deviceBulkImportService.processBulkImport(request, user, importedDeviceInfo -> { return deviceBulkImportService.processBulkImport(request, user, importedDeviceInfo -> {
onDeviceCreatedOrUpdated(importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), importedDeviceInfo.isUpdated(), user); onEntityUpdatedOrCreated(user, importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), !importedDeviceInfo.isUpdated());
}); });
} }

View File

@ -46,7 +46,6 @@ import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_DATA; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_DATA;
@ -207,32 +206,14 @@ public class DeviceProfileController extends BaseController {
checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
boolean isFirmwareChanged = false; DeviceProfile oldDeviceProfile = null;
boolean isSoftwareChanged = false;
if (!created) { if (!created) {
DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId()); oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId());
if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
isFirmwareChanged = true;
}
if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) {
isSoftwareChanged = true;
}
} }
DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); onEntityUpdatedOrCreated(getCurrentUser(), deviceProfile, oldDeviceProfile, created);
tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), savedDeviceProfile.getId(),
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
logEntityAction(savedDeviceProfile.getId(), savedDeviceProfile,
null,
created ? ActionType.ADDED : ActionType.UPDATED, null);
otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged);
sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(),
deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED);
return savedDeviceProfile; return savedDeviceProfile;
} catch (Exception e) { } catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile, logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile,

View File

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
@ -23,21 +24,20 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.export.EntitiesExportRequest;
import org.thingsboard.server.common.data.export.EntitiesExportResponse;
import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.export.EntityExportData;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.expimp.EntitiesExportImportService; import org.thingsboard.server.service.expimp.EntitiesExportImportService;
import org.thingsboard.server.service.expimp.imp.EntityImportResult;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
@RestController @RestController
@ -51,49 +51,69 @@ public class EntitiesExportImportController extends BaseController {
@PostMapping("/export/{entityType}/{entityId}") @PostMapping("/export/{entityType}/{entityId}")
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
public EntityExportData<?> exportEntity(@PathVariable EntityType entityType, public EntityExportData<?> exportEntity(@ApiParam(allowableValues = "DEVICE, DEVICE_PROFILE, ASSET, RULE_CHAIN, DASHBOARD, CUSTOMER") @PathVariable EntityType entityType,
@PathVariable("entityId") UUID entityUuid) throws ThingsboardException { @PathVariable("entityId") UUID entityUuid) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid);
try { try {
return exportImportService.exportEntity(getTenantId(), entityId); return exportEntity(getCurrentUser(), entityId);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
} }
@PostMapping("/export/batch")
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
public EntitiesExportResponse exportEntities(@RequestBody EntitiesExportRequest exportRequest) throws ThingsboardException {
TenantId tenantId = getTenantId();
EntitiesExportResponse exportResponse = new EntitiesExportResponse(); // @PostMapping("/export/batch")
// @PreAuthorize("hasAuthority('TENANT_ADMIN')")
Map<EntityType, List<EntityExportData<HasId<EntityId>>>> result = new HashMap<>(); // public EntitiesExportResponse exportEntities(@RequestBody EntitiesExportRequest exportRequest) throws ThingsboardException {
exportRequest.getEntities().forEach((entityType, entityIds) -> { // TenantId tenantId = getTenantId();
List<EntityExportData<HasId<EntityId>>> exportDataForEntityType = new LinkedList<>(); //
entityIds.forEach(entityId -> { // EntitiesExportResponse exportResponse = new EntitiesExportResponse();
EntityExportData<HasId<EntityId>> exportData = exportImportService.exportEntity(tenantId, entityId); //
exportDataForEntityType.add(exportData); // Map<EntityType, List<EntityExportData<HasId<EntityId>>>> result = new HashMap<>();
}); // exportRequest.getEntities().forEach((entityType, entityIds) -> {
result.put(entityType, exportDataForEntityType); // List<EntityExportData<HasId<EntityId>>> exportDataForEntityType = new LinkedList<>();
}); // entityIds.forEach(entityId -> {
// EntityExportData<HasId<EntityId>> exportData = exportImportService.exportEntity(tenantId, entityId);
exportResponse.setExportData(result); // exportDataForEntityType.add(exportData);
return exportResponse; // });
} // result.put(entityType, exportDataForEntityType);
// });
//
// exportResponse.setExportData(result);
// return exportResponse;
// }
// TODO: export and import of batches
// TODO: api to export and import whole customer, whole tenant // TODO: api to export and import whole customer, whole tenant
@PostMapping("/import") @PostMapping("/import")
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")
public <E extends HasId<I>, I extends EntityId, D extends EntityExportData<E>> E importEntity(@RequestBody D exportData) throws ThingsboardException { public <E extends HasId<I> & HasName & HasTenantId, I extends EntityId, D extends EntityExportData<E>> EntityImportResult<E> importEntity(@RequestBody D exportData) throws ThingsboardException {
try { try {
return exportImportService.importEntity(getTenantId(), exportData); return importEntity(getCurrentUser(), exportData);
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
} }
// public void importEntities(@RequestBody )
private <E extends HasId<I>, I extends EntityId> EntityExportData<HasId<EntityId>> exportEntity(SecurityUser user, I entityId) throws ThingsboardException {
checkEntityId(entityId, Operation.READ);
return exportImportService.exportEntity(getTenantId(), entityId);
}
private <E extends HasId<I> & HasName & HasTenantId, I extends EntityId, D extends EntityExportData<E>> EntityImportResult<E> importEntity(SecurityUser user, D exportData) throws ThingsboardException {
E existingEntity = exportImportService.findEntityByExternalId(user.getTenantId(), exportData.getMainEntity().getId());
if (existingEntity != null) {
checkEntityId(existingEntity.getId(), Operation.WRITE); // todo maybe need to extract permission check to BaseController and put there permission checks from other controllers
} else {
checkEntity(null, exportData.getMainEntity(), Resource.of(exportData.getEntityType()));
}
EntityImportResult<E> importResult = exportImportService.importEntity(getTenantId(), exportData);
onEntityUpdatedOrCreated(user, importResult.getSavedEntity(), importResult.getOldEntity(), importResult.getOldEntity() == null);
return importResult;
}
} }

View File

@ -78,11 +78,9 @@ import org.thingsboard.server.service.security.permission.Resource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -254,20 +252,7 @@ public class RuleChainController extends BaseController {
RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
if (RuleChainType.CORE.equals(savedRuleChain.getType())) { onEntityUpdatedOrCreated(getCurrentUser(), savedRuleChain, null, created);
tbClusterService.broadcastEntityStateChangeEvent(ruleChain.getTenantId(), savedRuleChain.getId(),
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
}
logEntityAction(savedRuleChain.getId(), savedRuleChain,
null,
created ? ActionType.ADDED : ActionType.UPDATED, null);
if (RuleChainType.EDGE.equals(savedRuleChain.getType())) {
if (!created) {
sendEntityNotificationMsg(savedRuleChain.getTenantId(), savedRuleChain.getId(), EdgeEventActionType.UPDATED);
}
}
return savedRuleChain; return savedRuleChain;
} catch (Exception e) { } catch (Exception e) {
@ -294,6 +279,7 @@ public class RuleChainController extends BaseController {
RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName()); RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName());
tbClusterService.broadcastEntityStateChangeEvent(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED); tbClusterService.broadcastEntityStateChangeEvent(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED);
logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null); logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null);

View File

@ -24,7 +24,9 @@ import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.expimp.exp.EntityExportService; import org.thingsboard.server.service.expimp.exp.EntityExportService;
import org.thingsboard.server.service.expimp.imp.EntityImportResult;
import org.thingsboard.server.service.expimp.imp.EntityImportService; import org.thingsboard.server.service.expimp.imp.EntityImportService;
import org.thingsboard.server.service.expimp.imp.impl.AbstractEntityImportService;
import java.util.Collection; import java.util.Collection;
import java.util.EnumMap; import java.util.EnumMap;
@ -51,16 +53,21 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS
} }
// FIXME: somehow validate export data // FIXME: somehow validate export data
// FIXME: validate permissions for create or update
// FIXME: send entity lifecycle event
@Override @Override
public <E extends HasId<I>, I extends EntityId, D extends EntityExportData<E>> E importEntity(TenantId tenantId, D exportData) { public <E extends HasId<I>, I extends EntityId, D extends EntityExportData<E>> EntityImportResult<E> importEntity(TenantId tenantId, D exportData) {
EntityType entityType = exportData.getEntityType(); EntityType entityType = exportData.getEntityType();
EntityImportService<I, E, D> importService = getImportService(entityType); EntityImportService<I, E, D> importService = getImportService(entityType);
return importService.importEntity(tenantId, exportData); return importService.importEntity(tenantId, exportData);
} }
@Override
@SuppressWarnings("unchecked")
public <E extends HasId<I>, I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId) {
return (E) importServices.values().stream().filter(entityImportService -> entityImportService instanceof AbstractEntityImportService)
.findFirst().map(entityImportService -> (AbstractEntityImportService) importServices).get()
.findByExternalOrInternalId(tenantId, externalId); // FIXME !!!
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private <I extends EntityId, E extends HasId<I>> EntityExportService<I, E> getExportService(EntityType entityType) { private <I extends EntityId, E extends HasId<I>> EntityExportService<I, E> getExportService(EntityType entityType) {

View File

@ -19,11 +19,14 @@ import org.thingsboard.server.common.data.export.EntityExportData;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.service.expimp.imp.EntityImportResult;
public interface EntitiesExportImportService { public interface EntitiesExportImportService {
<E extends HasId<I>, I extends EntityId> EntityExportData<E> exportEntity(TenantId tenantId, I entityId); <E extends HasId<I>, I extends EntityId> EntityExportData<E> exportEntity(TenantId tenantId, I entityId);
<E extends HasId<I>, I extends EntityId, D extends EntityExportData<E>> E importEntity(TenantId tenantId, D exportData); <E extends HasId<I>, I extends EntityId, D extends EntityExportData<E>> EntityImportResult<E> importEntity(TenantId tenantId, D exportData);
<E extends HasId<I>, I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId);
} }

View File

@ -0,0 +1,26 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.expimp.imp;
import lombok.Data;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
@Data
public class EntityImportResult<E extends HasId<? extends EntityId>> {
private E savedEntity;
private E oldEntity;
}

View File

@ -23,7 +23,8 @@ import org.thingsboard.server.common.data.id.TenantId;
public interface EntityImportService<I extends EntityId, E extends HasId<I>, D extends EntityExportData<E>> { public interface EntityImportService<I extends EntityId, E extends HasId<I>, D extends EntityExportData<E>> {
E importEntity(TenantId tenantId, D exportData); // FIXME: get rid of boilerplate for import result creation and everything else
EntityImportResult<E> importEntity(TenantId tenantId, D exportData);
EntityType getEntityType(); EntityType getEntityType();

View File

@ -38,7 +38,6 @@ public abstract class AbstractEntityImportService<I extends EntityId, E extends
return findByExternalOrInternalId(tenantId, externalId); return findByExternalOrInternalId(tenantId, externalId);
} }
protected final <ID extends EntityId> ID getInternalId(TenantId tenantId, ID externalId) { protected final <ID extends EntityId> ID getInternalId(TenantId tenantId, ID externalId) {
if (externalId == null) { if (externalId == null) {
return null; return null;
@ -50,7 +49,7 @@ public abstract class AbstractEntityImportService<I extends EntityId, E extends
return entity.getId(); return entity.getId();
} }
private <T extends HasId<ID>, ID extends EntityId> T findByExternalOrInternalId(TenantId tenantId, ID externalOrInternalId) { public final <T extends HasId<ID>, ID extends EntityId> T findByExternalOrInternalId(TenantId tenantId, ID externalOrInternalId) {
ExportableEntityDao<T> dao = getDao(externalOrInternalId.getEntityType()); ExportableEntityDao<T> dao = getDao(externalOrInternalId.getEntityType());
return Optional.ofNullable(dao.findByTenantIdAndExternalId(tenantId.getId(), externalOrInternalId.getId())) return Optional.ofNullable(dao.findByTenantIdAndExternalId(tenantId.getId(), externalOrInternalId.getId()))
.orElseGet(() -> dao.findByTenantIdAndId(tenantId.getId(), externalOrInternalId.getId())); .orElseGet(() -> dao.findByTenantIdAndId(tenantId.getId(), externalOrInternalId.getId()));

View File

@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.asset.AssetService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.expimp.imp.EntityImportResult;
@Service @Service
@TbCoreComponent @TbCoreComponent
@ -34,7 +35,7 @@ public class AssetImportService extends AbstractEntityImportService<AssetId, Ass
@Override @Override
public Asset importEntity(TenantId tenantId, AssetExportData exportData) { public EntityImportResult<Asset> importEntity(TenantId tenantId, AssetExportData exportData) {
Asset asset = exportData.getAsset(); Asset asset = exportData.getAsset();
Asset existingAsset = findByExternalId(tenantId, asset.getId()); // TODO: extract boiler plate to abstract class ... Asset existingAsset = findByExternalId(tenantId, asset.getId()); // TODO: extract boiler plate to abstract class ...
@ -51,7 +52,10 @@ public class AssetImportService extends AbstractEntityImportService<AssetId, Ass
Asset savedAsset = assetService.saveAsset(asset); Asset savedAsset = assetService.saveAsset(asset);
return savedAsset; EntityImportResult<Asset> importResult = new EntityImportResult<>();
importResult.setSavedEntity(savedAsset);
importResult.setOldEntity(existingAsset);
return importResult;
} }
@Override @Override

View File

@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.expimp.imp.EntityImportResult;
@Service @Service
@TbCoreComponent @TbCoreComponent
@ -34,7 +35,7 @@ public class CustomerImportService extends AbstractEntityImportService<CustomerI
@Override @Override
public Customer importEntity(TenantId tenantId, CustomerExportData exportData) { public EntityImportResult<Customer> importEntity(TenantId tenantId, CustomerExportData exportData) {
Customer customer = exportData.getCustomer(); Customer customer = exportData.getCustomer();
Customer existingCustomer = findByExternalId(tenantId, customer.getId()); Customer existingCustomer = findByExternalId(tenantId, customer.getId());
@ -49,7 +50,10 @@ public class CustomerImportService extends AbstractEntityImportService<CustomerI
Customer savedCustomer = customerService.saveCustomer(customer); Customer savedCustomer = customerService.saveCustomer(customer);
return savedCustomer; EntityImportResult<Customer> importResult = new EntityImportResult<>();
importResult.setSavedEntity(savedCustomer);
importResult.setOldEntity(existingCustomer);
return importResult;
} }
@Override @Override

View File

@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.expimp.imp.EntityImportResult;
@Service @Service
@TbCoreComponent @TbCoreComponent
@ -34,7 +35,7 @@ public class DashboardImportService extends AbstractEntityImportService<Dashboar
@Override @Override
public Dashboard importEntity(TenantId tenantId, DashboardExportData exportData) { public EntityImportResult<Dashboard> importEntity(TenantId tenantId, DashboardExportData exportData) {
Dashboard dashboard = exportData.getDashboard(); Dashboard dashboard = exportData.getDashboard();
Dashboard existingDashboard = findByExternalId(tenantId, dashboard.getId()); Dashboard existingDashboard = findByExternalId(tenantId, dashboard.getId());
@ -51,7 +52,10 @@ public class DashboardImportService extends AbstractEntityImportService<Dashboar
Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); Dashboard savedDashboard = dashboardService.saveDashboard(dashboard);
return savedDashboard; EntityImportResult<Dashboard> importResult = new EntityImportResult<>();
importResult.setSavedEntity(savedDashboard);
importResult.setOldEntity(existingDashboard);
return importResult;
} }
@Override @Override

View File

@ -18,7 +18,6 @@ package org.thingsboard.server.service.expimp.imp.impl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.export.impl.DeviceExportData; import org.thingsboard.server.common.data.export.impl.DeviceExportData;
@ -26,7 +25,7 @@ import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.expimp.imp.EntityImportResult;
@Service @Service
@TbCoreComponent @TbCoreComponent
@ -38,7 +37,7 @@ public class DeviceImportService extends AbstractEntityImportService<DeviceId, D
@Transactional @Transactional
@Override @Override
public Device importEntity(TenantId tenantId, DeviceExportData exportData) { public EntityImportResult<Device> importEntity(TenantId tenantId, DeviceExportData exportData) {
Device device = exportData.getDevice(); Device device = exportData.getDevice();
Device existingDevice = findByExternalId(tenantId, device.getId()); // FIXME: !!! Device existingDevice = findByExternalId(tenantId, device.getId()); // FIXME: !!!
// what if exporting and importing back already exported entity ? (save version and then load it back) // what if exporting and importing back already exported entity ? (save version and then load it back)
@ -70,7 +69,10 @@ public class DeviceImportService extends AbstractEntityImportService<DeviceId, D
Device savedDevice = deviceService.saveDeviceWithCredentials(device, exportData.getCredentials()); Device savedDevice = deviceService.saveDeviceWithCredentials(device, exportData.getCredentials());
return savedDevice; EntityImportResult<Device> importResult = new EntityImportResult<>();
importResult.setSavedEntity(savedDevice);
importResult.setOldEntity(existingDevice);
return importResult;
} }
@Override @Override

View File

@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.expimp.imp.EntityImportResult;
@Service @Service
@TbCoreComponent @TbCoreComponent
@ -34,7 +35,7 @@ public class DeviceProfileImportService extends AbstractEntityImportService<Devi
@Override @Override
public DeviceProfile importEntity(TenantId tenantId, DeviceProfileExportData exportData) { public EntityImportResult<DeviceProfile> importEntity(TenantId tenantId, DeviceProfileExportData exportData) {
DeviceProfile deviceProfile = exportData.getDeviceProfile(); DeviceProfile deviceProfile = exportData.getDeviceProfile();
DeviceProfile existingDeviceProfile = findByExternalId(tenantId, deviceProfile.getId()); DeviceProfile existingDeviceProfile = findByExternalId(tenantId, deviceProfile.getId());
@ -54,7 +55,10 @@ public class DeviceProfileImportService extends AbstractEntityImportService<Devi
DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile);
return savedDeviceProfile; EntityImportResult<DeviceProfile> importResult = new EntityImportResult<>();
importResult.setSavedEntity(savedDeviceProfile);
importResult.setOldEntity(existingDeviceProfile);
return importResult;
} }
@Override @Override

View File

@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.expimp.imp.EntityImportResult;
@Service @Service
@TbCoreComponent @TbCoreComponent
@ -37,7 +38,7 @@ public class RuleChainImportService extends AbstractEntityImportService<RuleChai
@Transactional @Transactional
@Override @Override
public RuleChain importEntity(TenantId tenantId, RuleChainExportData exportData) { public EntityImportResult<RuleChain> importEntity(TenantId tenantId, RuleChainExportData exportData) {
RuleChain ruleChain = exportData.getRuleChain(); RuleChain ruleChain = exportData.getRuleChain();
RuleChain existingRuleChain = findByExternalId(tenantId, ruleChain.getId()); RuleChain existingRuleChain = findByExternalId(tenantId, ruleChain.getId());
@ -69,7 +70,10 @@ public class RuleChainImportService extends AbstractEntityImportService<RuleChai
}); });
ruleChainService.saveRuleChainMetaData(tenantId, metaData); ruleChainService.saveRuleChainMetaData(tenantId, metaData);
return savedRuleChain; EntityImportResult<RuleChain> importResult = new EntityImportResult<>();
importResult.setSavedEntity(savedRuleChain);
importResult.setOldEntity(existingRuleChain);
return importResult;
} }
@Override @Override

View File

@ -43,6 +43,9 @@ import org.thingsboard.server.common.data.id.HasId;
public interface EntityExportData<E extends HasId<? extends EntityId>> { public interface EntityExportData<E extends HasId<? extends EntityId>> {
@JsonIgnore @JsonIgnore
EntityType getEntityType(); // fixme: maybe remove if not needed, as well as generic E getMainEntity();
@JsonIgnore
EntityType getEntityType(); // fixme: maybe remove if not needed
} }

View File

@ -25,6 +25,11 @@ public class AssetExportData implements EntityExportData<Asset> {
private Asset asset; private Asset asset;
@Override
public Asset getMainEntity() {
return asset;
}
@Override @Override
public EntityType getEntityType() { public EntityType getEntityType() {
return EntityType.ASSET; return EntityType.ASSET;

View File

@ -25,6 +25,11 @@ public class CustomerExportData implements EntityExportData<Customer> {
private Customer customer; private Customer customer;
@Override
public Customer getMainEntity() {
return customer;
}
@Override @Override
public EntityType getEntityType() { public EntityType getEntityType() {
return EntityType.CUSTOMER; return EntityType.CUSTOMER;

View File

@ -25,6 +25,11 @@ public class DashboardExportData implements EntityExportData<Dashboard> {
private Dashboard dashboard; private Dashboard dashboard;
@Override
public Dashboard getMainEntity() {
return dashboard;
}
@Override @Override
public EntityType getEntityType() { public EntityType getEntityType() {
return EntityType.DASHBOARD; return EntityType.DASHBOARD;

View File

@ -27,6 +27,11 @@ public class DeviceExportData implements EntityExportData<Device> {
private Device device; private Device device;
private DeviceCredentials credentials; private DeviceCredentials credentials;
@Override
public Device getMainEntity() {
return device;
}
@Override @Override
public EntityType getEntityType() { public EntityType getEntityType() {
return EntityType.DEVICE; return EntityType.DEVICE;

View File

@ -25,6 +25,11 @@ public class DeviceProfileExportData implements EntityExportData<DeviceProfile>
private DeviceProfile deviceProfile; private DeviceProfile deviceProfile;
@Override
public DeviceProfile getMainEntity() {
return deviceProfile;
}
@Override @Override
public EntityType getEntityType() { public EntityType getEntityType() {
return EntityType.DEVICE_PROFILE; return EntityType.DEVICE_PROFILE;

View File

@ -27,6 +27,11 @@ public class RuleChainExportData implements EntityExportData<RuleChain> {
private RuleChain ruleChain; private RuleChain ruleChain;
private RuleChainMetaData metaData; private RuleChainMetaData metaData;
@Override
public RuleChain getMainEntity() {
return ruleChain;
}
@Override @Override
public EntityType getEntityType() { public EntityType getEntityType() {
return EntityType.RULE_CHAIN; return EntityType.RULE_CHAIN;